diff --git a/.gitignore b/.gitignore index 84c07d25..6654d55e 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,5 @@ out/ /tomcat-e2e/ /e2eTest.log /files/ + +*.log diff --git a/src/e2eTest/resources/application.yml b/src/e2eTest/resources/application.yml index 330660e2..dcd84955 100644 --- a/src/e2eTest/resources/application.yml +++ b/src/e2eTest/resources/application.yml @@ -9,6 +9,9 @@ hideout: public-key: "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1SU1LfVLPHCozMxH2Mo4lgOEePzNm0tRgeLezV6ffAt0gunVTLw7onLRnrq0/IzW7yWR7QkrmBL7jTKEn5u+qKhbwKfBstIs+bMY2Zkp18gnTxKLxoS2tFczGkPLPgizskuemMghRniWaoLcyehkd3qqGElvW/VDL5AaWTg0nLVkjRo9z+40RQzuVaE8AkAFmxZzow3x+VJYKdjykkJ0iT9wCS0DRTXu269V264Vf/3jvredZiKRkgwlL9xNAwxXFg0x/XFw005UWVRIkdgcKWTjpBP2dPwVZ4WWC+9aGVd+Gyn1o0CLelf4rEjGoXbAAEgAqeGUxrcIlbjXfbcmwIDAQAB" storage: type: local + debug: + trace-query-exception: true + trace-query-call: true spring: flyway: diff --git a/src/intTest/kotlin/mastodon/account/AccountApiTest.kt b/src/intTest/kotlin/mastodon/account/AccountApiTest.kt index cf67b35d..f2f9ffc7 100644 --- a/src/intTest/kotlin/mastodon/account/AccountApiTest.kt +++ b/src/intTest/kotlin/mastodon/account/AccountApiTest.kt @@ -1,7 +1,7 @@ package mastodon.account import dev.usbharu.hideout.SpringApplication -import dev.usbharu.hideout.core.infrastructure.exposedquery.ActorQueryServiceImpl +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.infrastructure.exposedquery.FollowerQueryServiceImpl import kotlinx.coroutines.test.runTest import org.assertj.core.api.Assertions.assertThat @@ -39,7 +39,7 @@ class AccountApiTest { private lateinit var followerQueryServiceImpl: FollowerQueryServiceImpl @Autowired - private lateinit var userQueryServiceImpl: ActorQueryServiceImpl + private lateinit var actorRepository: ActorRepository @Autowired @@ -100,7 +100,7 @@ class AccountApiTest { .asyncDispatch() .andExpect { status { isFound() } } - userQueryServiceImpl.findByNameAndDomain("api-test-user-1", "example.com") + actorRepository.findByNameAndDomain("api-test-user-1", "example.com") } @Test @@ -116,7 +116,7 @@ class AccountApiTest { .asyncDispatch() .andExpect { status { isFound() } } - userQueryServiceImpl.findByNameAndDomain("api-test-user-2", "example.com") + actorRepository.findByNameAndDomain("api-test-user-2", "example.com") } @Test diff --git a/src/intTest/kotlin/util/WithHttpSignatureSecurityContextFactory.kt b/src/intTest/kotlin/util/WithHttpSignatureSecurityContextFactory.kt index faabf58a..fc9772f0 100644 --- a/src/intTest/kotlin/util/WithHttpSignatureSecurityContextFactory.kt +++ b/src/intTest/kotlin/util/WithHttpSignatureSecurityContextFactory.kt @@ -1,8 +1,9 @@ package util import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureUser -import dev.usbharu.hideout.core.query.ActorQueryService import dev.usbharu.httpsignature.common.HttpHeaders import dev.usbharu.httpsignature.common.HttpMethod import dev.usbharu.httpsignature.common.HttpRequest @@ -14,7 +15,7 @@ import org.springframework.security.web.authentication.preauth.PreAuthenticatedA import java.net.URL class WithHttpSignatureSecurityContextFactory( - private val actorQueryService: ActorQueryService, + private val actorRepository: ActorRepository, private val transaction: Transaction ) : WithSecurityContextFactory { @@ -28,7 +29,8 @@ class WithHttpSignatureSecurityContextFactory( ) ) val httpSignatureUser = transaction.transaction { - val findByKeyId = actorQueryService.findByKeyId(annotation.keyId) + val findByKeyId = + actorRepository.findByKeyId(annotation.keyId) ?: throw UserNotFoundException.withKeyId(annotation.keyId) HttpSignatureUser( findByKeyId.name, findByKeyId.domain, diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Person.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Person.kt index 369905ec..0b3e4ff1 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Person.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Person.kt @@ -49,9 +49,9 @@ constructor( @Suppress("CyclomaticComplexMethod") override fun hashCode(): Int { var result = super.hashCode() - result = 31 * result + name.hashCode() + result = 31 * result + (name?.hashCode() ?: 0) result = 31 * result + id.hashCode() - result = 31 * result + (preferredUsername?.hashCode() ?: 0) + result = 31 * result + preferredUsername.hashCode() result = 31 * result + (summary?.hashCode() ?: 0) result = 31 * result + inbox.hashCode() result = 31 * result + outbox.hashCode() @@ -61,15 +61,15 @@ constructor( result = 31 * result + endpoints.hashCode() result = 31 * result + (followers?.hashCode() ?: 0) result = 31 * result + (following?.hashCode() ?: 0) - result = 31 * result + manuallyApprovesFollowers.hashCode() + result = 31 * result + (manuallyApprovesFollowers?.hashCode() ?: 0) return result } override fun toString(): String { return "Person(" + - "name='$name', " + + "name=$name, " + "id='$id', " + - "preferredUsername=$preferredUsername, " + + "preferredUsername='$preferredUsername', " + "summary=$summary, " + "inbox='$inbox', " + "outbox='$outbox', " + diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/NoteQueryServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/NoteQueryServiceImpl.kt index 19b217d5..1ebc9511 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/NoteQueryServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/NoteQueryServiceImpl.kt @@ -5,12 +5,10 @@ import dev.usbharu.hideout.activitypub.domain.model.Note import dev.usbharu.hideout.activitypub.query.NoteQueryService import dev.usbharu.hideout.activitypub.service.objects.note.APNoteServiceImpl.Companion.public import dev.usbharu.hideout.application.infrastructure.exposed.QueryMapper -import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException import dev.usbharu.hideout.core.domain.model.post.Post import dev.usbharu.hideout.core.domain.model.post.PostRepository import dev.usbharu.hideout.core.domain.model.post.Visibility import dev.usbharu.hideout.core.infrastructure.exposedrepository.* -import dev.usbharu.hideout.util.singleOr import org.jetbrains.exposed.sql.Query import org.jetbrains.exposed.sql.ResultRow import org.jetbrains.exposed.sql.select @@ -21,39 +19,42 @@ import java.time.Instant @Repository class NoteQueryServiceImpl(private val postRepository: PostRepository, private val postQueryMapper: QueryMapper) : NoteQueryService { - override suspend fun findById(id: Long): Pair { + override suspend fun findById(id: Long): Pair? { return Posts .leftJoin(Actors) .leftJoin(PostsMedia) .leftJoin(Media) .select { Posts.id eq id } .let { - it.toNote() to postQueryMapper.map(it) - .singleOr { FailedToGetResourcesException("id: $id does not exist.") } + (it.toNote() ?: return null) to ( + postQueryMapper.map(it) + .singleOrNull() ?: return null + ) } } - override suspend fun findByApid(apId: String): Pair { + override suspend fun findByApid(apId: String): Pair? { return Posts .leftJoin(Actors) .leftJoin(PostsMedia) .leftJoin(Media) .select { Posts.apId eq apId } .let { - it.toNote() to postQueryMapper.map(it) - .singleOr { FailedToGetResourcesException("apid: $apId does not exist.") } + (it.toNote() ?: return null) to ( + postQueryMapper.map(it) + .singleOrNull() ?: return null + ) } } private suspend fun ResultRow.toNote(mediaList: List): Note { val replyId = this[Posts.replyId] val replyTo = if (replyId != null) { - try { - postRepository.findById(replyId).url - } catch (e: FailedToGetResourcesException) { - logger.warn("Failed to get replyId: $replyId", e) - null + val url = postRepository.findById(replyId)?.url + if (url == null) { + logger.warn("Failed to get replyId: $replyId") } + url } else { null } @@ -76,11 +77,11 @@ class NoteQueryServiceImpl(private val postRepository: PostRepository, private v ) } - private suspend fun Query.toNote(): Note { + private suspend fun Query.toNote(): Note? { return this.groupBy { it[Posts.id] } .map { it.value } .map { it.first().toNote(it.mapNotNull { resultRow -> resultRow.toMediaOrNull() }) } - .singleOr { FailedToGetResourcesException("resource does not exist.") } + .singleOrNull() } private fun visibility(visibility: Visibility, followers: String?): Pair, List> { diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/actor/UserAPControllerImpl.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/actor/UserAPControllerImpl.kt index ea8ad508..df90a913 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/actor/UserAPControllerImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/actor/UserAPControllerImpl.kt @@ -2,7 +2,7 @@ package dev.usbharu.hideout.activitypub.interfaces.api.actor import dev.usbharu.hideout.activitypub.domain.model.Person import dev.usbharu.hideout.activitypub.service.objects.user.APUserService -import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException +import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.RestController @@ -12,7 +12,7 @@ class UserAPControllerImpl(private val apUserService: APUserService) : UserAPCon override suspend fun userAp(username: String): ResponseEntity { val person = try { apUserService.getPersonByName(username) - } catch (_: FailedToGetResourcesException) { + } catch (_: UserNotFoundException) { return ResponseEntity.notFound().build() } person.context += listOf("https://www.w3.org/ns/activitystreams") diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/webfinger/WebFingerController.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/webfinger/WebFingerController.kt index 3a7bfdd8..39665c71 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/webfinger/WebFingerController.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/webfinger/WebFingerController.kt @@ -3,7 +3,7 @@ package dev.usbharu.hideout.activitypub.interfaces.api.webfinger import dev.usbharu.hideout.activitypub.domain.model.webfinger.WebFinger import dev.usbharu.hideout.activitypub.service.webfinger.WebFingerApiService import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException +import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException import dev.usbharu.hideout.util.AcctUtil import kotlinx.coroutines.runBlocking import org.slf4j.LoggerFactory @@ -29,7 +29,7 @@ class WebFingerController( } val user = try { webFingerApiService.findByNameAndDomain(acct.username, acct.domain ?: applicationConfig.url.host) - } catch (_: FailedToGetResourcesException) { + } catch (_: UserNotFoundException) { return@runBlocking ResponseEntity.notFound().build() } val webFinger = WebFinger( diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/query/NoteQueryService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/query/NoteQueryService.kt index f61b6992..4c57168c 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/query/NoteQueryService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/query/NoteQueryService.kt @@ -4,6 +4,6 @@ import dev.usbharu.hideout.activitypub.domain.model.Note import dev.usbharu.hideout.core.domain.model.post.Post interface NoteQueryService { - suspend fun findById(id: Long): Pair - suspend fun findByApid(apId: String): Pair + suspend fun findById(id: Long): Pair? + suspend fun findByApid(apId: String): Pair? } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/APDeliverAcceptJobProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/APDeliverAcceptJobProcessor.kt index 3a48b5df..0936d8b7 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/APDeliverAcceptJobProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/APDeliverAcceptJobProcessor.kt @@ -2,22 +2,22 @@ package dev.usbharu.hideout.activitypub.service.activity.accept import dev.usbharu.hideout.activitypub.service.common.APRequestService import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.external.job.DeliverAcceptJob import dev.usbharu.hideout.core.external.job.DeliverAcceptJobParam -import dev.usbharu.hideout.core.query.ActorQueryService import dev.usbharu.hideout.core.service.job.JobProcessor import org.springframework.stereotype.Service @Service class APDeliverAcceptJobProcessor( private val apRequestService: APRequestService, - private val actorQueryService: ActorQueryService, private val deliverAcceptJob: DeliverAcceptJob, - private val transaction: Transaction + private val transaction: Transaction, + private val actorRepository: ActorRepository ) : JobProcessor { override suspend fun process(param: DeliverAcceptJobParam): Unit = transaction.transaction { - apRequestService.apPost(param.inbox, param.accept, actorQueryService.findById(param.signer)) + apRequestService.apPost(param.inbox, param.accept, actorRepository.findById(param.signer)) } override fun job(): DeliverAcceptJob = deliverAcceptJob diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessor.kt index 1a48f22d..cae83622 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessor.kt @@ -7,15 +7,16 @@ import dev.usbharu.hideout.activitypub.service.common.AbstractActivityPubProcess import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessContext import dev.usbharu.hideout.activitypub.service.common.ActivityType import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.query.ActorQueryService +import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.service.relationship.RelationshipService import org.springframework.stereotype.Service @Service class ApAcceptProcessor( transaction: Transaction, - private val actorQueryService: ActorQueryService, - private val relationshipService: RelationshipService + private val relationshipService: RelationshipService, + private val actorRepository: ActorRepository ) : AbstractActivityPubProcessor(transaction) { @@ -32,8 +33,8 @@ class ApAcceptProcessor( val userUrl = follow.apObject val followerUrl = follow.actor - val user = actorQueryService.findByUrl(userUrl) - val follower = actorQueryService.findByUrl(followerUrl) + val user = actorRepository.findByUrl(userUrl) ?: throw UserNotFoundException.withUrl(userUrl) + val follower = actorRepository.findByUrl(followerUrl) ?: throw UserNotFoundException.withUrl(followerUrl) relationshipService.acceptFollowRequest(user.id, follower.id) logger.debug("SUCCESS Follow from ${user.url} to ${follower.url}.") diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/block/BlockActivityPubProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/block/BlockActivityPubProcessor.kt index e31f4e6c..7262edd4 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/block/BlockActivityPubProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/block/BlockActivityPubProcessor.kt @@ -5,7 +5,8 @@ import dev.usbharu.hideout.activitypub.service.common.AbstractActivityPubProcess import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessContext import dev.usbharu.hideout.activitypub.service.common.ActivityType import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.query.ActorQueryService +import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.service.relationship.RelationshipService import org.springframework.stereotype.Service @@ -14,14 +15,17 @@ import org.springframework.stereotype.Service */ @Service class BlockActivityPubProcessor( - private val actorQueryService: ActorQueryService, private val relationshipService: RelationshipService, + private val actorRepository: ActorRepository, transaction: Transaction ) : AbstractActivityPubProcessor(transaction) { override suspend fun internalProcess(activity: ActivityPubProcessContext) { - val user = actorQueryService.findByUrl(activity.activity.actor) - val target = actorQueryService.findByUrl(activity.activity.apObject) + val user = actorRepository.findByUrl(activity.activity.actor) + ?: throw UserNotFoundException.withUrl(activity.activity.actor) + val target = actorRepository.findByUrl(activity.activity.apObject) ?: throw UserNotFoundException.withUrl( + activity.activity.apObject + ) relationshipService.block(user.id, target.id) } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/ApSendCreateServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/ApSendCreateServiceImpl.kt index 0ba9413e..500a7c76 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/ApSendCreateServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/ApSendCreateServiceImpl.kt @@ -4,9 +4,11 @@ import com.fasterxml.jackson.databind.ObjectMapper import dev.usbharu.hideout.activitypub.domain.model.Create import dev.usbharu.hideout.activitypub.query.NoteQueryService import dev.usbharu.hideout.application.config.ApplicationConfig +import dev.usbharu.hideout.core.domain.exception.resource.PostNotFoundException +import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.post.Post import dev.usbharu.hideout.core.external.job.DeliverPostJob -import dev.usbharu.hideout.core.query.ActorQueryService import dev.usbharu.hideout.core.query.FollowerQueryService import dev.usbharu.hideout.core.service.job.JobQueueParentService import org.slf4j.LoggerFactory @@ -17,9 +19,9 @@ class ApSendCreateServiceImpl( private val followerQueryService: FollowerQueryService, private val objectMapper: ObjectMapper, private val jobQueueParentService: JobQueueParentService, - private val actorQueryService: ActorQueryService, private val noteQueryService: NoteQueryService, - private val applicationConfig: ApplicationConfig + private val applicationConfig: ApplicationConfig, + private val actorRepository: ActorRepository ) : ApSendCreateService { override suspend fun createNote(post: Post) { logger.info("CREATE Create Local Note ${post.url}") @@ -29,8 +31,8 @@ class ApSendCreateServiceImpl( logger.debug("DELIVER Deliver Note Create ${followers.size} accounts.") - val userEntity = actorQueryService.findById(post.actorId) - val note = noteQueryService.findById(post.id).first + val userEntity = actorRepository.findById(post.actorId) ?: throw UserNotFoundException.withId(post.actorId) + val note = noteQueryService.findById(post.id)?.first ?: throw PostNotFoundException.withId(post.id) val create = Create( name = "Create Note", apObject = note, diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APDeleteProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APDeleteProcessor.kt index 508c3d45..1ad2acbf 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APDeleteProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APDeleteProcessor.kt @@ -8,9 +8,8 @@ import dev.usbharu.hideout.activitypub.service.common.AbstractActivityPubProcess import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessContext import dev.usbharu.hideout.activitypub.service.common.ActivityType import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException -import dev.usbharu.hideout.core.query.ActorQueryService -import dev.usbharu.hideout.core.query.PostQueryService +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository +import dev.usbharu.hideout.core.domain.model.post.PostRepository import dev.usbharu.hideout.core.service.post.PostService import dev.usbharu.hideout.core.service.user.UserService import org.springframework.stereotype.Service @@ -18,10 +17,10 @@ import org.springframework.stereotype.Service @Service class APDeleteProcessor( transaction: Transaction, - private val postQueryService: PostQueryService, - private val actorQueryService: ActorQueryService, private val userService: UserService, - private val postService: PostService + private val postService: PostService, + private val actorRepository: ActorRepository, + private val postRepository: PostRepository ) : AbstractActivityPubProcessor(transaction) { override suspend fun internalProcess(activity: ActivityPubProcessContext) { @@ -34,19 +33,15 @@ class APDeleteProcessor( throw IllegalActivityPubObjectException("object hasn't id or object") } - try { - val actor = actorQueryService.findByUrl(deleteId) - userService.deleteRemoteActor(actor.id) - } catch (e: Exception) { - logger.warn("FAILED delete id: {} is not found.", deleteId, e) - } + val actor = actorRepository.findByUrl(deleteId) + actor?.let { userService.deleteRemoteActor(it.id) } - try { - val post = postQueryService.findByApId(deleteId) - postService.deleteRemote(post) - } catch (e: FailedToGetResourcesException) { - logger.warn("FAILED delete id: {} is not found.", deleteId, e) + val post = postRepository.findByApId(deleteId) + if (post == null) { + logger.warn("FAILED Delete id: {} is not found.", deleteId) + return } + postService.deleteRemote(post) } override fun isSupported(activityType: ActivityType): Boolean = activityType == ActivityType.Delete diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APDeliverDeleteJobProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APDeliverDeleteJobProcessor.kt index 5c72c304..9924594f 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APDeliverDeleteJobProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APDeliverDeleteJobProcessor.kt @@ -2,21 +2,21 @@ package dev.usbharu.hideout.activitypub.service.activity.delete import dev.usbharu.hideout.activitypub.service.common.APRequestService import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.external.job.DeliverDeleteJob import dev.usbharu.hideout.core.external.job.DeliverDeleteJobParam -import dev.usbharu.hideout.core.query.ActorQueryService import dev.usbharu.hideout.core.service.job.JobProcessor import org.springframework.stereotype.Service @Service class APDeliverDeleteJobProcessor( private val apRequestService: APRequestService, - private val actorQueryService: ActorQueryService, private val transaction: Transaction, - private val deliverDeleteJob: DeliverDeleteJob + private val deliverDeleteJob: DeliverDeleteJob, + private val actorRepository: ActorRepository ) : JobProcessor { override suspend fun process(param: DeliverDeleteJobParam): Unit = transaction.transaction { - apRequestService.apPost(param.inbox, param.delete, actorQueryService.findById(param.signer)) + apRequestService.apPost(param.inbox, param.delete, actorRepository.findById(param.signer)) } override fun job(): DeliverDeleteJob = deliverDeleteJob diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APSendDeleteService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APSendDeleteService.kt index 2d1dde2a..626418d8 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APSendDeleteService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APSendDeleteService.kt @@ -4,11 +4,12 @@ import dev.usbharu.hideout.activitypub.domain.model.Delete import dev.usbharu.hideout.activitypub.domain.model.Tombstone import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectValue import dev.usbharu.hideout.application.config.ApplicationConfig +import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException import dev.usbharu.hideout.core.domain.model.actor.Actor +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.post.Post import dev.usbharu.hideout.core.external.job.DeliverDeleteJob import dev.usbharu.hideout.core.external.job.DeliverDeleteJobParam -import dev.usbharu.hideout.core.query.ActorQueryService import dev.usbharu.hideout.core.query.FollowerQueryService import dev.usbharu.hideout.core.service.job.JobQueueParentService import org.springframework.stereotype.Service @@ -24,11 +25,12 @@ class APSendDeleteServiceImpl( private val jobQueueParentService: JobQueueParentService, private val delverDeleteJob: DeliverDeleteJob, private val followerQueryService: FollowerQueryService, - private val actorQueryService: ActorQueryService, - private val applicationConfig: ApplicationConfig + private val applicationConfig: ApplicationConfig, + private val actorRepository: ActorRepository ) : APSendDeleteService { override suspend fun sendDeleteNote(deletedPost: Post) { - val actor = actorQueryService.findById(deletedPost.actorId) + val actor = + actorRepository.findById(deletedPost.actorId) ?: throw UserNotFoundException.withId(deletedPost.actorId) val followersById = followerQueryService.findFollowersById(deletedPost.actorId) val delete = Delete( diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowJobProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowJobProcessor.kt index 292f0ee2..50641d62 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowJobProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowJobProcessor.kt @@ -5,9 +5,10 @@ import com.fasterxml.jackson.module.kotlin.readValue import dev.usbharu.hideout.activitypub.domain.model.Follow import dev.usbharu.hideout.activitypub.service.objects.user.APUserService import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.external.job.ReceiveFollowJob import dev.usbharu.hideout.core.external.job.ReceiveFollowJobParam -import dev.usbharu.hideout.core.query.ActorQueryService import dev.usbharu.hideout.core.service.job.JobProcessor import dev.usbharu.hideout.core.service.relationship.RelationshipService import org.slf4j.LoggerFactory @@ -16,10 +17,10 @@ import org.springframework.stereotype.Service @Service class APReceiveFollowJobProcessor( private val transaction: Transaction, - private val actorQueryService: ActorQueryService, private val apUserService: APUserService, private val objectMapper: ObjectMapper, - private val relationshipService: RelationshipService + private val relationshipService: RelationshipService, + private val actorRepository: ActorRepository ) : JobProcessor { override suspend fun process(param: ReceiveFollowJobParam) = transaction.transaction { @@ -28,9 +29,10 @@ class APReceiveFollowJobProcessor( logger.info("START Follow from: {} to {}", param.targetActor, param.actor) - val targetEntity = actorQueryService.findByUrl(param.targetActor) + val targetEntity = + actorRepository.findByUrl(param.targetActor) ?: throw UserNotFoundException.withUrl(param.targetActor) val followActorEntity = - actorQueryService.findByUrl(follow.actor) + actorRepository.findByUrl(follow.actor) ?: throw UserNotFoundException.withUrl(follow.actor) relationshipService.followRequest(followActorEntity.id, targetEntity.id) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APLikeProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APLikeProcessor.kt index 665c7c94..994e2135 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APLikeProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APLikeProcessor.kt @@ -8,7 +8,6 @@ import dev.usbharu.hideout.activitypub.service.common.ActivityType import dev.usbharu.hideout.activitypub.service.objects.note.APNoteService import dev.usbharu.hideout.activitypub.service.objects.user.APUserService import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.query.PostQueryService import dev.usbharu.hideout.core.service.reaction.ReactionService import org.springframework.stereotype.Service @@ -17,7 +16,6 @@ class APLikeProcessor( transaction: Transaction, private val apUserService: APUserService, private val apNoteService: APNoteService, - private val postQueryService: PostQueryService, private val reactionService: ReactionService ) : AbstractActivityPubProcessor(transaction) { @@ -30,23 +28,20 @@ class APLikeProcessor( val personWithEntity = apUserService.fetchPersonWithEntity(actor) try { - apNoteService.fetchNoteAsync(target).await() + val post = apNoteService.fetchNoteWithEntity(target).second + reactionService.receiveReaction( + content, + actor.substringAfter("://").substringBefore("/"), + personWithEntity.second.id, + post.id + ) + + logger.debug("SUCCESS Add Like($content) from ${personWithEntity.second.url} to ${post.url}") } catch (e: FailedToGetActivityPubResourceException) { logger.debug("FAILED failed to get {}", target) logger.trace("", e) return } - - val post = postQueryService.findByUrl(target) - - reactionService.receiveReaction( - content, - actor.substringAfter("://").substringBefore("/"), - personWithEntity.second.id, - post.id - ) - - logger.debug("SUCCESS Add Like($content) from ${personWithEntity.second.url} to ${post.url}") } override fun isSupported(activityType: ActivityType): Boolean = activityType == ActivityType.Like diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APReactionService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APReactionService.kt index 1995cf83..ae87392c 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APReactionService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APReactionService.kt @@ -1,12 +1,14 @@ package dev.usbharu.hideout.activitypub.service.activity.like import com.fasterxml.jackson.databind.ObjectMapper +import dev.usbharu.hideout.core.domain.exception.resource.PostNotFoundException +import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository +import dev.usbharu.hideout.core.domain.model.post.PostRepository import dev.usbharu.hideout.core.domain.model.reaction.Reaction import dev.usbharu.hideout.core.external.job.DeliverReactionJob import dev.usbharu.hideout.core.external.job.DeliverRemoveReactionJob -import dev.usbharu.hideout.core.query.ActorQueryService import dev.usbharu.hideout.core.query.FollowerQueryService -import dev.usbharu.hideout.core.query.PostQueryService import dev.usbharu.hideout.core.service.job.JobQueueParentService import org.springframework.beans.factory.annotation.Qualifier import org.springframework.stereotype.Service @@ -19,16 +21,16 @@ interface APReactionService { @Service class APReactionServiceImpl( private val jobQueueParentService: JobQueueParentService, - private val actorQueryService: ActorQueryService, private val followerQueryService: FollowerQueryService, - private val postQueryService: PostQueryService, - @Qualifier("activitypub") private val objectMapper: ObjectMapper + private val actorRepository: ActorRepository, + @Qualifier("activitypub") private val objectMapper: ObjectMapper, + private val postRepository: PostRepository ) : APReactionService { override suspend fun reaction(like: Reaction) { val followers = followerQueryService.findFollowersById(like.actorId) - val user = actorQueryService.findById(like.actorId) + val user = actorRepository.findById(like.actorId) ?: throw UserNotFoundException.withId(like.actorId) val post = - postQueryService.findById(like.postId) + postRepository.findById(like.postId) ?: throw PostNotFoundException.withId(like.postId) followers.forEach { follower -> jobQueueParentService.schedule(DeliverReactionJob) { props[DeliverReactionJob.actor] = user.url @@ -42,9 +44,9 @@ class APReactionServiceImpl( override suspend fun removeReaction(like: Reaction) { val followers = followerQueryService.findFollowersById(like.actorId) - val user = actorQueryService.findById(like.actorId) + val user = actorRepository.findById(like.actorId) ?: throw UserNotFoundException.withId(like.actorId) val post = - postQueryService.findById(like.postId) + postRepository.findById(like.postId) ?: throw PostNotFoundException.withId(like.postId) followers.forEach { follower -> jobQueueParentService.schedule(DeliverRemoveReactionJob) { props[DeliverRemoveReactionJob.actor] = user.url diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApReactionJobProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApReactionJobProcessor.kt index e7857e8e..ee4f8605 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApReactionJobProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApReactionJobProcessor.kt @@ -4,21 +4,21 @@ import dev.usbharu.hideout.activitypub.domain.model.Like import dev.usbharu.hideout.activitypub.service.common.APRequestService import dev.usbharu.hideout.application.config.ApplicationConfig import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.external.job.DeliverReactionJob import dev.usbharu.hideout.core.external.job.DeliverReactionJobParam -import dev.usbharu.hideout.core.query.ActorQueryService import dev.usbharu.hideout.core.service.job.JobProcessor import org.springframework.stereotype.Service @Service class ApReactionJobProcessor( - private val actorQueryService: ActorQueryService, private val apRequestService: APRequestService, private val applicationConfig: ApplicationConfig, - private val transaction: Transaction + private val transaction: Transaction, + private val actorRepository: ActorRepository ) : JobProcessor { override suspend fun process(param: DeliverReactionJobParam): Unit = transaction.transaction { - val signer = actorQueryService.findByUrl(param.actor) + val signer = actorRepository.findByUrl(param.actor) apRequestService.apPost( param.inbox, diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApRemoveReactionJobProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApRemoveReactionJobProcessor.kt index 5dd3e6e7..0d48c53a 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApRemoveReactionJobProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApRemoveReactionJobProcessor.kt @@ -7,25 +7,25 @@ import dev.usbharu.hideout.activitypub.domain.model.Undo import dev.usbharu.hideout.activitypub.service.common.APRequestService import dev.usbharu.hideout.application.config.ApplicationConfig import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.external.job.DeliverRemoveReactionJob import dev.usbharu.hideout.core.external.job.DeliverRemoveReactionJobParam -import dev.usbharu.hideout.core.query.ActorQueryService import dev.usbharu.hideout.core.service.job.JobProcessor import org.springframework.stereotype.Service import java.time.Instant @Service class ApRemoveReactionJobProcessor( - private val actorQueryService: ActorQueryService, private val transaction: Transaction, private val objectMapper: ObjectMapper, private val apRequestService: APRequestService, - private val applicationConfig: ApplicationConfig + private val applicationConfig: ApplicationConfig, + private val actorRepository: ActorRepository ) : JobProcessor { override suspend fun process(param: DeliverRemoveReactionJobParam): Unit = transaction.transaction { val like = objectMapper.readValue(param.like) - val signer = actorQueryService.findByUrl(param.actor) + val signer = actorRepository.findByUrl(param.actor) apRequestService.apPost( param.inbox, diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/APDeliverRejectJobProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/APDeliverRejectJobProcessor.kt index 0ef702d5..a7706473 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/APDeliverRejectJobProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/APDeliverRejectJobProcessor.kt @@ -2,22 +2,22 @@ package dev.usbharu.hideout.activitypub.service.activity.reject import dev.usbharu.hideout.activitypub.service.common.APRequestService import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.external.job.DeliverRejectJob import dev.usbharu.hideout.core.external.job.DeliverRejectJobParam -import dev.usbharu.hideout.core.query.ActorQueryService import dev.usbharu.hideout.core.service.job.JobProcessor import org.springframework.stereotype.Component @Component class APDeliverRejectJobProcessor( private val apRequestService: APRequestService, - private val actorQueryService: ActorQueryService, private val deliverRejectJob: DeliverRejectJob, - private val transaction: Transaction + private val transaction: Transaction, + private val actorRepository: ActorRepository ) : JobProcessor { override suspend fun process(param: DeliverRejectJobParam): Unit = transaction.transaction { - apRequestService.apPost(param.inbox, param.reject, actorQueryService.findById(param.signer)) + apRequestService.apPost(param.inbox, param.reject, actorRepository.findById(param.signer)) } override fun job(): DeliverRejectJob = deliverRejectJob diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/ApRejectProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/ApRejectProcessor.kt index 29bbd1f9..d1c53f0e 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/ApRejectProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/ApRejectProcessor.kt @@ -6,15 +6,16 @@ import dev.usbharu.hideout.activitypub.service.common.AbstractActivityPubProcess import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessContext import dev.usbharu.hideout.activitypub.service.common.ActivityType import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.query.ActorQueryService +import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.service.relationship.RelationshipService import org.springframework.stereotype.Service @Service class ApRejectProcessor( private val relationshipService: RelationshipService, - private val actorQueryService: ActorQueryService, - transaction: Transaction + transaction: Transaction, + private val actorRepository: ActorRepository ) : AbstractActivityPubProcessor(transaction) { override suspend fun internalProcess(activity: ActivityPubProcessContext) { @@ -26,13 +27,15 @@ class ApRejectProcessor( } when (activityType) { "Follow" -> { - val user = actorQueryService.findByUrl(activity.activity.actor) + val user = actorRepository.findByUrl(activity.activity.actor) ?: throw UserNotFoundException.withUrl( + activity.activity.actor + ) activity.activity.apObject as Follow val actor = activity.activity.apObject.actor - val target = actorQueryService.findByUrl(actor) + val target = actorRepository.findByUrl(actor) ?: throw UserNotFoundException.withUrl(actor) logger.debug("REJECT Follow user {} target {}", user.url, target.url) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APDeliverUndoJobProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APDeliverUndoJobProcessor.kt index 70e31921..ebc3257c 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APDeliverUndoJobProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APDeliverUndoJobProcessor.kt @@ -2,9 +2,9 @@ package dev.usbharu.hideout.activitypub.service.activity.undo import dev.usbharu.hideout.activitypub.service.common.APRequestService import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.external.job.DeliverUndoJob import dev.usbharu.hideout.core.external.job.DeliverUndoJobParam -import dev.usbharu.hideout.core.query.ActorQueryService import dev.usbharu.hideout.core.service.job.JobProcessor import org.springframework.stereotype.Service @@ -12,11 +12,11 @@ import org.springframework.stereotype.Service class APDeliverUndoJobProcessor( private val deliverUndoJob: DeliverUndoJob, private val apRequestService: APRequestService, - private val actorQueryService: ActorQueryService, - private val transaction: Transaction + private val transaction: Transaction, + private val actorRepository: ActorRepository ) : JobProcessor { override suspend fun process(param: DeliverUndoJobParam): Unit = transaction.transaction { - apRequestService.apPost(param.inbox, param.undo, actorQueryService.findById(param.signer)) + apRequestService.apPost(param.inbox, param.undo, actorRepository.findById(param.signer)) } override fun job(): DeliverUndoJob = deliverUndoJob diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt index 77388337..864c848f 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt @@ -7,8 +7,11 @@ import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessContext import dev.usbharu.hideout.activitypub.service.common.ActivityType import dev.usbharu.hideout.activitypub.service.objects.user.APUserService import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.query.ActorQueryService -import dev.usbharu.hideout.core.query.PostQueryService +import dev.usbharu.hideout.core.domain.exception.resource.PostNotFoundException +import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException +import dev.usbharu.hideout.core.domain.exception.resource.local.LocalUserNotFoundException +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository +import dev.usbharu.hideout.core.domain.model.post.PostRepository import dev.usbharu.hideout.core.service.reaction.ReactionService import dev.usbharu.hideout.core.service.relationship.RelationshipService import org.springframework.stereotype.Service @@ -17,69 +20,36 @@ import org.springframework.stereotype.Service class APUndoProcessor( transaction: Transaction, private val apUserService: APUserService, - private val actorQueryService: ActorQueryService, private val relationshipService: RelationshipService, - private val postQueryService: PostQueryService, - private val reactionService: ReactionService -) : - AbstractActivityPubProcessor(transaction) { + private val reactionService: ReactionService, + private val actorRepository: ActorRepository, + private val postRepository: PostRepository +) : AbstractActivityPubProcessor(transaction) { override suspend fun internalProcess(activity: ActivityPubProcessContext) { val undo = activity.activity - val type = - undo.apObject.type - .firstOrNull { it == "Block" || it == "Follow" || it == "Like" || it == "Announce" || it == "Accept" } - ?: return + val type = undo.apObject.type.firstOrNull { + it == "Block" || it == "Follow" || it == "Like" || it == "Announce" || it == "Accept" + } ?: return when (type) { "Follow" -> { - val follow = undo.apObject as Follow - - apUserService.fetchPerson(undo.actor, follow.apObject) - val follower = actorQueryService.findByUrl(undo.actor) - val target = actorQueryService.findByUrl(follow.apObject) - - relationshipService.unfollow(follower.id, target.id) + follow(undo) return } "Block" -> { - val block = undo.apObject as Block - - val blocker = apUserService.fetchPersonWithEntity(undo.actor, block.apObject).second - val target = actorQueryService.findByUrl(block.apObject) - - relationshipService.unblock(blocker.id, target.id) + block(undo) return } "Accept" -> { - val accept = undo.apObject as Accept - - val acceptObject = if (accept.apObject is ObjectValue) { - accept.apObject.`object` - } else if (accept.apObject is Follow) { - accept.apObject.apObject - } else { - logger.warn("FAILED Unsupported type. Undo Accept {}", accept.apObject.type) - return - } - - val accepter = apUserService.fetchPersonWithEntity(undo.actor, acceptObject).second - val target = actorQueryService.findByUrl(acceptObject) - - relationshipService.rejectFollowRequest(accepter.id, target.id) + accept(undo) return } "Like" -> { - val like = undo.apObject as Like - - val post = postQueryService.findByUrl(like.apObject) - - val actor = actorQueryService.findByUrl(like.actor) - - reactionService.receiveRemoveReaction(actor.id, post.id) + like(undo) return } @@ -88,6 +58,57 @@ class APUndoProcessor( TODO() } + private suspend fun accept(undo: Undo) { + val accept = undo.apObject as Accept + + val acceptObject = if (accept.apObject is ObjectValue) { + accept.apObject.`object` + } else if (accept.apObject is Follow) { + accept.apObject.apObject + } else { + logger.warn("FAILED Unsupported type. Undo Accept {}", accept.apObject.type) + return + } + + val accepter = apUserService.fetchPersonWithEntity(undo.actor, acceptObject).second + val target = actorRepository.findByUrl(acceptObject) ?: throw UserNotFoundException.withUrl(acceptObject) + + relationshipService.rejectFollowRequest(accepter.id, target.id) + return + } + + private suspend fun like(undo: Undo) { + val like = undo.apObject as Like + + val post = postRepository.findByUrl(like.apObject) ?: throw PostNotFoundException.withUrl(like.apObject) + + val signer = actorRepository.findById(post.actorId) ?: throw LocalUserNotFoundException.withId(post.actorId) + val actor = apUserService.fetchPersonWithEntity(like.actor, signer.url).second + + reactionService.receiveRemoveReaction(actor.id, post.id) + return + } + + private suspend fun block(undo: Undo) { + val block = undo.apObject as Block + + val blocker = apUserService.fetchPersonWithEntity(undo.actor, block.apObject).second + val target = actorRepository.findByUrl(block.apObject) ?: throw UserNotFoundException.withUrl(block.apObject) + + relationshipService.unblock(blocker.id, target.id) + return + } + + private suspend fun follow(undo: Undo) { + val follow = undo.apObject as Follow + + val follower = apUserService.fetchPersonWithEntity(undo.actor, follow.apObject).second + val target = actorRepository.findByUrl(follow.apObject) ?: throw UserNotFoundException.withUrl(follow.apObject) + + relationshipService.unfollow(follower.id, target.id) + return + } + override fun isSupported(activityType: ActivityType): Boolean = activityType == ActivityType.Undo override fun type(): Class = Undo::class.java diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APService.kt index 4f5c2902..fdf89ba8 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APService.kt @@ -229,7 +229,6 @@ class APServiceImpl( props[it.json] = json props[it.type] = type.name val writeValueAsString = objectMapper.writeValueAsString(httpRequest) - println(writeValueAsString) props[it.httpRequest] = writeValueAsString props[it.headers] = objectMapper.writeValueAsString(map) } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/AbstractActivityPubProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/AbstractActivityPubProcessor.kt index 1bb106e3..e26e909d 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/AbstractActivityPubProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/AbstractActivityPubProcessor.kt @@ -18,7 +18,7 @@ abstract class AbstractActivityPubProcessor( if (activity.isAuthorized.not() && allowUnauthorized.not()) { throw HttpSignatureUnauthorizedException() } - logger.info("START ActivityPub process") + logger.info("START ActivityPub process. {}", this.type()) try { transaction.transaction { internalProcess(activity) @@ -27,7 +27,7 @@ abstract class AbstractActivityPubProcessor( logger.warn("FAILED ActivityPub process", e) throw FailedProcessException("Failed process", e) } - logger.info("SUCCESS ActivityPub process") + logger.info("SUCCESS ActivityPub process. {}", this.type()) } abstract suspend fun internalProcess(activity: ActivityPubProcessContext) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/inbox/InboxJobProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/inbox/InboxJobProcessor.kt index 44906dc7..2a884ae0 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/inbox/InboxJobProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/inbox/InboxJobProcessor.kt @@ -7,10 +7,8 @@ import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessContext import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessor import dev.usbharu.hideout.activitypub.service.objects.user.APUserService import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException import dev.usbharu.hideout.core.external.job.InboxJob import dev.usbharu.hideout.core.external.job.InboxJobParam -import dev.usbharu.hideout.core.query.ActorQueryService import dev.usbharu.hideout.core.service.job.JobProcessor import dev.usbharu.hideout.util.RsaUtil import dev.usbharu.httpsignature.common.HttpHeaders @@ -30,7 +28,6 @@ class InboxJobProcessor( private val objectMapper: ObjectMapper, private val signatureHeaderParser: SignatureHeaderParser, private val signatureVerifier: HttpSignatureVerifier, - private val actorQueryService: ActorQueryService, private val apUserService: APUserService, private val transaction: Transaction ) : JobProcessor { @@ -41,7 +38,8 @@ class InboxJobProcessor( private suspend fun verifyHttpSignature( httpRequest: HttpRequest, signature: Signature, - transaction: Transaction + transaction: Transaction, + actor: String ): Boolean { val requiredHeaders = when (httpRequest.method) { HttpMethod.GET -> getRequiredHeaders @@ -53,11 +51,7 @@ class InboxJobProcessor( } val user = transaction.transaction { - try { - actorQueryService.findByKeyId(signature.keyId) - } catch (_: FailedToGetResourcesException) { - apUserService.fetchPersonWithEntity(signature.keyId).second - } + apUserService.fetchPersonWithEntity(actor).second } @Suppress("TooGenericExceptionCaught") @@ -93,7 +87,6 @@ class InboxJobProcessor( logger.trace("type: {}\njson: \n{}", param.type, jsonNode.toPrettyString()) } - val map = objectMapper.readValue>>(param.headers) val httpRequest = objectMapper.readValue(param.httpRequest).copy(headers = HttpHeaders(map)) @@ -104,7 +97,17 @@ class InboxJobProcessor( logger.debug("Has signature? {}", signature != null) - val verify = signature?.let { verifyHttpSignature(httpRequest, it, transaction) } ?: false + // todo 不正なactorを取得してしまわないようにする + val verify = + signature?.let { + verifyHttpSignature( + httpRequest, + it, + transaction, + jsonNode.get("actor")?.asText() ?: signature.keyId + ) + } + ?: false logger.debug("Is verifying success? {}", verify) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteService.kt index 32a542d4..7d284e0f 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteService.kt @@ -6,39 +6,21 @@ import dev.usbharu.hideout.activitypub.query.NoteQueryService import dev.usbharu.hideout.activitypub.service.common.APResourceResolveService import dev.usbharu.hideout.activitypub.service.common.resolve import dev.usbharu.hideout.activitypub.service.objects.user.APUserService -import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException import dev.usbharu.hideout.core.domain.model.post.Post import dev.usbharu.hideout.core.domain.model.post.PostRepository import dev.usbharu.hideout.core.domain.model.post.Visibility -import dev.usbharu.hideout.core.query.PostQueryService import dev.usbharu.hideout.core.service.media.MediaService import dev.usbharu.hideout.core.service.media.RemoteMedia import dev.usbharu.hideout.core.service.post.PostService import io.ktor.client.plugins.* -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Deferred -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.async -import kotlinx.coroutines.slf4j.MDCContext -import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction import org.slf4j.LoggerFactory -import org.springframework.cache.annotation.Cacheable import org.springframework.stereotype.Service import java.time.Instant interface APNoteService { - - @Cacheable("fetchNote") - fun fetchNoteAsync(url: String, targetActor: String? = null): Deferred { - return CoroutineScope(Dispatchers.IO + MDCContext()).async { - newSuspendedTransaction(MDCContext()) { - fetchNote(url, targetActor) - } - } - } - - suspend fun fetchNote(url: String, targetActor: String? = null): Note + suspend fun fetchNote(url: String, targetActor: String? = null): Note = fetchNoteWithEntity(url, targetActor).first suspend fun fetchNote(note: Note, targetActor: String? = null): Note + suspend fun fetchNoteWithEntity(url: String, targetActor: String? = null): Pair } @Service @@ -46,7 +28,6 @@ interface APNoteService { class APNoteServiceImpl( private val postRepository: PostRepository, private val apUserService: APUserService, - private val postQueryService: PostQueryService, private val postService: PostService, private val apResourceResolveService: APResourceResolveService, private val postBuilder: Post.PostBuilder, @@ -57,13 +38,14 @@ class APNoteServiceImpl( private val logger = LoggerFactory.getLogger(APNoteServiceImpl::class.java) - override suspend fun fetchNote(url: String, targetActor: String?): Note { + override suspend fun fetchNoteWithEntity(url: String, targetActor: String?): Pair { logger.debug("START Fetch Note url: {}", url) - try { - val post = noteQueryService.findByApid(url) + + val post = noteQueryService.findByApid(url) + + if (post != null) { logger.debug("SUCCESS Found in local url: {}", url) - return post.first - } catch (_: FailedToGetResourcesException) { + return post } logger.info("AP GET url: {}", url) @@ -77,7 +59,7 @@ class APNoteServiceImpl( ) throw FailedToGetActivityPubResourceException("Could not retrieve $url.", e) } - val savedNote = saveNote(note, targetActor, url) + val savedNote = saveIfMissing(note, targetActor, url) logger.debug("SUCCESS Fetch Note url: {}", url) return savedNote } @@ -86,58 +68,51 @@ class APNoteServiceImpl( note: Note, targetActor: String?, url: String - ): Note { - requireNotNull(note.id) { "id is null" } + ): Pair = noteQueryService.findByApid(note.id) ?: saveNote(note, targetActor, url) - return try { - noteQueryService.findByApid(note.id).first - } catch (_: FailedToGetResourcesException) { - saveNote(note, targetActor, url) - } - } - - private suspend fun saveNote(note: Note, targetActor: String?, url: String): Note { + private suspend fun saveNote(note: Note, targetActor: String?, url: String): Pair { val person = apUserService.fetchPersonWithEntity( note.attributedTo, targetActor ) + val post = postRepository.findByApId(note.id) + + if (post != null) { + return note to post + } + logger.debug("VISIBILITY url: {} to: {} cc: {}", note.id, note.to, note.cc) - val visibility = - if (note.to.contains(public)) { - Visibility.PUBLIC - } else if (note.to.contains(person.second.followers) && note.cc.contains(public)) { - Visibility.UNLISTED - } else if (note.to.contains(person.second.followers)) { - Visibility.FOLLOWERS - } else { - Visibility.DIRECT - } + val visibility = if (note.to.contains(public)) { + Visibility.PUBLIC + } else if (note.to.contains(person.second.followers) && note.cc.contains(public)) { + Visibility.UNLISTED + } else if (note.to.contains(person.second.followers)) { + Visibility.FOLLOWERS + } else { + Visibility.DIRECT + } logger.debug("VISIBILITY is {} url: {}", visibility.name, note.id) val reply = note.inReplyTo?.let { fetchNote(it, targetActor) - postQueryService.findByUrl(it) + postRepository.findByUrl(it) } - val mediaList = note.attachment - .filter { it.url != null } - .map { - mediaService.uploadRemoteMedia( - RemoteMedia( - it.name, - it.url, - it.mediaType, - description = it.name - ) + val mediaList = note.attachment.map { + mediaService.uploadRemoteMedia( + RemoteMedia( + it.name, + it.url, + it.mediaType, + description = it.name ) - } - .map { it.id } + ) + }.map { it.id } - // TODO: リモートのメディア処理を追加 - postService.createRemote( + val createRemote = postService.createRemote( postBuilder.of( id = postRepository.generateId(), actorId = person.second.id, @@ -151,11 +126,11 @@ class APNoteServiceImpl( mediaIds = mediaList ) ) - return note + return note to createRemote } override suspend fun fetchNote(note: Note, targetActor: String?): Note = - saveIfMissing(note, targetActor, note.id) + saveIfMissing(note, targetActor, note.id).first companion object { const val public: String = "https://www.w3.org/ns/activitystreams#Public" diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/ApNoteJobProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/ApNoteJobProcessor.kt index a9dbab74..921eaddf 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/ApNoteJobProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/ApNoteJobProcessor.kt @@ -5,9 +5,9 @@ import com.fasterxml.jackson.module.kotlin.readValue import dev.usbharu.hideout.activitypub.domain.model.Create import dev.usbharu.hideout.activitypub.service.common.APRequestService import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.external.job.DeliverPostJob import dev.usbharu.hideout.core.external.job.DeliverPostJobParam -import dev.usbharu.hideout.core.query.ActorQueryService import dev.usbharu.hideout.core.service.job.JobProcessor import org.slf4j.LoggerFactory import org.springframework.stereotype.Service @@ -16,13 +16,13 @@ import org.springframework.stereotype.Service class ApNoteJobProcessor( private val transaction: Transaction, private val objectMapper: ObjectMapper, - private val actorQueryService: ActorQueryService, - private val apRequestService: APRequestService + private val apRequestService: APRequestService, + private val actorRepository: ActorRepository ) : JobProcessor { override suspend fun process(param: DeliverPostJobParam) { val create = objectMapper.readValue(param.create) transaction.transaction { - val signer = actorQueryService.findByUrl(param.actor) + val signer = actorRepository.findByUrl(param.actor) logger.debug("CreateNoteJob: actor: {} create: {} inbox: {}", param.actor, create, param.inbox) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/NoteApApiServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/NoteApApiServiceImpl.kt index 4420ceb0..e6dc9ca0 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/NoteApApiServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/NoteApApiServiceImpl.kt @@ -3,7 +3,6 @@ package dev.usbharu.hideout.activitypub.service.objects.note import dev.usbharu.hideout.activitypub.domain.model.Note import dev.usbharu.hideout.activitypub.query.NoteQueryService import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException import dev.usbharu.hideout.core.domain.model.post.Post import dev.usbharu.hideout.core.domain.model.post.Visibility import dev.usbharu.hideout.core.query.FollowerQueryService @@ -17,12 +16,13 @@ class NoteApApiServiceImpl( private val transaction: Transaction ) : NoteApApiService { override suspend fun getNote(postId: Long, userId: Long?): Note? = transaction.transaction { - val findById = try { - noteQueryService.findById(postId) - } catch (e: FailedToGetResourcesException) { - logger.warn("Note not found.", e) + val findById = noteQueryService.findById(postId) + + if (findById == null) { + logger.warn("Note not found. $postId $userId") return@transaction null } + when (findById.second.visibility) { Visibility.PUBLIC, Visibility.UNLISTED -> { return@transaction findById.first diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt index 009c6821..503785c5 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt @@ -1,6 +1,5 @@ package dev.usbharu.hideout.activitypub.service.objects.user -import dev.usbharu.hideout.activitypub.domain.exception.IllegalActivityPubObjectException import dev.usbharu.hideout.activitypub.domain.model.Image import dev.usbharu.hideout.activitypub.domain.model.Key import dev.usbharu.hideout.activitypub.domain.model.Person @@ -8,13 +7,12 @@ import dev.usbharu.hideout.activitypub.service.common.APResourceResolveService import dev.usbharu.hideout.activitypub.service.common.resolve import dev.usbharu.hideout.application.config.ApplicationConfig import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException +import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException import dev.usbharu.hideout.core.domain.model.actor.Actor -import dev.usbharu.hideout.core.query.ActorQueryService +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.service.user.RemoteUserCreateDto import dev.usbharu.hideout.core.service.user.UserService import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional interface APUserService { suspend fun getPersonByName(name: String): Person @@ -34,16 +32,17 @@ interface APUserService { @Service class APUserServiceImpl( private val userService: UserService, - private val actorQueryService: ActorQueryService, private val transaction: Transaction, private val applicationConfig: ApplicationConfig, - private val apResourceResolveService: APResourceResolveService + private val apResourceResolveService: APResourceResolveService, + private val actorRepository: ActorRepository ) : APUserService { override suspend fun getPersonByName(name: String): Person { val userEntity = transaction.transaction { - actorQueryService.findByNameAndDomain(name, applicationConfig.url.host) + actorRepository.findByNameAndDomain(name, applicationConfig.url.host) + ?: throw UserNotFoundException.withNameAndDomain(name, applicationConfig.url.host) } // TODO: JOINで書き直し val userUrl = "${applicationConfig.url}/users/$name" @@ -76,40 +75,40 @@ class APUserServiceImpl( override suspend fun fetchPerson(url: String, targetActor: String?): Person = fetchPersonWithEntity(url, targetActor).first - @Transactional override suspend fun fetchPersonWithEntity(url: String, targetActor: String?): Pair { - return try { - val userEntity = actorQueryService.findByUrl(url) - val id = userEntity.url - return entityToPerson(userEntity, id) to userEntity - } catch (ignore: FailedToGetResourcesException) { - val person = apResourceResolveService.resolve(url, null as Long?) + val userEntity = actorRepository.findByUrl(url) - val id = person.id - try { - val userEntity = actorQueryService.findByUrl(id) - return entityToPerson(userEntity, id) to userEntity - } catch (_: FailedToGetResourcesException) { - } - person to userService.createRemoteUser( - RemoteUserCreateDto( - name = person.preferredUsername - ?: throw IllegalActivityPubObjectException("preferredUsername is null"), - domain = id.substringAfter("://").substringBefore("/"), - screenName = person.name ?: person.preferredUsername, - description = person.summary.orEmpty(), - inbox = person.inbox, - outbox = person.outbox, - url = id, - publicKey = person.publicKey.publicKeyPem, - keyId = person.publicKey.id, - following = person.following, - followers = person.followers, - sharedInbox = person.endpoints["sharedInbox"], - locked = person.manuallyApprovesFollowers - ) - ) + if (userEntity != null) { + return entityToPerson(userEntity, userEntity.url) to userEntity } + + val person = apResourceResolveService.resolve(url, null as Long?) + + val id = person.id + + val actor = actorRepository.findByUrlWithLock(id) + + if (actor != null) { + return person to actor + } + + return person to userService.createRemoteUser( + RemoteUserCreateDto( + name = person.preferredUsername, + domain = id.substringAfter("://").substringBefore("/"), + screenName = person.name ?: person.preferredUsername, + description = person.summary.orEmpty(), + inbox = person.inbox, + outbox = person.outbox, + url = id, + publicKey = person.publicKey.publicKeyPem, + keyId = person.publicKey.id, + following = person.following, + followers = person.followers, + sharedInbox = person.endpoints["sharedInbox"], + locked = person.manuallyApprovesFollowers + ) + ) } private fun entityToPerson( diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/webfinger/WebFingerApiService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/webfinger/WebFingerApiService.kt index 1cdd8230..25b774a0 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/webfinger/WebFingerApiService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/webfinger/WebFingerApiService.kt @@ -1,8 +1,9 @@ package dev.usbharu.hideout.activitypub.service.webfinger import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException import dev.usbharu.hideout.core.domain.model.actor.Actor -import dev.usbharu.hideout.core.query.ActorQueryService +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import org.springframework.stereotype.Service @Service @@ -11,11 +12,17 @@ interface WebFingerApiService { } @Service -class WebFingerApiServiceImpl(private val transaction: Transaction, private val actorQueryService: ActorQueryService) : +class WebFingerApiServiceImpl( + private val transaction: Transaction, + private val actorRepository: ActorRepository +) : WebFingerApiService { override suspend fun findByNameAndDomain(name: String, domain: String): Actor { return transaction.transaction { - actorQueryService.findByNameAndDomain(name, domain) + actorRepository.findByNameAndDomain(name, domain) ?: throw UserNotFoundException.withNameAndDomain( + name, + domain + ) } } } diff --git a/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt b/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt index 144379c9..966be809 100644 --- a/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt +++ b/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt @@ -7,19 +7,18 @@ import com.nimbusds.jose.jwk.source.ImmutableJWKSet import com.nimbusds.jose.jwk.source.JWKSource import com.nimbusds.jose.proc.SecurityContext import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureFilter import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureUserDetailsService import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureVerifierComposite import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.UserDetailsImpl import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.UserDetailsServiceImpl -import dev.usbharu.hideout.core.query.ActorQueryService import dev.usbharu.hideout.util.RsaUtil import dev.usbharu.hideout.util.hasAnyScope import dev.usbharu.httpsignature.sign.RsaSha256HttpSignatureSigner import dev.usbharu.httpsignature.verify.DefaultSignatureHeaderParser import dev.usbharu.httpsignature.verify.RsaSha256HttpSignatureVerifier import jakarta.annotation.PostConstruct -import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer import org.springframework.boot.context.properties.ConfigurationProperties @@ -68,9 +67,6 @@ import java.util.* @Suppress("FunctionMaxLength", "TooManyFunctions") class SecurityConfig { - @Autowired - private lateinit var actorQueryService: ActorQueryService - @Bean fun authenticationManager(authenticationConfiguration: AuthenticationConfiguration): AuthenticationManager? = authenticationConfiguration.authenticationManager @@ -130,12 +126,14 @@ class SecurityConfig { @Bean @Order(1) - fun httpSignatureAuthenticationProvider(transaction: Transaction): PreAuthenticatedAuthenticationProvider { + fun httpSignatureAuthenticationProvider( + transaction: Transaction, + actorRepository: ActorRepository + ): PreAuthenticatedAuthenticationProvider { val provider = PreAuthenticatedAuthenticationProvider() val signatureHeaderParser = DefaultSignatureHeaderParser() provider.setPreAuthenticatedUserDetailsService( HttpSignatureUserDetailsService( - actorQueryService, HttpSignatureVerifierComposite( mapOf( "rsa-sha256" to RsaSha256HttpSignatureVerifier( @@ -145,7 +143,8 @@ class SecurityConfig { signatureHeaderParser ), transaction, - signatureHeaderParser + signatureHeaderParser, + actorRepository ) ) provider.setUserDetailsChecker(AccountStatusUserDetailsChecker()) diff --git a/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedTransaction.kt b/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedTransaction.kt index 74be00ff..05fe6305 100644 --- a/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedTransaction.kt +++ b/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedTransaction.kt @@ -1,25 +1,37 @@ package dev.usbharu.hideout.application.infrastructure.exposed import dev.usbharu.hideout.application.external.Transaction +import kotlinx.coroutines.runBlocking import kotlinx.coroutines.slf4j.MDCContext -import org.jetbrains.exposed.sql.StdOutSqlLogger +import org.jetbrains.exposed.sql.Slf4jSqlDebugLogger import org.jetbrains.exposed.sql.addLogger import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction +import org.jetbrains.exposed.sql.transactions.transaction import org.springframework.stereotype.Service import java.sql.Connection @Service class ExposedTransaction : Transaction { override suspend fun transaction(block: suspend () -> T): T { - return newSuspendedTransaction(MDCContext(), transactionIsolation = Connection.TRANSACTION_SERIALIZABLE) { - addLogger(StdOutSqlLogger) - block() +// return newSuspendedTransaction(MDCContext(), transactionIsolation = java.sql.Connection.TRANSACTION_READ_COMMITTED) { +// warnLongQueriesDuration = 1000 +// addLogger(Slf4jSqlDebugLogger) +// block() +// } + + return transaction(transactionIsolation = Connection.TRANSACTION_READ_COMMITTED) { + debug = true + warnLongQueriesDuration = 1000 + addLogger(Slf4jSqlDebugLogger) + runBlocking(MDCContext()) { + block() + } } } override suspend fun transaction(transactionLevel: Int, block: suspend () -> T): T { return newSuspendedTransaction(MDCContext(), transactionIsolation = transactionLevel) { - addLogger(StdOutSqlLogger) + addLogger(Slf4jSqlDebugLogger) block() } } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/HideoutException.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/HideoutException.kt new file mode 100644 index 00000000..0307e24d --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/HideoutException.kt @@ -0,0 +1,21 @@ +package dev.usbharu.hideout.core.domain.exception + +import java.io.Serial + +open class HideoutException : 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 + ) + + companion object { + @Serial + private const val serialVersionUID: Long = 8506638570017469956L + } +} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/SQLExceptionTranslator.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/SQLExceptionTranslator.kt new file mode 100644 index 00000000..842c4ea8 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/SQLExceptionTranslator.kt @@ -0,0 +1,7 @@ +package dev.usbharu.hideout.core.domain.exception + +import dev.usbharu.hideout.core.domain.exception.resource.ResourceAccessException + +interface SQLExceptionTranslator { + fun translate(message: String, sql: String? = null, exception: Exception): ResourceAccessException +} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/SpringDataAccessExceptionSQLExceptionTranslator.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/SpringDataAccessExceptionSQLExceptionTranslator.kt new file mode 100644 index 00000000..6917f583 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/SpringDataAccessExceptionSQLExceptionTranslator.kt @@ -0,0 +1,19 @@ +package dev.usbharu.hideout.core.domain.exception + +import dev.usbharu.hideout.core.domain.exception.resource.DuplicateException +import dev.usbharu.hideout.core.domain.exception.resource.ResourceAccessException +import org.springframework.dao.DataAccessException +import org.springframework.dao.DuplicateKeyException + +class SpringDataAccessExceptionSQLExceptionTranslator : SQLExceptionTranslator { + override fun translate(message: String, sql: String?, exception: Exception): ResourceAccessException { + if (exception !is DataAccessException) { + throw IllegalArgumentException("exception must be DataAccessException.") + } + + return when (exception) { + is DuplicateKeyException -> DuplicateException(message, exception.rootCause) + else -> ResourceAccessException(message, exception.rootCause) + } + } +} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/resource/DuplicateException.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/resource/DuplicateException.kt new file mode 100644 index 00000000..adb26d2d --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/resource/DuplicateException.kt @@ -0,0 +1,21 @@ +package dev.usbharu.hideout.core.domain.exception.resource + +import java.io.Serial + +class DuplicateException : ResourceAccessException { + 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 + ) + + companion object { + @Serial + private const val serialVersionUID: Long = 7092046653037974417L + } +} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/resource/NotFoundException.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/resource/NotFoundException.kt new file mode 100644 index 00000000..bc2e6938 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/resource/NotFoundException.kt @@ -0,0 +1,14 @@ +package dev.usbharu.hideout.core.domain.exception.resource + +open class NotFoundException : ResourceAccessException { + 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 + ) +} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/resource/PostNotFoundException.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/resource/PostNotFoundException.kt new file mode 100644 index 00000000..cae271aa --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/resource/PostNotFoundException.kt @@ -0,0 +1,27 @@ +package dev.usbharu.hideout.core.domain.exception.resource + +import java.io.Serial + +class PostNotFoundException : NotFoundException { + 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 + ) + + companion object { + @Serial + private const val serialVersionUID: Long = 1315818410686905717L + + fun withApId(apId: String): PostNotFoundException = PostNotFoundException("apId: $apId was not found.") + + fun withId(id: Long): PostNotFoundException = PostNotFoundException("id: $id was not found.") + + fun withUrl(url: String): PostNotFoundException = PostNotFoundException("url: $url was not found.") + } +} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/resource/ResourceAccessException.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/resource/ResourceAccessException.kt new file mode 100644 index 00000000..b61fd4f4 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/resource/ResourceAccessException.kt @@ -0,0 +1,16 @@ +package dev.usbharu.hideout.core.domain.exception.resource + +import dev.usbharu.hideout.core.domain.exception.HideoutException + +open class ResourceAccessException : HideoutException { + 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 + ) +} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/resource/UserNotFoundException.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/resource/UserNotFoundException.kt new file mode 100644 index 00000000..30e2f133 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/resource/UserNotFoundException.kt @@ -0,0 +1,36 @@ +package dev.usbharu.hideout.core.domain.exception.resource + +import java.io.Serial + +open class UserNotFoundException : NotFoundException { + 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 + ) + + companion object { + @Serial + private const val serialVersionUID: Long = 3219433672235626200L + + fun withName(string: String, throwable: Throwable? = null): UserNotFoundException = + UserNotFoundException("name: $string was not found.", throwable) + + fun withId(id: Long, throwable: Throwable? = null): UserNotFoundException = + UserNotFoundException("id: $id was not found.", throwable) + + fun withUrl(url: String, throwable: Throwable? = null): UserNotFoundException = + UserNotFoundException("url: $url was not found.", throwable) + + fun withNameAndDomain(name: String, domain: String, throwable: Throwable? = null): UserNotFoundException = + UserNotFoundException("name: $name domain: $domain (@$name@$domain) was not found.", throwable) + + fun withKeyId(keyId: String, throwable: Throwable? = null) = + UserNotFoundException("keyId: $keyId was not found.", throwable) + } +} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/resource/local/LocalUserNotFoundException.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/resource/local/LocalUserNotFoundException.kt new file mode 100644 index 00000000..672dfc53 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/resource/local/LocalUserNotFoundException.kt @@ -0,0 +1,31 @@ +package dev.usbharu.hideout.core.domain.exception.resource.local + +import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException +import java.io.Serial + +class LocalUserNotFoundException : UserNotFoundException { + 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 + ) + + companion object { + @Serial + private const val serialVersionUID: Long = -4742548128672528145L + + fun withName(string: String, throwable: Throwable? = null): LocalUserNotFoundException = + LocalUserNotFoundException("name: $string was not found.", throwable) + + fun withId(id: Long, throwable: Throwable? = null): LocalUserNotFoundException = + LocalUserNotFoundException("id: $id was not found.", throwable) + + fun withUrl(url: String, throwable: Throwable? = null): LocalUserNotFoundException = + LocalUserNotFoundException("url: $url was not found.", throwable) + } +} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor.kt index 6689e542..0377df95 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor.kt @@ -57,6 +57,31 @@ data class Actor private constructor( postsCount: Int = 0, lastPostDate: Instant? = null ): Actor { + if (id == 0L) { + return Actor( + id = id, + name = name, + domain = domain, + screenName = screenName, + description = description, + inbox = inbox, + outbox = outbox, + url = url, + publicKey = publicKey, + privateKey = privateKey, + createdAt = createdAt, + keyId = keyId, + followers = followers, + following = following, + instance = instance, + locked = locked, + followersCount = followersCount, + followingCount = followingCount, + postsCount = postsCount, + lastPostDate = lastPostDate + ) + } + // idは0未満ではいけない require(id >= 0) { "id must be greater than or equal to 0." } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorRepository.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorRepository.kt index 39887a5e..5f87a817 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorRepository.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorRepository.kt @@ -3,11 +3,30 @@ package dev.usbharu.hideout.core.domain.model.actor import org.springframework.stereotype.Repository @Repository +@Suppress("TooManyFunctions") interface ActorRepository { suspend fun save(actor: Actor): Actor suspend fun findById(id: Long): Actor? + suspend fun findByIdWithLock(id: Long): Actor? + + suspend fun findAll(limit: Int, offset: Long): List + + suspend fun findByName(name: String): List + + suspend fun findByNameAndDomain(name: String, domain: String): Actor? + + suspend fun findByNameAndDomainWithLock(name: String, domain: String): Actor? + + suspend fun findByUrl(url: String): Actor? + + suspend fun findByUrlWithLock(url: String): Actor? + + suspend fun findByIds(ids: List): List + + suspend fun findByKeyId(keyId: String): Actor? + suspend fun delete(id: Long) suspend fun nextId(): Long diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/deletedActor/DeletedActorRepository.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/deletedActor/DeletedActorRepository.kt index f2d18368..0a9bb6c2 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/deletedActor/DeletedActorRepository.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/deletedActor/DeletedActorRepository.kt @@ -3,5 +3,6 @@ package dev.usbharu.hideout.core.domain.model.deletedActor interface DeletedActorRepository { suspend fun save(deletedActor: DeletedActor): DeletedActor suspend fun delete(deletedActor: DeletedActor) - suspend fun findById(id: Long): DeletedActor + suspend fun findById(id: Long): DeletedActor? + suspend fun findByNameAndDomain(name: String, domain: String): DeletedActor? } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceRepository.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceRepository.kt index 35b6026e..c7676096 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceRepository.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceRepository.kt @@ -3,6 +3,7 @@ package dev.usbharu.hideout.core.domain.model.instance interface InstanceRepository { suspend fun generateId(): Long suspend fun save(instance: Instance): Instance - suspend fun findById(id: Long): Instance + suspend fun findById(id: Long): Instance? suspend fun delete(instance: Instance) + suspend fun findByUrl(url: String): Instance? } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/MediaRepository.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/MediaRepository.kt index 1e18f68b..7f1bd6cf 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/MediaRepository.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/MediaRepository.kt @@ -3,6 +3,7 @@ package dev.usbharu.hideout.core.domain.model.media interface MediaRepository { suspend fun generateId(): Long suspend fun save(media: Media): Media - suspend fun findById(id: Long): Media + suspend fun findById(id: Long): Media? suspend fun delete(id: Long) + suspend fun findByRemoteUrl(remoteUrl: String): Media? } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostRepository.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostRepository.kt index f2feb6f0..004be864 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostRepository.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostRepository.kt @@ -6,7 +6,12 @@ import org.springframework.stereotype.Repository @Repository interface PostRepository { suspend fun generateId(): Long - suspend fun save(post: Post): Boolean + suspend fun save(post: Post): Post suspend fun delete(id: Long) - suspend fun findById(id: Long): Post + suspend fun findById(id: Long): Post? + suspend fun findByUrl(url: String): Post? + + suspend fun findByApId(apId: String): Post? + suspend fun existByApIdWithLock(apId: String): Boolean + suspend fun findByActorId(actorId: Long): List } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/ReactionRepository.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/ReactionRepository.kt index 64b324ff..0b6183b5 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/ReactionRepository.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/ReactionRepository.kt @@ -3,10 +3,15 @@ package dev.usbharu.hideout.core.domain.model.reaction import org.springframework.stereotype.Repository @Repository +@Suppress("FunctionMaxLength") interface ReactionRepository { suspend fun generateId(): Long suspend fun save(reaction: Reaction): Reaction suspend fun delete(reaction: Reaction): Reaction suspend fun deleteByPostId(postId: Long): Int suspend fun deleteByActorId(actorId: Long): Int + suspend fun findByPostId(postId: Long): List + suspend fun findByPostIdAndActorIdAndEmojiId(postId: Long, actorId: Long, emojiId: Long): Reaction? + suspend fun existByPostIdAndActorIdAndEmojiId(postId: Long, actorId: Long, emojiId: Long): Boolean + suspend fun findByPostIdAndActorId(postId: Long, actorId: Long): List } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepository.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepository.kt index 6335a878..ac96db6b 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepository.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepository.kt @@ -30,4 +30,16 @@ interface RelationshipRepository { suspend fun findByUserIdAndTargetUserId(actorId: Long, targetActorId: Long): Relationship? suspend fun deleteByActorIdOrTargetActorId(actorId: Long, targetActorId: Long) + + suspend fun findByTargetIdAndFollowing(targetId: Long, following: Boolean): List + + @Suppress("LongParameterList", "FunctionMaxLength") + suspend fun findByTargetIdAndFollowRequestAndIgnoreFollowRequest( + maxId: Long?, + sinceId: Long?, + limit: Int, + targetId: Long, + followRequest: Boolean, + ignoreFollowRequest: Boolean + ): List } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepositoryImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepositoryImpl.kt index 57610d1d..a8085686 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepositoryImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepositoryImpl.kt @@ -1,21 +1,22 @@ package dev.usbharu.hideout.core.domain.model.relationship +import dev.usbharu.hideout.core.infrastructure.exposedrepository.AbstractRepository import dev.usbharu.hideout.core.infrastructure.exposedrepository.Actors import org.jetbrains.exposed.dao.id.LongIdTable import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq +import org.slf4j.Logger +import org.slf4j.LoggerFactory import org.springframework.stereotype.Service @Service -class RelationshipRepositoryImpl : RelationshipRepository { - override suspend fun save(relationship: Relationship): Relationship { - val singleOrNull = - Relationships - .select { - (Relationships.actorId eq relationship.actorId) - .and(Relationships.targetActorId eq relationship.targetActorId) - } - .singleOrNull() +class RelationshipRepositoryImpl : RelationshipRepository, AbstractRepository() { + override suspend fun save(relationship: Relationship): Relationship = query { + val singleOrNull = Relationships.select { + (Relationships.actorId eq relationship.actorId).and( + Relationships.targetActorId eq relationship.targetActorId + ) + }.forUpdate().singleOrNull() if (singleOrNull == null) { Relationships.insert { @@ -28,41 +29,77 @@ class RelationshipRepositoryImpl : RelationshipRepository { it[ignoreFollowRequestFromTarget] = relationship.ignoreFollowRequestToTarget } } else { - Relationships - .update({ - (Relationships.actorId eq relationship.actorId) - .and(Relationships.targetActorId eq relationship.targetActorId) - }) { - it[following] = relationship.following - it[blocking] = relationship.blocking - it[muting] = relationship.muting - it[followRequest] = relationship.followRequest - it[ignoreFollowRequestFromTarget] = relationship.ignoreFollowRequestToTarget - } + Relationships.update({ + (Relationships.actorId eq relationship.actorId).and( + Relationships.targetActorId eq relationship.targetActorId + ) + }) { + it[following] = relationship.following + it[blocking] = relationship.blocking + it[muting] = relationship.muting + it[followRequest] = relationship.followRequest + it[ignoreFollowRequestFromTarget] = relationship.ignoreFollowRequestToTarget + } } - return relationship + return@query relationship } - override suspend fun delete(relationship: Relationship) { + override suspend fun delete(relationship: Relationship): Unit = query { Relationships.deleteWhere { - (Relationships.actorId eq relationship.actorId) - .and(Relationships.targetActorId eq relationship.targetActorId) + (Relationships.actorId eq relationship.actorId).and( + Relationships.targetActorId eq relationship.targetActorId + ) } } - override suspend fun findByUserIdAndTargetUserId(actorId: Long, targetActorId: Long): Relationship? { - return Relationships.select { - (Relationships.actorId eq actorId) - .and(Relationships.targetActorId eq targetActorId) - }.singleOrNull() - ?.toRelationships() + override suspend fun findByUserIdAndTargetUserId(actorId: Long, targetActorId: Long): Relationship? = query { + return@query Relationships.select { + (Relationships.actorId eq actorId).and(Relationships.targetActorId eq targetActorId) + }.singleOrNull()?.toRelationships() } - override suspend fun deleteByActorIdOrTargetActorId(actorId: Long, targetActorId: Long) { + override suspend fun deleteByActorIdOrTargetActorId(actorId: Long, targetActorId: Long): Unit = query { Relationships.deleteWhere { Relationships.actorId.eq(actorId).or(Relationships.targetActorId.eq(targetActorId)) } } + + override suspend fun findByTargetIdAndFollowing(targetId: Long, following: Boolean): List = query { + return@query Relationships + .select { Relationships.targetActorId eq targetId and (Relationships.following eq following) } + .map { it.toRelationships() } + } + + override suspend fun findByTargetIdAndFollowRequestAndIgnoreFollowRequest( + maxId: Long?, + sinceId: Long?, + limit: Int, + targetId: Long, + followRequest: Boolean, + ignoreFollowRequest: Boolean + ): List = query { + val query = Relationships.select { + Relationships.targetActorId.eq(targetId).and(Relationships.followRequest.eq(followRequest)) + .and(Relationships.ignoreFollowRequestFromTarget.eq(ignoreFollowRequest)) + }.limit(limit) + + if (maxId != null) { + query.andWhere { Relationships.id lessEq maxId } + } + + if (sinceId != null) { + query.andWhere { Relationships.id greaterEq sinceId } + } + + return@query query.map { it.toRelationships() } + } + + override val logger: Logger + get() = Companion.logger + + companion object { + private val logger = LoggerFactory.getLogger(RelationshipRepositoryImpl::class.java) + } } fun ResultRow.toRelationships(): Relationship = Relationship( diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/ActorQueryServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/ActorQueryServiceImpl.kt deleted file mode 100644 index 6dab78c2..00000000 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/ActorQueryServiceImpl.kt +++ /dev/null @@ -1,60 +0,0 @@ -package dev.usbharu.hideout.core.infrastructure.exposedquery - -import dev.usbharu.hideout.application.infrastructure.exposed.QueryMapper -import dev.usbharu.hideout.application.infrastructure.exposed.ResultRowMapper -import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException -import dev.usbharu.hideout.core.domain.model.actor.Actor -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Actors -import dev.usbharu.hideout.core.query.ActorQueryService -import dev.usbharu.hideout.util.singleOr -import org.jetbrains.exposed.sql.and -import org.jetbrains.exposed.sql.select -import org.jetbrains.exposed.sql.selectAll -import org.slf4j.LoggerFactory -import org.springframework.stereotype.Repository - -@Repository -class ActorQueryServiceImpl( - private val actorResultRowMapper: ResultRowMapper, - private val actorQueryMapper: QueryMapper -) : ActorQueryService { - - private val logger = LoggerFactory.getLogger(ActorQueryServiceImpl::class.java) - - override suspend fun findAll(limit: Int, offset: Long): List = - Actors.selectAll().limit(limit, offset).let(actorQueryMapper::map) - - override suspend fun findById(id: Long): Actor = Actors.select { Actors.id eq id } - .singleOr { FailedToGetResourcesException("id: $id is duplicate or does not exist.", it) } - .let(actorResultRowMapper::map) - - override suspend fun findByName(name: String): List = - Actors.select { Actors.name eq name }.let(actorQueryMapper::map) - - override suspend fun findByNameAndDomain(name: String, domain: String): Actor = - Actors - .select { Actors.name eq name and (Actors.domain eq domain) } - .singleOr { - FailedToGetResourcesException("name: $name,domain: $domain is duplicate or does not exist.", it) - } - .let(actorResultRowMapper::map) - - override suspend fun findByUrl(url: String): Actor { - logger.trace("findByUrl url: $url") - return Actors.select { Actors.url eq url } - .singleOr { FailedToGetResourcesException("url: $url is duplicate or does not exist.", it) } - .let(actorResultRowMapper::map) - } - - override suspend fun findByIds(ids: List): List = - Actors.select { Actors.id inList ids }.let(actorQueryMapper::map) - - override suspend fun existByNameAndDomain(name: String, domain: String): Boolean = - Actors.select { Actors.name eq name and (Actors.domain eq domain) }.empty().not() - - override suspend fun findByKeyId(keyId: String): Actor { - return Actors.select { Actors.keyId eq keyId } - .singleOr { FailedToGetResourcesException("keyId: $keyId is duplicate or does not exist.", it) } - .let(actorResultRowMapper::map) - } -} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/DeletedActorQueryServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/DeletedActorQueryServiceImpl.kt deleted file mode 100644 index f412b310..00000000 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/DeletedActorQueryServiceImpl.kt +++ /dev/null @@ -1,23 +0,0 @@ -package dev.usbharu.hideout.core.infrastructure.exposedquery - -import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException -import dev.usbharu.hideout.core.domain.model.deletedActor.DeletedActor -import dev.usbharu.hideout.core.infrastructure.exposedrepository.DeletedActors -import dev.usbharu.hideout.core.infrastructure.exposedrepository.toDeletedActor -import dev.usbharu.hideout.core.query.DeletedActorQueryService -import dev.usbharu.hideout.util.singleOr -import org.jetbrains.exposed.sql.and -import org.jetbrains.exposed.sql.select -import org.springframework.stereotype.Repository - -@Repository -class DeletedActorQueryServiceImpl : DeletedActorQueryService { - override suspend fun findByNameAndDomain(name: String, domain: String): DeletedActor { - return DeletedActors - .select { DeletedActors.name eq name and (DeletedActors.domain eq domain) } - .singleOr { - FailedToGetResourcesException("name: $name domain: $domain was not exist or duplicate.", it) - } - .toDeletedActor() - } -} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/FollowerQueryServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/FollowerQueryServiceImpl.kt index f7161b2e..fde53cd3 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/FollowerQueryServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/FollowerQueryServiceImpl.kt @@ -1,21 +1,19 @@ package dev.usbharu.hideout.core.infrastructure.exposedquery import dev.usbharu.hideout.core.domain.model.actor.Actor +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository -import dev.usbharu.hideout.core.query.ActorQueryService import dev.usbharu.hideout.core.query.FollowerQueryService -import dev.usbharu.hideout.core.query.RelationshipQueryService import org.springframework.stereotype.Repository @Repository class FollowerQueryServiceImpl( - private val relationshipQueryService: RelationshipQueryService, - private val actorQueryService: ActorQueryService, - private val relationshipRepository: RelationshipRepository + private val relationshipRepository: RelationshipRepository, + private val actorRepository: ActorRepository ) : FollowerQueryService { override suspend fun findFollowersById(id: Long): List { - return actorQueryService.findByIds( - relationshipQueryService.findByTargetIdAndFollowing(id, true).map { it.actorId } + return actorRepository.findByIds( + relationshipRepository.findByTargetIdAndFollowing(id, true).map { it.actorId } ) } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/InstanceQueryServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/InstanceQueryServiceImpl.kt deleted file mode 100644 index c7d276ef..00000000 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/InstanceQueryServiceImpl.kt +++ /dev/null @@ -1,16 +0,0 @@ -package dev.usbharu.hideout.core.infrastructure.exposedquery - -import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Instance -import dev.usbharu.hideout.core.infrastructure.exposedrepository.toInstance -import dev.usbharu.hideout.core.query.InstanceQueryService -import dev.usbharu.hideout.util.singleOr -import org.jetbrains.exposed.sql.select -import org.springframework.stereotype.Repository -import dev.usbharu.hideout.core.domain.model.instance.Instance as InstanceEntity - -@Repository -class InstanceQueryServiceImpl : InstanceQueryService { - override suspend fun findByUrl(url: String): InstanceEntity = Instance.select { Instance.url eq url } - .singleOr { FailedToGetResourcesException("$url is doesn't exist", it) }.toInstance() -} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/MediaQueryServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/MediaQueryServiceImpl.kt deleted file mode 100644 index 7b5a5d8e..00000000 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/MediaQueryServiceImpl.kt +++ /dev/null @@ -1,31 +0,0 @@ -package dev.usbharu.hideout.core.infrastructure.exposedquery - -import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Media -import dev.usbharu.hideout.core.infrastructure.exposedrepository.PostsMedia -import dev.usbharu.hideout.core.infrastructure.exposedrepository.toMedia -import dev.usbharu.hideout.core.query.MediaQueryService -import dev.usbharu.hideout.util.singleOr -import org.jetbrains.exposed.sql.innerJoin -import org.jetbrains.exposed.sql.select -import org.springframework.stereotype.Repository -import dev.usbharu.hideout.core.domain.model.media.Media as MediaEntity - -@Repository -class MediaQueryServiceImpl : MediaQueryService { - override suspend fun findByPostId(postId: Long): List { - return Media.innerJoin( - PostsMedia, - onColumn = { id }, - otherColumn = { mediaId } - ) - .select { PostsMedia.postId eq postId } - .map { it.toMedia() } - } - - override suspend fun findByRemoteUrl(remoteUrl: String): MediaEntity { - return Media.select { Media.remoteUrl eq remoteUrl } - .singleOr { FailedToGetResourcesException("remoteUrl: $remoteUrl is duplicate or not exist.", it) } - .toMedia() - } -} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/PostQueryServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/PostQueryServiceImpl.kt deleted file mode 100644 index 65d8d492..00000000 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/PostQueryServiceImpl.kt +++ /dev/null @@ -1,39 +0,0 @@ -package dev.usbharu.hideout.core.infrastructure.exposedquery - -import dev.usbharu.hideout.application.infrastructure.exposed.QueryMapper -import dev.usbharu.hideout.application.infrastructure.exposed.ResultRowMapper -import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException -import dev.usbharu.hideout.core.domain.model.post.Post -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts -import dev.usbharu.hideout.core.infrastructure.exposedrepository.PostsMedia -import dev.usbharu.hideout.core.query.PostQueryService -import dev.usbharu.hideout.util.singleOr -import org.jetbrains.exposed.sql.select -import org.springframework.stereotype.Repository - -@Repository -class PostQueryServiceImpl( - private val postResultRowMapper: ResultRowMapper, - private val postQueryMapper: QueryMapper -) : PostQueryService { - override suspend fun findById(id: Long): Post = - Posts.leftJoin(PostsMedia) - .select { Posts.id eq id } - .singleOr { FailedToGetResourcesException("id: $id is duplicate or does not exist.", it) } - .let(postResultRowMapper::map) - - override suspend fun findByUrl(url: String): Post = - Posts.leftJoin(PostsMedia) - .select { Posts.url eq url } - .let(postQueryMapper::map) - .singleOr { FailedToGetResourcesException("url: $url is duplicate or does not exist.", it) } - - override suspend fun findByApId(string: String): Post = - Posts.leftJoin(PostsMedia) - .select { Posts.apId eq string } - .let(postQueryMapper::map) - .singleOr { FailedToGetResourcesException("apId: $string is duplicate or does not exist.", it) } - - override suspend fun findByActorId(actorId: Long): List = - Posts.leftJoin(PostsMedia).select { Posts.actorId eq actorId }.let(postQueryMapper::map) -} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/ReactionQueryServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/ReactionQueryServiceImpl.kt deleted file mode 100644 index 8e51e2e6..00000000 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/ReactionQueryServiceImpl.kt +++ /dev/null @@ -1,50 +0,0 @@ -package dev.usbharu.hideout.core.infrastructure.exposedquery - -import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException -import dev.usbharu.hideout.core.domain.model.reaction.Reaction -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Reactions -import dev.usbharu.hideout.core.infrastructure.exposedrepository.toReaction -import dev.usbharu.hideout.core.query.ReactionQueryService -import dev.usbharu.hideout.util.singleOr -import org.jetbrains.exposed.sql.and -import org.jetbrains.exposed.sql.select -import org.springframework.stereotype.Repository - -@Repository -class ReactionQueryServiceImpl : ReactionQueryService { - override suspend fun findByPostId(postId: Long, actorId: Long?): List { - return Reactions.select { - Reactions.postId.eq(postId) - }.map { it.toReaction() } - } - - @Suppress("FunctionMaxLength") - override suspend fun findByPostIdAndActorIdAndEmojiId(postId: Long, actorId: Long, emojiId: Long): Reaction { - return Reactions - .select { - Reactions.postId.eq(postId).and(Reactions.actorId.eq(actorId)).and( - Reactions.emojiId.eq(emojiId) - ) - } - .singleOr { - FailedToGetResourcesException( - "postId: $postId,userId: $actorId,emojiId: $emojiId is duplicate or does not exist.", - it - ) - } - .toReaction() - } - - override suspend fun reactionAlreadyExist(postId: Long, actorId: Long, emojiId: Long): Boolean { - return Reactions.select { - Reactions.postId.eq(postId).and(Reactions.actorId.eq(actorId)).and( - Reactions.emojiId.eq(emojiId) - ) - }.empty().not() - } - - override suspend fun findByPostIdAndActorId(postId: Long, actorId: Long): List { - return Reactions.select { Reactions.postId eq postId and (Reactions.actorId eq actorId) } - .map { it.toReaction() } - } -} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/RelationshipQueryServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/RelationshipQueryServiceImpl.kt deleted file mode 100644 index 43d8206a..00000000 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/RelationshipQueryServiceImpl.kt +++ /dev/null @@ -1,43 +0,0 @@ -package dev.usbharu.hideout.core.infrastructure.exposedquery - -import dev.usbharu.hideout.core.domain.model.relationship.Relationship -import dev.usbharu.hideout.core.domain.model.relationship.Relationships -import dev.usbharu.hideout.core.domain.model.relationship.toRelationships -import dev.usbharu.hideout.core.query.RelationshipQueryService -import org.jetbrains.exposed.sql.and -import org.jetbrains.exposed.sql.andWhere -import org.jetbrains.exposed.sql.select -import org.springframework.stereotype.Service - -@Service -class RelationshipQueryServiceImpl : RelationshipQueryService { - override suspend fun findByTargetIdAndFollowing(targetId: Long, following: Boolean): List = - Relationships.select { Relationships.targetActorId eq targetId and (Relationships.following eq following) } - .map { it.toRelationships() } - - override suspend fun findByTargetIdAndFollowRequestAndIgnoreFollowRequest( - maxId: Long?, - sinceId: Long?, - limit: Int, - targetId: Long, - followRequest: Boolean, - ignoreFollowRequest: Boolean - ): List { - val query = Relationships - .select { - Relationships.targetActorId.eq(targetId) - .and(Relationships.followRequest.eq(followRequest)) - .and(Relationships.ignoreFollowRequestFromTarget.eq(ignoreFollowRequest)) - }.limit(limit) - - if (maxId != null) { - query.andWhere { Relationships.id greater maxId } - } - - if (sinceId != null) { - query.andWhere { Relationships.id less sinceId } - } - - return query.map { it.toRelationships() } - } -} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/AbstractRepository.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/AbstractRepository.kt new file mode 100644 index 00000000..3e22db17 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/AbstractRepository.kt @@ -0,0 +1,63 @@ +package dev.usbharu.hideout.core.infrastructure.exposedrepository + +import dev.usbharu.hideout.core.domain.exception.SpringDataAccessExceptionSQLExceptionTranslator +import org.slf4j.Logger +import org.springframework.beans.factory.annotation.Value +import org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator +import java.sql.SQLException + +@Suppress("VarCouldBeVal") +abstract class AbstractRepository { + protected abstract val logger: Logger + private val sqlErrorCodeSQLExceptionTranslator = SQLErrorCodeSQLExceptionTranslator() + private val springDataAccessExceptionSQLExceptionTranslator = SpringDataAccessExceptionSQLExceptionTranslator() + + @Value("\${hideout.debug.trace-query-exception:false}") + private var traceQueryException: Boolean = false + + @Value("\${hideout.debug.trace-query-call:false}") + private var traceQueryCall: Boolean = false + + protected suspend fun query(block: () -> T): T = try { + if (traceQueryCall) { + @Suppress("ThrowingExceptionsWithoutMessageOrCause") + logger.trace( + """ +***** QUERY CALL STACK TRACE ***** + +${Throwable().stackTrace.joinToString("\n")} + +***** QUERY CALL STACK TRACE ***** +""" + ) + } + + block.invoke() + } catch (e: SQLException) { + if (traceQueryException) { + logger.trace("FAILED EXECUTE SQL", e) + } + if (e.cause !is SQLException) { + throw e + } + + val dataAccessException = + sqlErrorCodeSQLExceptionTranslator.translate("Failed to persist entity", null, e.cause as SQLException) + ?: throw e + + if (traceQueryException) { + logger.trace("EXCEPTION TRANSLATED TO", dataAccessException) + } + + val translate = springDataAccessExceptionSQLExceptionTranslator.translate( + "Failed to persist entity", + null, + dataAccessException + ) + + if (traceQueryException) { + logger.trace("EXCEPTION TRANSLATED TO", translate) + } + throw translate + } +} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ActorRepositoryImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ActorRepositoryImpl.kt index 96ce6409..f4a70693 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ActorRepositoryImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ActorRepositoryImpl.kt @@ -1,5 +1,6 @@ package dev.usbharu.hideout.core.infrastructure.exposedrepository +import dev.usbharu.hideout.application.infrastructure.exposed.QueryMapper import dev.usbharu.hideout.application.infrastructure.exposed.ResultRowMapper import dev.usbharu.hideout.application.service.id.IdGenerateService import dev.usbharu.hideout.core.domain.model.actor.Actor @@ -7,17 +8,21 @@ import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.jetbrains.exposed.sql.javatime.timestamp +import org.slf4j.Logger +import org.slf4j.LoggerFactory import org.springframework.stereotype.Repository @Repository class ActorRepositoryImpl( private val idGenerateService: IdGenerateService, - private val actorResultRowMapper: ResultRowMapper -) : - ActorRepository { + private val actorResultRowMapper: ResultRowMapper, + private val actorQueryMapper: QueryMapper +) : ActorRepository, AbstractRepository() { + override val logger: Logger + get() = Companion.logger - override suspend fun save(actor: Actor): Actor { - val singleOrNull = Actors.select { Actors.id eq actor.id }.empty() + override suspend fun save(actor: Actor): Actor = query { + val singleOrNull = Actors.select { Actors.id eq actor.id }.forUpdate().empty() if (singleOrNull) { Actors.insert { it[id] = actor.id @@ -64,17 +69,60 @@ class ActorRepositoryImpl( it[lastPostAt] = actor.lastPostDate } } - return actor + return@query actor } - override suspend fun findById(id: Long): Actor? = - Actors.select { Actors.id eq id }.singleOrNull()?.let(actorResultRowMapper::map) + override suspend fun findById(id: Long): Actor? = query { + return@query Actors.select { Actors.id eq id }.singleOrNull()?.let(actorResultRowMapper::map) + } - override suspend fun delete(id: Long) { + override suspend fun findByIdWithLock(id: Long): Actor? = query { + return@query Actors.select { Actors.id eq id }.forUpdate().singleOrNull()?.let(actorResultRowMapper::map) + } + + override suspend fun findAll(limit: Int, offset: Long): List = query { + return@query Actors.selectAll().limit(limit, offset).let(actorQueryMapper::map) + } + + override suspend fun findByName(name: String): List = query { + return@query Actors.select { Actors.name eq name }.let(actorQueryMapper::map) + } + + override suspend fun findByNameAndDomain(name: String, domain: String): Actor? = query { + return@query Actors.select { Actors.name eq name and (Actors.domain eq domain) }.singleOrNull() + ?.let(actorResultRowMapper::map) + } + + override suspend fun findByNameAndDomainWithLock(name: String, domain: String): Actor? = query { + return@query Actors.select { Actors.name eq name and (Actors.domain eq domain) }.forUpdate().singleOrNull() + ?.let(actorResultRowMapper::map) + } + + override suspend fun findByUrl(url: String): Actor? = query { + return@query Actors.select { Actors.url eq url }.singleOrNull()?.let(actorResultRowMapper::map) + } + + override suspend fun findByUrlWithLock(url: String): Actor? = query { + return@query Actors.select { Actors.url eq url }.forUpdate().singleOrNull()?.let(actorResultRowMapper::map) + } + + override suspend fun findByIds(ids: List): List = query { + return@query Actors.select { Actors.id inList ids }.let(actorQueryMapper::map) + } + + override suspend fun findByKeyId(keyId: String): Actor? = query { + return@query Actors.select { Actors.keyId eq keyId }.singleOrNull()?.let(actorResultRowMapper::map) + } + + override suspend fun delete(id: Long): Unit = query { Actors.deleteWhere { Actors.id.eq(id) } } override suspend fun nextId(): Long = idGenerateService.generateId() + + companion object { + private val logger = LoggerFactory.getLogger(ActorRepositoryImpl::class.java) + } } object Actors : Table("actors") { diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/DeletedActorRepositoryImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/DeletedActorRepositoryImpl.kt index 440c1a36..36b22b80 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/DeletedActorRepositoryImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/DeletedActorRepositoryImpl.kt @@ -1,18 +1,21 @@ package dev.usbharu.hideout.core.infrastructure.exposedrepository -import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException import dev.usbharu.hideout.core.domain.model.deletedActor.DeletedActor import dev.usbharu.hideout.core.domain.model.deletedActor.DeletedActorRepository -import dev.usbharu.hideout.util.singleOr import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.jetbrains.exposed.sql.javatime.timestamp +import org.slf4j.Logger +import org.slf4j.LoggerFactory import org.springframework.stereotype.Repository @Repository -class DeletedActorRepositoryImpl : DeletedActorRepository { - override suspend fun save(deletedActor: DeletedActor): DeletedActor { - val singleOrNull = DeletedActors.select { DeletedActors.id eq deletedActor.id }.singleOrNull() +class DeletedActorRepositoryImpl : DeletedActorRepository, AbstractRepository() { + override val logger: Logger + get() = Companion.logger + + override suspend fun save(deletedActor: DeletedActor): DeletedActor = query { + val singleOrNull = DeletedActors.select { DeletedActors.id eq deletedActor.id }.forUpdate().singleOrNull() if (singleOrNull == null) { DeletedActors.insert { @@ -30,18 +33,29 @@ class DeletedActorRepositoryImpl : DeletedActorRepository { it[DeletedActors.deletedAt] = deletedActor.deletedAt } } - return deletedActor + return@query deletedActor } - override suspend fun delete(deletedActor: DeletedActor) { + override suspend fun delete(deletedActor: DeletedActor): Unit = query { DeletedActors.deleteWhere { DeletedActors.id eq deletedActor.id } } - override suspend fun findById(id: Long): DeletedActor { - val singleOr = DeletedActors.select { DeletedActors.id eq id } - .singleOr { FailedToGetResourcesException("id: $id was not exist or duplicate", it) } + override suspend fun findById(id: Long): DeletedActor? = query { + return@query DeletedActors + .select { DeletedActors.id eq id } + .singleOrNull() + ?.toDeletedActor() + } - return deletedActor(singleOr) + override suspend fun findByNameAndDomain(name: String, domain: String): DeletedActor? = query { + return@query DeletedActors + .select { DeletedActors.name eq name and (DeletedActors.domain eq domain) } + .singleOrNull() + ?.toDeletedActor() + } + + companion object { + private val logger = LoggerFactory.getLogger(DeletedActorRepositoryImpl::class.java) } } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedTimelineRepository.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedTimelineRepository.kt index f8b36460..2630f733 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedTimelineRepository.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedTimelineRepository.kt @@ -5,6 +5,8 @@ import dev.usbharu.hideout.core.domain.model.post.Visibility import dev.usbharu.hideout.core.domain.model.timeline.Timeline import dev.usbharu.hideout.core.domain.model.timeline.TimelineRepository import org.jetbrains.exposed.sql.* +import org.slf4j.Logger +import org.slf4j.LoggerFactory import org.springframework.beans.factory.annotation.Qualifier import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty import org.springframework.stereotype.Repository @@ -12,11 +14,15 @@ import org.springframework.stereotype.Repository @Repository @Qualifier("jdbc") @ConditionalOnProperty("hideout.use-mongodb", havingValue = "false", matchIfMissing = true) -class ExposedTimelineRepository(private val idGenerateService: IdGenerateService) : TimelineRepository { +class ExposedTimelineRepository(private val idGenerateService: IdGenerateService) : TimelineRepository, + AbstractRepository() { + override val logger: Logger + get() = Companion.logger + override suspend fun generateId(): Long = idGenerateService.generateId() - override suspend fun save(timeline: Timeline): Timeline { - if (Timelines.select { Timelines.id eq timeline.id }.singleOrNull() == null) { + override suspend fun save(timeline: Timeline): Timeline = query { + if (Timelines.select { Timelines.id eq timeline.id }.forUpdate().singleOrNull() == null) { Timelines.insert { it[id] = timeline.id it[userId] = timeline.userId @@ -48,10 +54,10 @@ class ExposedTimelineRepository(private val idGenerateService: IdGenerateService it[mediaIds] = timeline.mediaIds.joinToString(",") } } - return timeline + return@query timeline } - override suspend fun saveAll(timelines: List): List { + override suspend fun saveAll(timelines: List): List = query { Timelines.batchInsert(timelines, true, false) { this[Timelines.id] = it.id this[Timelines.userId] = it.userId @@ -67,15 +73,21 @@ class ExposedTimelineRepository(private val idGenerateService: IdGenerateService this[Timelines.isPureRepost] = it.isPureRepost this[Timelines.mediaIds] = it.mediaIds.joinToString(",") } - return timelines + return@query timelines } - override suspend fun findByUserId(id: Long): List = - Timelines.select { Timelines.userId eq id }.map { it.toTimeline() } + override suspend fun findByUserId(id: Long): List = query { + return@query Timelines.select { Timelines.userId eq id }.map { it.toTimeline() } + } - override suspend fun findByUserIdAndTimelineId(userId: Long, timelineId: Long): List = - Timelines.select { Timelines.userId eq userId and (Timelines.timelineId eq timelineId) } + override suspend fun findByUserIdAndTimelineId(userId: Long, timelineId: Long): List = query { + return@query Timelines.select { Timelines.userId eq userId and (Timelines.timelineId eq timelineId) } .map { it.toTimeline() } + } + + companion object { + private val logger = LoggerFactory.getLogger(ExposedTimelineRepository::class.java) + } } fun ResultRow.toTimeline(): Timeline { diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/InstanceRepositoryImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/InstanceRepositoryImpl.kt index 883a5b48..485cf6de 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/InstanceRepositoryImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/InstanceRepositoryImpl.kt @@ -1,21 +1,25 @@ package dev.usbharu.hideout.core.infrastructure.exposedrepository import dev.usbharu.hideout.application.service.id.IdGenerateService -import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException import dev.usbharu.hideout.core.domain.model.instance.InstanceRepository -import dev.usbharu.hideout.util.singleOr import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.jetbrains.exposed.sql.javatime.timestamp +import org.slf4j.Logger +import org.slf4j.LoggerFactory import org.springframework.stereotype.Repository import dev.usbharu.hideout.core.domain.model.instance.Instance as InstanceEntity @Repository -class InstanceRepositoryImpl(private val idGenerateService: IdGenerateService) : InstanceRepository { +class InstanceRepositoryImpl(private val idGenerateService: IdGenerateService) : InstanceRepository, + AbstractRepository() { + override val logger: Logger + get() = Companion.logger + override suspend fun generateId(): Long = idGenerateService.generateId() - override suspend fun save(instance: InstanceEntity): InstanceEntity { - if (Instance.select { Instance.id.eq(instance.id) }.empty()) { + override suspend fun save(instance: InstanceEntity): InstanceEntity = query { + if (Instance.select { Instance.id.eq(instance.id) }.forUpdate().empty()) { Instance.insert { it[id] = instance.id it[name] = instance.name @@ -45,17 +49,25 @@ class InstanceRepositoryImpl(private val idGenerateService: IdGenerateService) : it[createdAt] = instance.createdAt } } - return instance + return@query instance } - override suspend fun findById(id: Long): InstanceEntity { - return Instance.select { Instance.id eq id } - .singleOr { FailedToGetResourcesException("id: $id doesn't exist.") }.toInstance() + override suspend fun findById(id: Long): InstanceEntity? = query { + return@query Instance.select { Instance.id eq id } + .singleOrNull()?.toInstance() } - override suspend fun delete(instance: InstanceEntity) { + override suspend fun delete(instance: InstanceEntity): Unit = query { Instance.deleteWhere { id eq instance.id } } + + override suspend fun findByUrl(url: String): dev.usbharu.hideout.core.domain.model.instance.Instance? = query { + return@query Instance.select { Instance.url eq url }.singleOrNull()?.toInstance() + } + + companion object { + private val logger = LoggerFactory.getLogger(InstanceRepositoryImpl::class.java) + } } fun ResultRow.toInstance(): InstanceEntity { diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/MediaRepositoryImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/MediaRepositoryImpl.kt index 97b7c527..c838031c 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/MediaRepositoryImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/MediaRepositoryImpl.kt @@ -1,25 +1,28 @@ package dev.usbharu.hideout.core.infrastructure.exposedrepository import dev.usbharu.hideout.application.service.id.IdGenerateService -import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException import dev.usbharu.hideout.core.domain.model.media.MediaRepository import dev.usbharu.hideout.core.infrastructure.exposedrepository.Media.mimeType import dev.usbharu.hideout.core.service.media.FileType import dev.usbharu.hideout.core.service.media.MimeType -import dev.usbharu.hideout.util.singleOr import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq +import org.slf4j.Logger +import org.slf4j.LoggerFactory import org.springframework.stereotype.Repository import dev.usbharu.hideout.core.domain.model.media.Media as EntityMedia @Repository -class MediaRepositoryImpl(private val idGenerateService: IdGenerateService) : MediaRepository { +class MediaRepositoryImpl(private val idGenerateService: IdGenerateService) : MediaRepository, AbstractRepository() { + override val logger: Logger + get() = Companion.logger + override suspend fun generateId(): Long = idGenerateService.generateId() - override suspend fun save(media: EntityMedia): EntityMedia { + override suspend fun save(media: EntityMedia): EntityMedia = query { if (Media.select { Media.id eq media.id - }.singleOrNull() != null + }.forUpdate().singleOrNull() != null ) { Media.update({ Media.id eq media.id }) { it[name] = media.name @@ -44,24 +47,32 @@ class MediaRepositoryImpl(private val idGenerateService: IdGenerateService) : Me it[description] = media.description } } - return media + return@query media } - override suspend fun findById(id: Long): EntityMedia { - return Media + override suspend fun findById(id: Long): EntityMedia? = query { + return@query Media .select { Media.id eq id } - .singleOr { - FailedToGetResourcesException("id: $id was not found.") - }.toMedia() + .singleOrNull() + ?.toMedia() } - override suspend fun delete(id: Long) { + override suspend fun delete(id: Long): Unit = query { Media.deleteWhere { Media.id eq id } } + + override suspend fun findByRemoteUrl(remoteUrl: String): dev.usbharu.hideout.core.domain.model.media.Media? = + query { + return@query Media.select { Media.remoteUrl eq remoteUrl }.singleOrNull()?.toMedia() + } + + companion object { + private val logger = LoggerFactory.getLogger(MediaRepositoryImpl::class.java) + } } fun ResultRow.toMedia(): EntityMedia { diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/PostRepositoryImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/PostRepositoryImpl.kt index e1e323e2..2a1899fd 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/PostRepositoryImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/PostRepositoryImpl.kt @@ -2,24 +2,26 @@ package dev.usbharu.hideout.core.infrastructure.exposedrepository import dev.usbharu.hideout.application.infrastructure.exposed.QueryMapper import dev.usbharu.hideout.application.service.id.IdGenerateService -import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException import dev.usbharu.hideout.core.domain.model.post.Post import dev.usbharu.hideout.core.domain.model.post.PostRepository -import dev.usbharu.hideout.util.singleOr import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq +import org.slf4j.Logger +import org.slf4j.LoggerFactory import org.springframework.stereotype.Repository @Repository class PostRepositoryImpl( private val idGenerateService: IdGenerateService, private val postQueryMapper: QueryMapper -) : PostRepository { +) : PostRepository, AbstractRepository() { + override val logger: Logger + get() = Companion.logger override suspend fun generateId(): Long = idGenerateService.generateId() - override suspend fun save(post: Post): Boolean { - val singleOrNull = Posts.select { Posts.id eq post.id }.singleOrNull() + override suspend fun save(post: Post): Post = query { + val singleOrNull = Posts.select { Posts.id eq post.id }.forUpdate().singleOrNull() if (singleOrNull == null) { Posts.insert { it[id] = post.id @@ -61,18 +63,45 @@ class PostRepositoryImpl( it[deleted] = post.delted } } - return singleOrNull == null + return@query post } - override suspend fun findById(id: Long): Post = - Posts.leftJoin(PostsMedia) + override suspend fun findById(id: Long): Post? = query { + return@query Posts.leftJoin(PostsMedia) .select { Posts.id eq id } .let(postQueryMapper::map) - .singleOr { FailedToGetResourcesException("id: $id was not found.", it) } + .singleOrNull() + } - override suspend fun delete(id: Long) { + override suspend fun findByUrl(url: String): Post? = query { + return@query Posts.leftJoin(PostsMedia) + .select { Posts.url eq url } + .let(postQueryMapper::map) + .singleOrNull() + } + + override suspend fun findByApId(apId: String): Post? = query { + return@query Posts.leftJoin(PostsMedia) + .select { Posts.apId eq apId } + .let(postQueryMapper::map) + .singleOrNull() + } + + override suspend fun existByApIdWithLock(apId: String): Boolean = query { + return@query Posts.select { Posts.apId eq apId }.forUpdate().empty().not() + } + + override suspend fun findByActorId(actorId: Long): List = query { + return@query Posts.select { Posts.actorId eq actorId }.let(postQueryMapper::map) + } + + override suspend fun delete(id: Long): Unit = query { Posts.deleteWhere { Posts.id eq id } } + + companion object { + private val logger = LoggerFactory.getLogger(PostRepositoryImpl::class.java) + } } object Posts : Table() { diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ReactionRepositoryImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ReactionRepositoryImpl.kt index 0b14d787..1a3a14ff 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ReactionRepositoryImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ReactionRepositoryImpl.kt @@ -6,17 +6,21 @@ import dev.usbharu.hideout.core.domain.model.reaction.ReactionRepository import org.jetbrains.exposed.dao.id.LongIdTable import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq +import org.slf4j.Logger +import org.slf4j.LoggerFactory import org.springframework.stereotype.Repository @Repository class ReactionRepositoryImpl( private val idGenerateService: IdGenerateService -) : ReactionRepository { +) : ReactionRepository, AbstractRepository() { + override val logger: Logger + get() = Companion.logger override suspend fun generateId(): Long = idGenerateService.generateId() - override suspend fun save(reaction: Reaction): Reaction { - if (Reactions.select { Reactions.id eq reaction.id }.empty()) { + override suspend fun save(reaction: Reaction): Reaction = query { + if (Reactions.select { Reactions.id eq reaction.id }.forUpdate().empty()) { Reactions.insert { it[id] = reaction.id it[emojiId] = reaction.emojiId @@ -30,30 +34,62 @@ class ReactionRepositoryImpl( it[actorId] = reaction.actorId } } - return reaction + return@query reaction } - override suspend fun delete(reaction: Reaction): Reaction { + override suspend fun delete(reaction: Reaction): Reaction = query { Reactions.deleteWhere { - id.eq(reaction.id) - .and(postId.eq(reaction.postId)) - .and(actorId.eq(reaction.actorId)) + id.eq(reaction.id).and(postId.eq(reaction.postId)).and(actorId.eq(reaction.actorId)) .and(emojiId.eq(reaction.emojiId)) } - return reaction + return@query reaction } - override suspend fun deleteByPostId(postId: Long): Int { - return Reactions.deleteWhere { + override suspend fun deleteByPostId(postId: Long): Int = query { + return@query Reactions.deleteWhere { Reactions.postId eq postId } } - override suspend fun deleteByActorId(actorId: Long): Int { - return Reactions.deleteWhere { + override suspend fun deleteByActorId(actorId: Long): Int = query { + return@query Reactions.deleteWhere { Reactions.actorId eq actorId } } + + override suspend fun findByPostId(postId: Long): List = query { + return@query Reactions.select { Reactions.postId eq postId }.map { it.toReaction() } + } + + override suspend fun findByPostIdAndActorIdAndEmojiId(postId: Long, actorId: Long, emojiId: Long): Reaction? = + query { + return@query Reactions.select { + Reactions.postId eq postId and (Reactions.actorId eq actorId).and( + Reactions.emojiId.eq( + emojiId + ) + ) + }.singleOrNull()?.toReaction() + } + + override suspend fun existByPostIdAndActorIdAndEmojiId(postId: Long, actorId: Long, emojiId: Long): Boolean = + query { + return@query Reactions.select { + Reactions.postId + .eq(postId) + .and(Reactions.actorId.eq(actorId)) + .and(Reactions.emojiId.eq(emojiId)) + }.empty().not() + } + + override suspend fun findByPostIdAndActorId(postId: Long, actorId: Long): List = query { + return@query Reactions.select { Reactions.postId eq postId and (Reactions.actorId eq actorId) } + .map { it.toReaction() } + } + + companion object { + private val logger = LoggerFactory.getLogger(ReactionRepositoryImpl::class.java) + } } fun ResultRow.toReaction(): Reaction { @@ -67,10 +103,10 @@ fun ResultRow.toReaction(): Reaction { object Reactions : LongIdTable("reactions") { val emojiId: Column = long("emoji_id") - val postId: Column = long("post_id") - .references(Posts.id, onDelete = ReferenceOption.CASCADE, onUpdate = ReferenceOption.CASCADE) - val actorId: Column = long("actor_id") - .references(Actors.id, onDelete = ReferenceOption.CASCADE, onUpdate = ReferenceOption.CASCADE) + val postId: Column = + long("post_id").references(Posts.id, onDelete = ReferenceOption.CASCADE, onUpdate = ReferenceOption.CASCADE) + val actorId: Column = + long("actor_id").references(Actors.id, onDelete = ReferenceOption.CASCADE, onUpdate = ReferenceOption.CASCADE) init { uniqueIndex(emojiId, postId, actorId) diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/UserDetailRepositoryImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/UserDetailRepositoryImpl.kt index 4fb7b9c5..9b58365d 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/UserDetailRepositoryImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/UserDetailRepositoryImpl.kt @@ -8,12 +8,17 @@ import org.jetbrains.exposed.sql.deleteWhere import org.jetbrains.exposed.sql.insert import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.update +import org.slf4j.Logger +import org.slf4j.LoggerFactory import org.springframework.stereotype.Repository @Repository -class UserDetailRepositoryImpl : UserDetailRepository { - override suspend fun save(userDetail: UserDetail): UserDetail { - val singleOrNull = UserDetails.select { UserDetails.actorId eq userDetail.actorId }.singleOrNull() +class UserDetailRepositoryImpl : UserDetailRepository, AbstractRepository() { + override val logger: Logger + get() = Companion.logger + + override suspend fun save(userDetail: UserDetail): UserDetail = query { + val singleOrNull = UserDetails.select { UserDetails.actorId eq userDetail.actorId }.forUpdate().singleOrNull() if (singleOrNull == null) { UserDetails.insert { it[actorId] = userDetail.actorId @@ -26,15 +31,15 @@ class UserDetailRepositoryImpl : UserDetailRepository { it[autoAcceptFolloweeFollowRequest] = userDetail.autoAcceptFolloweeFollowRequest } } - return userDetail + return@query userDetail } - override suspend fun delete(userDetail: UserDetail) { + override suspend fun delete(userDetail: UserDetail): Unit = query { UserDetails.deleteWhere { UserDetails.actorId eq userDetail.actorId } } - override suspend fun findByActorId(actorId: Long): UserDetail? { - return UserDetails + override suspend fun findByActorId(actorId: Long): UserDetail? = query { + return@query UserDetails .select { UserDetails.actorId eq actorId } .singleOrNull() ?.let { @@ -45,6 +50,10 @@ class UserDetailRepositoryImpl : UserDetailRepository { ) } } + + companion object { + private val logger = LoggerFactory.getLogger(UserDetailRepositoryImpl::class.java) + } } object UserDetails : LongIdTable("user_details") { diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/httpsignature/HttpRequestMixIn.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/httpsignature/HttpRequestMixIn.kt index 4f998d91..4e14bdae 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/httpsignature/HttpRequestMixIn.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/httpsignature/HttpRequestMixIn.kt @@ -13,6 +13,7 @@ import java.net.URL @JsonDeserialize(using = HttpRequestDeserializer::class) @JsonSubTypes +@Suppress("UnnecessaryAbstractClass") abstract class HttpRequestMixIn class HttpRequestDeserializer : JsonDeserializer() { diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobmongodb/KJobMongoJobQueueWorkerService.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobmongodb/KJobMongoJobQueueWorkerService.kt index fd57f210..6e5fdad1 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobmongodb/KJobMongoJobQueueWorkerService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobmongodb/KJobMongoJobQueueWorkerService.kt @@ -7,8 +7,11 @@ import dev.usbharu.hideout.core.service.job.JobQueueWorkerService import kjob.core.dsl.JobContextWithProps import kjob.core.dsl.JobRegisterContext import kjob.core.dsl.KJobFunctions +import kjob.core.job.JobExecutionType import kjob.core.kjob import kjob.mongo.Mongo +import kotlinx.coroutines.CancellationException +import org.slf4j.MDC import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty import org.springframework.stereotype.Service @@ -24,6 +27,8 @@ class KJobMongoJobQueueWorkerService( nonBlockingMaxJobs = 10 blockingMaxJobs = 10 jobExecutionPeriodInSeconds = 1 + maxRetries = 3 + defaultJobExecutor = JobExecutionType.NON_BLOCKING }.start() } @@ -37,8 +42,21 @@ class KJobMongoJobQueueWorkerService( for (jobProcessor in jobQueueProcessorList) { kjob.register(jobProcessor.job()) { execute { - val param = it.convertUnsafe(props) - jobProcessor.process(param) + @Suppress("TooGenericExceptionCaught") + try { + MDC.put("x-job-id", this.jobId) + val param = it.convertUnsafe(props) + jobProcessor.process(param) + } catch (e: CancellationException) { + throw e + } catch (e: Exception) { + logger.warn("FAILED Excute Job. job name: {} job id: {}", it.name, this.jobId, e) + throw e + } finally { + MDC.remove("x-job-id") + } + }.onError { + logger.warn("FAILED Excute Job. job name: {} job id: {}", this.jobName, this.jobId, error) } } } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/mongorepository/MongoTimelineRepositoryWrapper.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/mongorepository/MongoTimelineRepositoryWrapper.kt index dfaebfce..f5148bdb 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/mongorepository/MongoTimelineRepositoryWrapper.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/mongorepository/MongoTimelineRepositoryWrapper.kt @@ -1,11 +1,15 @@ package dev.usbharu.hideout.core.infrastructure.mongorepository import dev.usbharu.hideout.application.service.id.IdGenerateService +import dev.usbharu.hideout.core.domain.exception.resource.DuplicateException +import dev.usbharu.hideout.core.domain.exception.resource.ResourceAccessException import dev.usbharu.hideout.core.domain.model.timeline.Timeline import dev.usbharu.hideout.core.domain.model.timeline.TimelineRepository import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty +import org.springframework.dao.DataAccessException +import org.springframework.dao.DuplicateKeyException import org.springframework.stereotype.Repository @Repository @@ -23,8 +27,15 @@ class MongoTimelineRepositoryWrapper( } } - override suspend fun saveAll(timelines: List): List = - mongoTimelineRepository.saveAll(timelines) + override suspend fun saveAll(timelines: List): List { + try { + return mongoTimelineRepository.saveAll(timelines) + } catch (e: DuplicateKeyException) { + throw DuplicateException("Timeline duplicate.", e) + } catch (e: DataAccessException) { + throw ResourceAccessException(e) + } + } override suspend fun findByUserId(id: Long): List { return withContext(Dispatchers.IO) { diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureUserDetailsService.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureUserDetailsService.kt index 8c891da3..17552026 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureUserDetailsService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureUserDetailsService.kt @@ -1,9 +1,8 @@ package dev.usbharu.hideout.core.infrastructure.springframework.httpsignature import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException import dev.usbharu.hideout.core.domain.exception.HttpSignatureVerifyException -import dev.usbharu.hideout.core.query.ActorQueryService +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.util.RsaUtil import dev.usbharu.httpsignature.common.HttpMethod import dev.usbharu.httpsignature.common.HttpRequest @@ -20,10 +19,10 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken class HttpSignatureUserDetailsService( - private val actorQueryService: ActorQueryService, private val httpSignatureVerifier: HttpSignatureVerifier, private val transaction: Transaction, - private val httpSignatureHeaderParser: SignatureHeaderParser + private val httpSignatureHeaderParser: SignatureHeaderParser, + private val actorRepository: ActorRepository ) : AuthenticationUserDetailsService { override fun loadUserDetails(token: PreAuthenticatedAuthenticationToken): UserDetails = runBlocking { @@ -34,11 +33,7 @@ class HttpSignatureUserDetailsService( val keyId = token.principal as String val findByKeyId = transaction.transaction { - try { - actorQueryService.findByKeyId(keyId) - } catch (e: FailedToGetResourcesException) { - throw UsernameNotFoundException("User not found", e) - } + actorRepository.findByKeyId(keyId) ?: throw UsernameNotFoundException("keyId: $keyId not found.") } val signature = httpSignatureHeaderParser.parse(credentials.headers) diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/UserDetailsServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/UserDetailsServiceImpl.kt index 2e979cea..6da7e79a 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/UserDetailsServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/UserDetailsServiceImpl.kt @@ -2,9 +2,9 @@ package dev.usbharu.hideout.core.infrastructure.springframework.oauth2 import dev.usbharu.hideout.application.config.ApplicationConfig import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException +import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository -import dev.usbharu.hideout.core.query.ActorQueryService import kotlinx.coroutines.runBlocking import org.springframework.security.core.userdetails.UserDetails import org.springframework.security.core.userdetails.UserDetailsService @@ -13,10 +13,10 @@ import org.springframework.stereotype.Service @Service class UserDetailsServiceImpl( - private val actorQueryService: ActorQueryService, private val applicationConfig: ApplicationConfig, private val userDetailRepository: UserDetailRepository, - private val transaction: Transaction + private val transaction: Transaction, + private val actorRepository: ActorRepository ) : UserDetailsService { override fun loadUserByUsername(username: String?): UserDetails = runBlocking { @@ -24,11 +24,10 @@ class UserDetailsServiceImpl( throw UsernameNotFoundException("$username not found") } transaction.transaction { - val findById = try { - actorQueryService.findByNameAndDomain(username, applicationConfig.url.host) - } catch (e: FailedToGetResourcesException) { - throw UsernameNotFoundException("$username not found", e) - } + val findById = + actorRepository.findByNameAndDomain(username, applicationConfig.url.host) + ?: throw UserNotFoundException.withNameAndDomain(username, applicationConfig.url.host) + val userDetails = userDetailRepository.findByActorId(findById.id) ?: throw UsernameNotFoundException("${findById.id} not found.") UserDetailsImpl( diff --git a/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/AuthController.kt b/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/AuthController.kt index b42f791b..972df120 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/AuthController.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/AuthController.kt @@ -6,5 +6,6 @@ import org.springframework.web.bind.annotation.GetMapping @Controller class AuthController { @GetMapping("/auth/sign_up") + @Suppress("FunctionOnlyReturningConstant") fun signUp(): String = "sign_up" } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/query/ActorQueryService.kt b/src/main/kotlin/dev/usbharu/hideout/core/query/ActorQueryService.kt deleted file mode 100644 index 05b79e25..00000000 --- a/src/main/kotlin/dev/usbharu/hideout/core/query/ActorQueryService.kt +++ /dev/null @@ -1,16 +0,0 @@ -package dev.usbharu.hideout.core.query - -import dev.usbharu.hideout.core.domain.model.actor.Actor -import org.springframework.stereotype.Repository - -@Repository -interface ActorQueryService { - suspend fun findAll(limit: Int, offset: Long): List - suspend fun findById(id: Long): Actor - suspend fun findByName(name: String): List - suspend fun findByNameAndDomain(name: String, domain: String): Actor - suspend fun findByUrl(url: String): Actor - suspend fun findByIds(ids: List): List - suspend fun existByNameAndDomain(name: String, domain: String): Boolean - suspend fun findByKeyId(keyId: String): Actor -} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/query/DeletedActorQueryService.kt b/src/main/kotlin/dev/usbharu/hideout/core/query/DeletedActorQueryService.kt deleted file mode 100644 index de7fcc53..00000000 --- a/src/main/kotlin/dev/usbharu/hideout/core/query/DeletedActorQueryService.kt +++ /dev/null @@ -1,7 +0,0 @@ -package dev.usbharu.hideout.core.query - -import dev.usbharu.hideout.core.domain.model.deletedActor.DeletedActor - -interface DeletedActorQueryService { - suspend fun findByNameAndDomain(name: String, domain: String): DeletedActor -} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/query/InstanceQueryService.kt b/src/main/kotlin/dev/usbharu/hideout/core/query/InstanceQueryService.kt deleted file mode 100644 index 79e6b213..00000000 --- a/src/main/kotlin/dev/usbharu/hideout/core/query/InstanceQueryService.kt +++ /dev/null @@ -1,7 +0,0 @@ -package dev.usbharu.hideout.core.query - -import dev.usbharu.hideout.core.domain.model.instance.Instance - -interface InstanceQueryService { - suspend fun findByUrl(url: String): Instance -} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/query/MediaQueryService.kt b/src/main/kotlin/dev/usbharu/hideout/core/query/MediaQueryService.kt deleted file mode 100644 index 876c2f1e..00000000 --- a/src/main/kotlin/dev/usbharu/hideout/core/query/MediaQueryService.kt +++ /dev/null @@ -1,8 +0,0 @@ -package dev.usbharu.hideout.core.query - -import dev.usbharu.hideout.core.domain.model.media.Media - -interface MediaQueryService { - suspend fun findByPostId(postId: Long): List - suspend fun findByRemoteUrl(remoteUrl: String): Media -} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/query/PostQueryService.kt b/src/main/kotlin/dev/usbharu/hideout/core/query/PostQueryService.kt deleted file mode 100644 index 64999e89..00000000 --- a/src/main/kotlin/dev/usbharu/hideout/core/query/PostQueryService.kt +++ /dev/null @@ -1,12 +0,0 @@ -package dev.usbharu.hideout.core.query - -import dev.usbharu.hideout.core.domain.model.post.Post -import org.springframework.stereotype.Repository - -@Repository -interface PostQueryService { - suspend fun findById(id: Long): Post - suspend fun findByUrl(url: String): Post - suspend fun findByApId(string: String): Post - suspend fun findByActorId(actorId: Long): List -} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/query/ReactionQueryService.kt b/src/main/kotlin/dev/usbharu/hideout/core/query/ReactionQueryService.kt deleted file mode 100644 index ece7b040..00000000 --- a/src/main/kotlin/dev/usbharu/hideout/core/query/ReactionQueryService.kt +++ /dev/null @@ -1,16 +0,0 @@ -package dev.usbharu.hideout.core.query - -import dev.usbharu.hideout.core.domain.model.reaction.Reaction -import org.springframework.stereotype.Repository - -@Repository -interface ReactionQueryService { - suspend fun findByPostId(postId: Long, actorId: Long? = null): List - - @Suppress("FunctionMaxLength") - suspend fun findByPostIdAndActorIdAndEmojiId(postId: Long, actorId: Long, emojiId: Long): Reaction - - suspend fun reactionAlreadyExist(postId: Long, actorId: Long, emojiId: Long): Boolean - - suspend fun findByPostIdAndActorId(postId: Long, actorId: Long): List -} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/query/RelationshipQueryService.kt b/src/main/kotlin/dev/usbharu/hideout/core/query/RelationshipQueryService.kt deleted file mode 100644 index c7b2fb18..00000000 --- a/src/main/kotlin/dev/usbharu/hideout/core/query/RelationshipQueryService.kt +++ /dev/null @@ -1,18 +0,0 @@ -package dev.usbharu.hideout.core.query - -import dev.usbharu.hideout.core.domain.model.relationship.Relationship - -interface RelationshipQueryService { - - suspend fun findByTargetIdAndFollowing(targetId: Long, following: Boolean): List - - @Suppress("LongParameterList", "FunctionMaxLength") - suspend fun findByTargetIdAndFollowRequestAndIgnoreFollowRequest( - maxId: Long?, - sinceId: Long?, - limit: Int, - targetId: Long, - followRequest: Boolean, - ignoreFollowRequest: Boolean - ): List -} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/instance/InstanceService.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/instance/InstanceService.kt index d06e53ad..318db8ff 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/instance/InstanceService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/instance/InstanceService.kt @@ -1,12 +1,10 @@ package dev.usbharu.hideout.core.service.instance import com.fasterxml.jackson.databind.ObjectMapper -import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException import dev.usbharu.hideout.core.domain.model.instance.Instance import dev.usbharu.hideout.core.domain.model.instance.InstanceRepository import dev.usbharu.hideout.core.domain.model.instance.Nodeinfo import dev.usbharu.hideout.core.domain.model.instance.Nodeinfo2_0 -import dev.usbharu.hideout.core.query.InstanceQueryService import dev.usbharu.hideout.core.service.resource.ResourceResolveService import org.slf4j.LoggerFactory import org.springframework.beans.factory.annotation.Qualifier @@ -23,20 +21,20 @@ interface InstanceService { class InstanceServiceImpl( private val instanceRepository: InstanceRepository, private val resourceResolveService: ResourceResolveService, - @Qualifier("activitypub") private val objectMapper: ObjectMapper, - private val instanceQueryService: InstanceQueryService + @Qualifier("activitypub") private val objectMapper: ObjectMapper ) : InstanceService { override suspend fun fetchInstance(url: String, sharedInbox: String?): Instance { val u = URL(url) val resolveInstanceUrl = u.protocol + "://" + u.host - try { - return instanceQueryService.findByUrl(resolveInstanceUrl) - } catch (e: FailedToGetResourcesException) { - logger.info("Instance not found. try fetch instance info. url: {}", resolveInstanceUrl) - logger.debug("Failed to get resources. url: {}", resolveInstanceUrl, e) + val instance = instanceRepository.findByUrl(resolveInstanceUrl) + + if (instance != null) { + return instance } + logger.info("Instance not found. try fetch instance info. url: {}", resolveInstanceUrl) + val nodeinfoJson = resourceResolveService.resolve("$resolveInstanceUrl/.well-known/nodeinfo").bodyAsText() val nodeinfo = objectMapper.readValue(nodeinfoJson, Nodeinfo::class.java) val nodeinfoPathMap = nodeinfo.links.associate { it.rel to it.href } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/media/LocalFileSystemMediaDataStore.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/media/LocalFileSystemMediaDataStore.kt index 2b329ebb..bae8977d 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/media/LocalFileSystemMediaDataStore.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/media/LocalFileSystemMediaDataStore.kt @@ -74,6 +74,7 @@ class LocalFileSystemMediaDataStore( val fileSavePathString = fileSavePath.toAbsolutePath().toString() logger.info("MEDIA save. path: {}", fileSavePathString) + @Suppress("TooGenericExceptionCaught") try { dataSaveRequest.filePath.copyTo(fileSavePath) dataSaveRequest.thumbnailPath?.copyTo(thumbnailSavePath) @@ -97,6 +98,7 @@ class LocalFileSystemMediaDataStore( */ override suspend fun delete(id: String) { logger.info("START Media delete. id: {}", id) + @Suppress("TooGenericExceptionCaught") try { buildSavePath(savePath, id).deleteIfExists() buildSavePath(savePath, "thumbnail-$id").deleteIfExists() diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaServiceImpl.kt index 80d26318..6cc50463 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaServiceImpl.kt @@ -1,11 +1,9 @@ package dev.usbharu.hideout.core.service.media -import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException import dev.usbharu.hideout.core.domain.exception.media.MediaSaveException import dev.usbharu.hideout.core.domain.exception.media.UnsupportedMediaException import dev.usbharu.hideout.core.domain.model.media.Media import dev.usbharu.hideout.core.domain.model.media.MediaRepository -import dev.usbharu.hideout.core.query.MediaQueryService import dev.usbharu.hideout.core.service.media.converter.MediaProcessService import dev.usbharu.hideout.mastodon.interfaces.api.media.MediaRequest import dev.usbharu.hideout.util.withDelete @@ -24,8 +22,7 @@ class MediaServiceImpl( private val mediaRepository: MediaRepository, private val mediaProcessServices: List, private val remoteMediaDownloadService: RemoteMediaDownloadService, - private val renameService: MediaFileRenameService, - private val mediaQueryService: MediaQueryService + private val renameService: MediaFileRenameService ) : MediaService { @Suppress("LongMethod", "NestedBlockDepth") override suspend fun uploadLocalMedia(mediaRequest: MediaRequest): EntityMedia { @@ -102,11 +99,10 @@ class MediaServiceImpl( override suspend fun uploadRemoteMedia(remoteMedia: RemoteMedia): Media { logger.info("MEDIA Remote media. filename:${remoteMedia.name} url:${remoteMedia.url}") - try { - val findByRemoteUrl = mediaQueryService.findByRemoteUrl(remoteMedia.url) + val findByRemoteUrl = mediaRepository.findByRemoteUrl(remoteMedia.url) + if (findByRemoteUrl != null) { logger.warn("DUPLICATED Remote media is duplicated. url: {}", remoteMedia.url) return findByRemoteUrl - } catch (_: FailedToGetResourcesException) { } remoteMediaDownloadService.download(remoteMedia.url).withDelete().use { @@ -159,7 +155,7 @@ class MediaServiceImpl( } } - protected fun findMediaProcessor(mimeType: MimeType): MediaProcessService { + private fun findMediaProcessor(mimeType: MimeType): MediaProcessService { try { return mediaProcessServices.first { try { @@ -173,7 +169,7 @@ class MediaServiceImpl( } } - protected fun generateBlurhash(process: ProcessedMediaPath): String { + private fun generateBlurhash(process: ProcessedMediaPath): String { val path = if (process.thumbnailPath != null && process.thumbnailMimeType != null) { process.thumbnailPath } else { diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/post/PostServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/post/PostServiceImpl.kt index 63cfb4ef..fcd44f67 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/post/PostServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/post/PostServiceImpl.kt @@ -2,16 +2,15 @@ package dev.usbharu.hideout.core.service.post import dev.usbharu.hideout.activitypub.service.activity.create.ApSendCreateService import dev.usbharu.hideout.core.domain.exception.UserNotFoundException +import dev.usbharu.hideout.core.domain.exception.resource.DuplicateException +import dev.usbharu.hideout.core.domain.exception.resource.PostNotFoundException import dev.usbharu.hideout.core.domain.model.actor.Actor import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.post.Post import dev.usbharu.hideout.core.domain.model.post.PostRepository import dev.usbharu.hideout.core.domain.model.reaction.ReactionRepository -import dev.usbharu.hideout.core.query.PostQueryService import dev.usbharu.hideout.core.service.timeline.TimelineService -import org.jetbrains.exposed.exceptions.ExposedSQLException import org.slf4j.LoggerFactory -import org.springframework.dao.DuplicateKeyException import org.springframework.stereotype.Service import java.time.Instant @@ -20,7 +19,6 @@ class PostServiceImpl( private val postRepository: PostRepository, private val actorRepository: ActorRepository, private val timelineService: TimelineService, - private val postQueryService: PostQueryService, private val postBuilder: Post.PostBuilder, private val apSendCreateService: ApSendCreateService, private val reactionRepository: ReactionRepository @@ -69,7 +67,7 @@ class PostServiceImpl( } override suspend fun deleteByActor(actorId: Long) { - postQueryService.findByActorId(actorId).filterNot { it.delted }.forEach { postRepository.save(it.delete()) } + postRepository.findByActorId(actorId).filterNot { it.delted }.forEach { postRepository.save(it.delete()) } val actor = actorRepository.findById(actorId) ?: throw IllegalStateException("actor: $actorId was not found.") @@ -79,18 +77,12 @@ class PostServiceImpl( private suspend fun internalCreate(post: Post, isLocal: Boolean, actor: Actor): Post { return try { - if (postRepository.save(post)) { - try { - timelineService.publishTimeline(post, isLocal) - actorRepository.save(actor.incrementPostsCount()) - } catch (e: DuplicateKeyException) { - logger.trace("Timeline already exists.", e) - } - } - post - } catch (e: ExposedSQLException) { - logger.warn("FAILED Save to post. url: ${post.apId}", e) - postQueryService.findByApId(post.apId) + val save = postRepository.save(post) + timelineService.publishTimeline(post, isLocal) + actorRepository.save(actor.incrementPostsCount()) + save + } catch (_: DuplicateException) { + postRepository.findByApId(post.apId) ?: throw PostNotFoundException.withApId(post.apId) } } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/reaction/ReactionServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/reaction/ReactionServiceImpl.kt index 0323dc97..2a731029 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/reaction/ReactionServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/reaction/ReactionServiceImpl.kt @@ -1,10 +1,8 @@ package dev.usbharu.hideout.core.service.reaction import dev.usbharu.hideout.activitypub.service.activity.like.APReactionService -import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException import dev.usbharu.hideout.core.domain.model.reaction.Reaction import dev.usbharu.hideout.core.domain.model.reaction.ReactionRepository -import dev.usbharu.hideout.core.query.ReactionQueryService import org.jetbrains.exposed.exceptions.ExposedSQLException import org.slf4j.Logger import org.slf4j.LoggerFactory @@ -13,11 +11,10 @@ import org.springframework.stereotype.Service @Service class ReactionServiceImpl( private val reactionRepository: ReactionRepository, - private val apReactionService: APReactionService, - private val reactionQueryService: ReactionQueryService + private val apReactionService: APReactionService ) : ReactionService { override suspend fun receiveReaction(name: String, domain: String, actorId: Long, postId: Long) { - if (reactionQueryService.reactionAlreadyExist(postId, actorId, 0).not()) { + if (reactionRepository.existByPostIdAndActorIdAndEmojiId(postId, actorId, 0).not()) { try { reactionRepository.save( Reaction(reactionRepository.generateId(), 0, postId, actorId) @@ -28,31 +25,37 @@ class ReactionServiceImpl( } override suspend fun receiveRemoveReaction(actorId: Long, postId: Long) { - val reaction = reactionQueryService.findByPostIdAndActorIdAndEmojiId(postId, actorId, 0) + val reaction = reactionRepository.findByPostIdAndActorIdAndEmojiId(postId, actorId, 0) + if (reaction == null) { + LOGGER.warn("FAILED receive Remove Reaction. $actorId $postId") + return + } reactionRepository.delete(reaction) } override suspend fun sendReaction(name: String, actorId: Long, postId: Long) { - try { - val findByPostIdAndUserIdAndEmojiId = - reactionQueryService.findByPostIdAndActorIdAndEmojiId(postId, actorId, 0) + val findByPostIdAndUserIdAndEmojiId = + reactionRepository.findByPostIdAndActorIdAndEmojiId(postId, actorId, 0) + + if (findByPostIdAndUserIdAndEmojiId != null) { apReactionService.removeReaction(findByPostIdAndUserIdAndEmojiId) reactionRepository.delete(findByPostIdAndUserIdAndEmojiId) - } catch (_: FailedToGetResourcesException) { } + val reaction = Reaction(reactionRepository.generateId(), 0, postId, actorId) reactionRepository.save(reaction) apReactionService.reaction(reaction) } override suspend fun removeReaction(actorId: Long, postId: Long) { - try { - val findByPostIdAndUserIdAndEmojiId = - reactionQueryService.findByPostIdAndActorIdAndEmojiId(postId, actorId, 0) - reactionRepository.delete(findByPostIdAndUserIdAndEmojiId) - apReactionService.removeReaction(findByPostIdAndUserIdAndEmojiId) - } catch (_: FailedToGetResourcesException) { + val findByPostIdAndUserIdAndEmojiId = + reactionRepository.findByPostIdAndActorIdAndEmojiId(postId, actorId, 0) + if (findByPostIdAndUserIdAndEmojiId == null) { + LOGGER.warn("FAILED Remove reaction. actorId: $actorId postId: $postId") + return } + reactionRepository.delete(findByPostIdAndUserIdAndEmojiId) + apReactionService.removeReaction(findByPostIdAndUserIdAndEmojiId) } companion object { diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImpl.kt index 69082c3e..cd83150e 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImpl.kt @@ -6,12 +6,11 @@ import dev.usbharu.hideout.activitypub.service.activity.follow.APSendFollowServi import dev.usbharu.hideout.activitypub.service.activity.reject.ApSendRejectService import dev.usbharu.hideout.activitypub.service.activity.undo.APSendUndoService import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException +import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException import dev.usbharu.hideout.core.domain.model.actor.Actor 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.query.ActorQueryService import dev.usbharu.hideout.core.service.follow.SendFollowDto import org.slf4j.LoggerFactory import org.springframework.stereotype.Service @@ -19,7 +18,6 @@ import org.springframework.stereotype.Service @Service class RelationshipServiceImpl( private val applicationConfig: ApplicationConfig, - private val actorQueryService: ActorQueryService, private val relationshipRepository: RelationshipRepository, private val apSendFollowService: APSendFollowService, private val apSendBlockService: APSendBlockService, @@ -78,10 +76,10 @@ class RelationshipServiceImpl( val remoteUser = isRemoteUser(targetId) if (remoteUser != null) { - val user = actorQueryService.findById(actorId) + val user = actorRepository.findById(actorId) ?: throw UserNotFoundException.withId(actorId) apSendFollowService.sendFollow(SendFollowDto(user, remoteUser)) } else { - val target = actorQueryService.findById(targetId) + val target = actorRepository.findById(targetId) ?: throw UserNotFoundException.withId(targetId) if (target.locked.not()) { acceptFollowRequest(targetId, actorId) } @@ -93,8 +91,8 @@ class RelationshipServiceImpl( override suspend fun block(actorId: Long, targetId: Long) { val relationship = relationshipRepository.findByUserIdAndTargetUserId(actorId, targetId) - val user = actorQueryService.findById(actorId) - val targetActor = actorQueryService.findById(targetId) + val user = actorRepository.findById(actorId) ?: throw UserNotFoundException.withId(actorId) + val targetActor = actorRepository.findById(targetId) ?: throw UserNotFoundException.withId(targetId) if (relationship?.following == true) { actorRepository.save(user.decrementFollowing()) actorRepository.save(targetActor.decrementFollowers()) @@ -174,13 +172,13 @@ class RelationshipServiceImpl( val copy = relationship.copy(followRequest = false, following = true, blocking = false) - val user = actorQueryService.findById(actorId) + val user = actorRepository.findById(actorId) ?: throw UserNotFoundException.withId(actorId) actorRepository.save(user.incrementFollowers()) relationshipRepository.save(copy) - val remoteActor = actorQueryService.findById(targetId) + val remoteActor = actorRepository.findById(targetId) ?: throw UserNotFoundException.withId(targetId) actorRepository.save(remoteActor.incrementFollowing()) @@ -209,7 +207,7 @@ class RelationshipServiceImpl( val remoteUser = isRemoteUser(targetId) if (remoteUser != null) { - val user = actorQueryService.findById(actorId) + val user = actorRepository.findById(actorId) ?: throw UserNotFoundException.withId(actorId) apSendRejectService.sendRejectFollow(user, remoteUser) } } @@ -238,8 +236,8 @@ class RelationshipServiceImpl( return } - val user = actorQueryService.findById(actorId) - val targetActor = actorQueryService.findById(targetId) + val user = actorRepository.findById(actorId) ?: throw UserNotFoundException.withId(actorId) + val targetActor = actorRepository.findById(targetId) ?: throw UserNotFoundException.withId(targetId) if (relationship.following) { actorRepository.save(user.decrementFollowing()) @@ -280,7 +278,7 @@ class RelationshipServiceImpl( val remoteUser = isRemoteUser(targetId) if (remoteUser != null) { - val user = actorQueryService.findById(actorId) + val user = actorRepository.findById(actorId) ?: throw UserNotFoundException.withId(actorId) apSendUndoService.sendUndoBlock(user, remoteUser) } } @@ -315,12 +313,8 @@ class RelationshipServiceImpl( private suspend fun isRemoteUser(userId: Long): Actor? { logger.trace("isRemoteUser({})", userId) - val user = try { - actorQueryService.findById(userId) - } catch (e: FailedToGetResourcesException) { - logger.warn("User not found.", e) - throw IllegalStateException("User not found.", e) - } + val user = + actorRepository.findById(userId) ?: throw UserNotFoundException.withId(userId) logger.trace("user info {}", user) diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/resource/InMemoryCacheManager.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/resource/InMemoryCacheManager.kt index 6efbc980..fc35b768 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/resource/InMemoryCacheManager.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/resource/InMemoryCacheManager.kt @@ -46,7 +46,7 @@ class InMemoryCacheManager : CacheManager { override suspend fun getOrWait(key: String): ResolveResponse { while (valueStore.contains(key).not()) { if (cacheKey.containsKey(key).not()) { - throw IllegalStateException("Invalid cache key.") + throw IllegalStateException("Invalid cache key. $key") } delay(1) } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/TimelineService.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/TimelineService.kt index e53e327d..a6d8b185 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/TimelineService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/TimelineService.kt @@ -1,10 +1,11 @@ package dev.usbharu.hideout.core.service.timeline +import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository 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.timeline.Timeline import dev.usbharu.hideout.core.domain.model.timeline.TimelineRepository -import dev.usbharu.hideout.core.query.ActorQueryService import dev.usbharu.hideout.core.query.FollowerQueryService import org.slf4j.LoggerFactory import org.springframework.stereotype.Service @@ -12,14 +13,14 @@ import org.springframework.stereotype.Service @Service class TimelineService( private val followerQueryService: FollowerQueryService, - private val actorQueryService: ActorQueryService, - private val timelineRepository: TimelineRepository + private val timelineRepository: TimelineRepository, + private val actorRepository: ActorRepository ) { suspend fun publishTimeline(post: Post, isLocal: Boolean) { val findFollowersById = followerQueryService.findFollowersById(post.actorId).toMutableList() if (isLocal) { // 自分自身も含める必要がある - val user = actorQueryService.findById(post.actorId) + val user = actorRepository.findById(post.actorId) ?: throw UserNotFoundException.withId(post.actorId) findFollowersById.add(user) } val timelines = findFollowersById.map { diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserAuthServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserAuthServiceImpl.kt index 5ec5d4be..dd8f8669 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserAuthServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserAuthServiceImpl.kt @@ -1,6 +1,7 @@ package dev.usbharu.hideout.core.service.user -import dev.usbharu.hideout.core.query.ActorQueryService +import dev.usbharu.hideout.application.config.ApplicationConfig +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder import org.springframework.stereotype.Service import java.security.* @@ -8,13 +9,14 @@ import java.util.* @Service class UserAuthServiceImpl( - val actorQueryService: ActorQueryService + private val actorRepository: ActorRepository, + private val applicationConfig: ApplicationConfig ) : UserAuthService { override fun hash(password: String): String = BCryptPasswordEncoder().encode(password) override suspend fun usernameAlreadyUse(username: String): Boolean { - actorQueryService.findByName(username) + actorRepository.findByNameAndDomain(username, applicationConfig.url.host) ?: return false return true } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt index 36259740..df4b4fd6 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt @@ -2,7 +2,8 @@ package dev.usbharu.hideout.core.service.user import dev.usbharu.hideout.activitypub.service.activity.delete.APSendDeleteService import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException +import dev.usbharu.hideout.core.domain.exception.resource.DuplicateException +import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException import dev.usbharu.hideout.core.domain.model.actor.Actor import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.deletedActor.DeletedActor @@ -11,14 +12,10 @@ import dev.usbharu.hideout.core.domain.model.reaction.ReactionRepository import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository import dev.usbharu.hideout.core.domain.model.userdetails.UserDetail import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository -import dev.usbharu.hideout.core.query.ActorQueryService -import dev.usbharu.hideout.core.query.DeletedActorQueryService import dev.usbharu.hideout.core.service.instance.InstanceService import dev.usbharu.hideout.core.service.post.PostService -import org.jetbrains.exposed.exceptions.ExposedSQLException import org.slf4j.LoggerFactory import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional import java.time.Instant @Service @@ -26,13 +23,11 @@ import java.time.Instant class UserServiceImpl( private val actorRepository: ActorRepository, private val userAuthService: UserAuthService, - private val actorQueryService: ActorQueryService, private val actorBuilder: Actor.UserBuilder, private val applicationConfig: ApplicationConfig, private val instanceService: InstanceService, private val userDetailRepository: UserDetailRepository, private val deletedActorRepository: DeletedActorRepository, - private val deletedActorQueryService: DeletedActorQueryService, private val reactionRepository: ReactionRepository, private val relationshipRepository: RelationshipRepository, private val postService: PostService, @@ -42,7 +37,7 @@ class UserServiceImpl( UserService { override suspend fun usernameAlreadyUse(username: String): Boolean { - val findByNameAndDomain = actorQueryService.findByNameAndDomain(username, applicationConfig.url.host) + val findByNameAndDomain = actorRepository.findByNameAndDomain(username, applicationConfig.url.host) return findByNameAndDomain != null } @@ -73,15 +68,14 @@ class UserServiceImpl( return save } - @Transactional override suspend fun createRemoteUser(user: RemoteUserCreateDto): Actor { logger.info("START Create New remote user. name: {} url: {}", user.name, user.url) - try { - deletedActorQueryService.findByNameAndDomain(user.name, user.domain) + val deletedActor = deletedActorRepository.findByNameAndDomain(user.name, user.domain) + + if (deletedActor != null) { logger.warn("FAILED Deleted actor. user: ${user.name} domain: ${user.domain}") throw IllegalStateException("Cannot create Deleted actor.") - } catch (_: FailedToGetResourcesException) { } @Suppress("TooGenericExceptionCaught") @@ -114,9 +108,8 @@ class UserServiceImpl( val save = actorRepository.save(userEntity) logger.warn("SUCCESS Create New remote user. id: {} name: {} url: {}", userEntity.id, user.name, user.url) save - } catch (_: ExposedSQLException) { - logger.warn("FAILED User already exists. name: {} url: {}", user.name, user.url) - actorQueryService.findByUrl(user.url) + } catch (_: DuplicateException) { + actorRepository.findByUrl(user.url)!! } } @@ -142,7 +135,7 @@ class UserServiceImpl( } override suspend fun deleteRemoteActor(actorId: Long) { - val actor = actorQueryService.findById(actorId) + val actor = actorRepository.findByIdWithLock(actorId) ?: throw UserNotFoundException.withId(actorId) val deletedActor = DeletedActor( actor.id, actor.name, @@ -161,7 +154,7 @@ class UserServiceImpl( } override suspend fun deleteLocalUser(userId: Long) { - val actor = actorQueryService.findById(userId) + val actor = actorRepository.findByIdWithLock(userId) ?: throw UserNotFoundException.withId(userId) apSendDeleteService.sendDeleteActor(actor) val deletedActor = DeletedActor( actor.id, diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/AccountQueryServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/AccountQueryServiceImpl.kt index 92f38f09..4cd4ed55 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/AccountQueryServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/AccountQueryServiceImpl.kt @@ -1,11 +1,9 @@ package dev.usbharu.hideout.mastodon.infrastructure.exposedquery import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException import dev.usbharu.hideout.core.infrastructure.exposedrepository.Actors import dev.usbharu.hideout.domain.mastodon.model.generated.Account import dev.usbharu.hideout.mastodon.query.AccountQueryService -import dev.usbharu.hideout.util.singleOr import org.jetbrains.exposed.sql.ResultRow import org.jetbrains.exposed.sql.select import org.springframework.stereotype.Repository @@ -13,12 +11,12 @@ import java.time.Instant @Repository class AccountQueryServiceImpl(private val applicationConfig: ApplicationConfig) : AccountQueryService { - override suspend fun findById(accountId: Long): Account { + override suspend fun findById(accountId: Long): Account? { val query = Actors.select { Actors.id eq accountId } return query - .singleOr { FailedToGetResourcesException("accountId: $accountId wad not exist or duplicate", it) } - .let { toAccount(it) } + .singleOrNull() + ?.let { toAccount(it) } } override suspend fun findByIds(accountIds: List): List { diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/query/AccountQueryService.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/query/AccountQueryService.kt index 37eb2d98..eab3b746 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/query/AccountQueryService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/query/AccountQueryService.kt @@ -3,6 +3,6 @@ package dev.usbharu.hideout.mastodon.query import dev.usbharu.hideout.domain.mastodon.model.generated.Account interface AccountQueryService { - suspend fun findById(accountId: Long): Account + suspend fun findById(accountId: Long): Account? suspend fun findByIds(accountIds: List): List } diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt index 98cfdb3e..0cefb38c 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt @@ -2,7 +2,6 @@ package dev.usbharu.hideout.mastodon.service.account import dev.usbharu.hideout.application.external.Transaction import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository -import dev.usbharu.hideout.core.query.RelationshipQueryService import dev.usbharu.hideout.core.service.media.MediaService import dev.usbharu.hideout.core.service.relationship.RelationshipService import dev.usbharu.hideout.core.service.user.UpdateUserDto @@ -71,8 +70,7 @@ class AccountApiServiceImpl( private val statusQueryService: StatusQueryService, private val relationshipService: RelationshipService, private val relationshipRepository: RelationshipRepository, - private val mediaService: MediaService, - private val relationshipQueryService: RelationshipQueryService + private val mediaService: MediaService ) : AccountApiService { override suspend fun accountsStatuses( @@ -221,7 +219,7 @@ class AccountApiServiceImpl( limit: Int, withIgnore: Boolean ): List = transaction.transaction { - val actorIdList = relationshipQueryService + val actorIdList = relationshipRepository .findByTargetIdAndFollowRequestAndIgnoreFollowRequest( maxId = maxId, sinceId = sinceId, diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountService.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountService.kt index b3f57aaf..09b55cbd 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountService.kt @@ -14,7 +14,8 @@ interface AccountService { class AccountServiceImpl( private val accountQueryService: AccountQueryService ) : AccountService { - override suspend fun findById(id: Long): Account = accountQueryService.findById(id) + override suspend fun findById(id: Long): Account = + accountQueryService.findById(id) ?: throw IllegalArgumentException("Account $id not found.") override suspend fun findByIds(ids: List): List = accountQueryService.findByIds(ids) } diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/status/StatusesApiService.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/status/StatusesApiService.kt index eac73698..1a095d82 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/status/StatusesApiService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/status/StatusesApiService.kt @@ -1,11 +1,10 @@ package dev.usbharu.hideout.mastodon.service.status import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.media.MediaRepository import dev.usbharu.hideout.core.domain.model.media.toMediaAttachments -import dev.usbharu.hideout.core.query.ActorQueryService -import dev.usbharu.hideout.core.query.PostQueryService +import dev.usbharu.hideout.core.domain.model.post.PostRepository import dev.usbharu.hideout.core.service.post.PostCreateDto import dev.usbharu.hideout.core.service.post.PostService import dev.usbharu.hideout.domain.mastodon.model.generated.Status @@ -29,10 +28,10 @@ interface StatusesApiService { class StatsesApiServiceImpl( private val postService: PostService, private val accountService: AccountService, - private val postQueryService: PostQueryService, - private val actorQueryService: ActorQueryService, private val mediaRepository: MediaRepository, - private val transaction: Transaction + private val transaction: Transaction, + private val actorRepository: ActorRepository, + private val postRepository: PostRepository ) : StatusesApiService { override suspend fun postStatus( @@ -54,17 +53,18 @@ class StatsesApiServiceImpl( val account = accountService.findById(userId) val replyUser = if (post.replyId != null) { - try { - actorQueryService.findById(postQueryService.findById(post.replyId).actorId).id - } catch (ignore: FailedToGetResourcesException) { + val findById = postRepository.findById(post.replyId) + if (findById == null) { null + } else { + actorRepository.findById(findById.actorId)?.id } } else { null } // TODO: n+1解消 - val mediaAttachment = post.mediaIds.map { mediaId -> + val mediaAttachment = post.mediaIds.mapNotNull { mediaId -> mediaRepository.findById(mediaId) }.map { it.toMediaAttachments() diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index daff34db..2208876e 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,6 +1,6 @@ hideout: url: "https://test-hideout.usbharu.dev" - use-mongodb: false + use-mongodb: true security: jwt: generate: true @@ -22,14 +22,14 @@ spring: url: "jdbc:postgresql:hideout2" username: "postgres" password: "" - # data: - # mongodb: - # auto-index-creation: true - # host: localhost - # port: 27017 - # database: hideout - # username: hideoutuser - # password: hideoutpass + data: + mongodb: + auto-index-creation: true + host: localhost + port: 27017 + database: hideout + # username: hideoutuser + # password: hideoutpass servlet: multipart: max-file-size: 40MB diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index 26a50765..ea1c0ecd 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -1,13 +1,28 @@ + + logFile.log + + UTF-8 + %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level [%X{x-request-id}] %logger{36} - %msg%n + + + + logFile.%d{yyyy-MM-dd_HH}.log + + + 30 + + - %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level [%X{x-request-id}] [%X{x-job-id}] %logger{36} - + %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level [%X{x-request-id},%X{x-job-id}] %logger{36} - %msg%n + diff --git a/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/actor/ActorAPControllerImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/actor/ActorAPControllerImplTest.kt index 36cfa4c5..fdb9a65b 100644 --- a/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/actor/ActorAPControllerImplTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/actor/ActorAPControllerImplTest.kt @@ -5,7 +5,7 @@ import dev.usbharu.hideout.activitypub.domain.model.Key import dev.usbharu.hideout.activitypub.domain.model.Person import dev.usbharu.hideout.activitypub.service.objects.user.APUserService import dev.usbharu.hideout.application.config.ActivityPubConfig -import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException +import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -75,7 +75,7 @@ class ActorAPControllerImplTest { @Test fun `userAP 存在しないユーザーにGETすると404が返ってくる`() = runTest { - whenever(apUserService.getPersonByName(eq("fuga"))).doThrow(FailedToGetResourcesException::class) + whenever(apUserService.getPersonByName(eq("fuga"))).doThrow(UserNotFoundException::class) mockMvc .get("/users/fuga") diff --git a/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/webfinger/WebFingerControllerTest.kt b/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/webfinger/WebFingerControllerTest.kt index 41017444..7e038487 100644 --- a/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/webfinger/WebFingerControllerTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/webfinger/WebFingerControllerTest.kt @@ -5,7 +5,7 @@ import com.fasterxml.jackson.module.kotlin.readValue import dev.usbharu.hideout.activitypub.domain.model.webfinger.WebFinger import dev.usbharu.hideout.activitypub.service.webfinger.WebFingerApiService import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException +import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException import kotlinx.coroutines.test.runTest import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.BeforeEach @@ -82,9 +82,12 @@ class WebFingerControllerTest { @Test fun `webfinger 存在しないacctを指定したとき404 Not Foundが返ってくる`() = runTest { - whenever(webFingerApiService.findByNameAndDomain(eq("fuga"), eq("example.com"))).doThrow( - FailedToGetResourcesException::class - ) + whenever( + webFingerApiService.findByNameAndDomain( + eq("fuga"), + eq("example.com") + ) + ).doThrow(UserNotFoundException::class) mockMvc.perform(get("/.well-known/webfinger?resource=acct:fuga@example.com")) .andDo(print()) diff --git a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/APDeliverAcceptJobProcessorTest.kt b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/APDeliverAcceptJobProcessorTest.kt index f26cd135..336e1bf6 100644 --- a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/APDeliverAcceptJobProcessorTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/APDeliverAcceptJobProcessorTest.kt @@ -3,9 +3,9 @@ package dev.usbharu.hideout.activitypub.service.activity.accept import dev.usbharu.hideout.activitypub.domain.model.Accept import dev.usbharu.hideout.activitypub.domain.model.Follow import dev.usbharu.hideout.activitypub.service.common.APRequestService +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.external.job.DeliverAcceptJob import dev.usbharu.hideout.core.external.job.DeliverAcceptJobParam -import dev.usbharu.hideout.core.query.ActorQueryService import kotlinx.coroutines.test.runTest import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -25,7 +25,7 @@ class APDeliverAcceptJobProcessorTest { private lateinit var apRequestService: APRequestService @Mock - private lateinit var actorQueryService: ActorQueryService + private lateinit var actorRepository: ActorRepository @Mock private lateinit var deliverAcceptJob: DeliverAcceptJob @@ -40,7 +40,7 @@ class APDeliverAcceptJobProcessorTest { fun `process apPostが発行される`() = runTest { val user = UserBuilder.localUserOf() - whenever(actorQueryService.findById(eq(1))).doReturn(user) + whenever(actorRepository.findById(eq(1))).doReturn(user) val accept = Accept( apObject = Follow( diff --git a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessorTest.kt b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessorTest.kt index 793a65b6..c7ac9bde 100644 --- a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessorTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessorTest.kt @@ -7,7 +7,7 @@ import dev.usbharu.hideout.activitypub.domain.model.Like import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessContext import dev.usbharu.hideout.activitypub.service.common.ActivityType import dev.usbharu.hideout.application.config.ActivityPubConfig -import dev.usbharu.hideout.core.query.ActorQueryService +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.service.relationship.RelationshipService import dev.usbharu.httpsignature.common.HttpHeaders import dev.usbharu.httpsignature.common.HttpMethod @@ -34,7 +34,7 @@ import java.net.URL class ApAcceptProcessorTest { @Mock - private lateinit var actorQueryService: ActorQueryService + private lateinit var actorRepository: ActorRepository @Mock private lateinit var relationshipService: RelationshipService @@ -67,9 +67,9 @@ class ApAcceptProcessorTest { ) val user = UserBuilder.localUserOf() - whenever(actorQueryService.findByUrl(eq("https://example.com"))).doReturn(user) + whenever(actorRepository.findByUrl(eq("https://example.com"))).doReturn(user) val remoteUser = UserBuilder.remoteUserOf() - whenever(actorQueryService.findByUrl(eq("https://remote.example.com"))).doReturn(remoteUser) + whenever(actorRepository.findByUrl(eq("https://remote.example.com"))).doReturn(remoteUser) apAcceptProcessor.internalProcess(activity) diff --git a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/ApSendCreateServiceImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/ApSendCreateServiceImplTest.kt index c2f4f87e..24cdbf1b 100644 --- a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/ApSendCreateServiceImplTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/ApSendCreateServiceImplTest.kt @@ -6,8 +6,8 @@ import dev.usbharu.hideout.activitypub.query.NoteQueryService import dev.usbharu.hideout.activitypub.service.objects.note.APNoteServiceImpl import dev.usbharu.hideout.application.config.ActivityPubConfig import dev.usbharu.hideout.application.config.ApplicationConfig +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.external.job.DeliverPostJob -import dev.usbharu.hideout.core.query.ActorQueryService import dev.usbharu.hideout.core.query.FollowerQueryService import dev.usbharu.hideout.core.service.job.JobQueueParentService import kotlinx.coroutines.test.runTest @@ -36,7 +36,7 @@ class ApSendCreateServiceImplTest { private lateinit var jobQueueParentService: JobQueueParentService @Mock - private lateinit var actorQueryService: ActorQueryService + private lateinit var actorRepository: ActorRepository @Mock private lateinit var noteQueryService: NoteQueryService @@ -68,7 +68,7 @@ class ApSendCreateServiceImplTest { ) whenever(followerQueryService.findFollowersById(eq(post.actorId))).doReturn(followers) - whenever(actorQueryService.findById(eq(post.actorId))).doReturn(user) + whenever(actorRepository.findById(eq(post.actorId))).doReturn(user) whenever(noteQueryService.findById(eq(post.id))).doReturn(note to post) apSendCreateServiceImpl.createNote(post) diff --git a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APReactionServiceImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APReactionServiceImplTest.kt index 0dd3ae9a..716762b3 100644 --- a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APReactionServiceImplTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APReactionServiceImplTest.kt @@ -2,11 +2,12 @@ package dev.usbharu.hideout.activitypub.service.activity.like import dev.usbharu.hideout.application.service.id.TwitterSnowflakeIdGenerateService +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository +import dev.usbharu.hideout.core.domain.model.post.PostRepository import dev.usbharu.hideout.core.domain.model.reaction.Reaction import dev.usbharu.hideout.core.external.job.DeliverReactionJob import dev.usbharu.hideout.core.external.job.DeliverRemoveReactionJob import dev.usbharu.hideout.core.query.FollowerQueryService -import dev.usbharu.hideout.core.query.PostQueryService import dev.usbharu.hideout.core.service.job.JobQueueParentService import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Test @@ -22,7 +23,7 @@ class APReactionServiceImplTest { val user = UserBuilder.localUserOf() val post = PostBuilder.of() - val postQueryService = mock { + val postQueryService = mock { onBlocking { findById(eq(post.id)) } doReturn post } val followerQueryService = mock { @@ -33,11 +34,14 @@ class APReactionServiceImplTest { ) } val jobQueueParentService = mock() + val actorRepository = mock { + onBlocking { findById(eq(user.id)) }.doReturn(user) + } val apReactionServiceImpl = APReactionServiceImpl( jobQueueParentService = jobQueueParentService, - actorQueryService = mock(), + actorRepository = actorRepository, followerQueryService = followerQueryService, - postQueryService = postQueryService, + postRepository = postQueryService, objectMapper = objectMapper ) @@ -59,7 +63,7 @@ class APReactionServiceImplTest { val user = UserBuilder.localUserOf() val post = PostBuilder.of() - val postQueryService = mock { + val postQueryService = mock { onBlocking { findById(eq(post.id)) } doReturn post } val followerQueryService = mock { @@ -70,11 +74,14 @@ class APReactionServiceImplTest { ) } val jobQueueParentService = mock() + val actorRepository = mock { + onBlocking { findById(eq(user.id)) }.doReturn(user) + } val apReactionServiceImpl = APReactionServiceImpl( jobQueueParentService = jobQueueParentService, - actorQueryService = mock(), + actorRepository = actorRepository, followerQueryService = followerQueryService, - postQueryService = postQueryService, + postRepository = postQueryService, objectMapper = objectMapper ) diff --git a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteServiceImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteServiceImplTest.kt index 0a709b38..cece7a5b 100644 --- a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteServiceImplTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteServiceImplTest.kt @@ -13,11 +13,9 @@ import dev.usbharu.hideout.activitypub.service.objects.note.APNoteServiceImpl.Co import dev.usbharu.hideout.activitypub.service.objects.user.APUserService import dev.usbharu.hideout.application.config.CharacterLimit import dev.usbharu.hideout.application.service.id.TwitterSnowflakeIdGenerateService -import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.post.Post import dev.usbharu.hideout.core.domain.model.post.PostRepository -import dev.usbharu.hideout.core.query.ActorQueryService -import dev.usbharu.hideout.core.query.PostQueryService import dev.usbharu.hideout.core.service.post.PostService import io.ktor.client.* import io.ktor.client.call.* @@ -52,7 +50,7 @@ class APNoteServiceImplTest { val post = PostBuilder.of() val user = UserBuilder.localUserOf(id = post.actorId) - val actorQueryService = mock { + val actorQueryService = mock { onBlocking { findById(eq(post.actorId)) } doReturn user } val expected = Note( @@ -71,7 +69,6 @@ class APNoteServiceImplTest { val apNoteServiceImpl = APNoteServiceImpl( postRepository = mock(), apUserService = mock(), - postQueryService = mock(), postService = mock(), apResourceResolveService = mock(), postBuilder = Post.PostBuilder(CharacterLimit()), @@ -89,13 +86,9 @@ class APNoteServiceImplTest { val url = "https://example.com/note" val post = PostBuilder.of() - val postQueryService = mock { - onBlocking { findByApId(eq(post.apId)) } doReturn post - } + val user = UserBuilder.localUserOf(id = post.actorId) - val actorQueryService = mock { - onBlocking { findById(eq(post.actorId)) } doReturn user - } + val note = Note( id = post.apId, attributedTo = user.url, @@ -110,7 +103,7 @@ class APNoteServiceImplTest { onBlocking { resolve(eq(url), any(), isNull()) } doReturn note } val noteQueryService = mock { - onBlocking { findByApid(eq(url)) } doThrow FailedToGetResourcesException() + onBlocking { findByApid(eq(url)) } doReturn null } val person = Person( name = user.name, @@ -135,7 +128,7 @@ class APNoteServiceImplTest { following = user.following, manuallyApprovesFollowers = false - ) + ) val apUserService = mock { onBlocking { fetchPersonWithEntity(eq(note.attributedTo!!), isNull()) } doReturn (person to user) } @@ -145,7 +138,6 @@ class APNoteServiceImplTest { val apNoteServiceImpl = APNoteServiceImpl( postRepository = postRepository, apUserService = apUserService, - postQueryService = postQueryService, postService = mock(), apResourceResolveService = apResourceResolveService, postBuilder = Post.PostBuilder(CharacterLimit()), @@ -163,25 +155,7 @@ class APNoteServiceImplTest { fun `fetchNote(String,String) ノートをリモートから取得した際にエラーが返ってきたらFailedToGetActivityPubResourceExceptionがthrowされる`() = runTest { val url = "https://example.com/note" - val post = PostBuilder.of() - val postQueryService = mock { - onBlocking { findByApId(eq(post.apId)) } doReturn post - } - val user = UserBuilder.localUserOf(id = post.actorId) - val actorQueryService = mock { - onBlocking { findById(eq(post.actorId)) } doReturn user - } - val note = Note( - id = post.apId, - attributedTo = user.url, - content = post.text, - published = Instant.ofEpochMilli(post.createdAt).toString(), - to = listOfNotNull(public, user.followers), - sensitive = post.sensitive, - cc = listOfNotNull(public, user.followers), - inReplyTo = null - ) val apResourceResolveService = mock { val responseData = HttpResponseData( HttpStatusCode.BadRequest, @@ -207,12 +181,11 @@ class APNoteServiceImplTest { ) } val noteQueryService = mock { - onBlocking { findByApid(eq(url)) } doThrow FailedToGetResourcesException() + onBlocking { findByApid(eq(url)) } doReturn null } val apNoteServiceImpl = APNoteServiceImpl( postRepository = mock(), apUserService = mock(), - postQueryService = postQueryService, postService = mock(), apResourceResolveService = apResourceResolveService, postBuilder = Post.PostBuilder(CharacterLimit()), @@ -258,12 +231,11 @@ class APNoteServiceImplTest { } val postService = mock() val noteQueryService = mock { - onBlocking { findByApid(eq(post.apId)) } doThrow FailedToGetResourcesException() + onBlocking { findByApid(eq(post.apId)) } doReturn null } val apNoteServiceImpl = APNoteServiceImpl( postRepository = postRepository, apUserService = apUserService, - postQueryService = mock(), postService = postService, apResourceResolveService = mock(), postBuilder = postBuilder, @@ -300,9 +272,6 @@ class APNoteServiceImplTest { val user = UserBuilder.localUserOf() val post = PostBuilder.of(userId = user.id) - val actorQueryService = mock { - onBlocking { findById(eq(user.id)) } doReturn user - } val note = Note( id = post.apId, attributedTo = user.url, @@ -319,7 +288,6 @@ class APNoteServiceImplTest { val apNoteServiceImpl = APNoteServiceImpl( postRepository = mock(), apUserService = mock(), - postQueryService = mock(), postService = mock(), apResourceResolveService = mock(), postBuilder = postBuilder, diff --git a/src/test/kotlin/dev/usbharu/hideout/core/service/post/PostServiceImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/core/service/post/PostServiceImplTest.kt index cd43fd90..5a76ea07 100644 --- a/src/test/kotlin/dev/usbharu/hideout/core/service/post/PostServiceImplTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/core/service/post/PostServiceImplTest.kt @@ -2,15 +2,14 @@ package dev.usbharu.hideout.core.service.post import dev.usbharu.hideout.activitypub.service.activity.create.ApSendCreateService import dev.usbharu.hideout.application.config.CharacterLimit +import dev.usbharu.hideout.core.domain.exception.resource.DuplicateException import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.post.Post import dev.usbharu.hideout.core.domain.model.post.PostRepository import dev.usbharu.hideout.core.domain.model.reaction.ReactionRepository -import dev.usbharu.hideout.core.query.PostQueryService import dev.usbharu.hideout.core.service.timeline.TimelineService import kotlinx.coroutines.test.runTest import org.assertj.core.api.Assertions.assertThat -import org.jetbrains.exposed.exceptions.ExposedSQLException import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith import org.mockito.InjectMocks @@ -20,7 +19,6 @@ import org.mockito.Mockito.mockStatic import org.mockito.Spy import org.mockito.junit.jupiter.MockitoExtension import org.mockito.kotlin.* -import org.springframework.dao.DuplicateKeyException import utils.PostBuilder import utils.UserBuilder import java.time.Instant @@ -36,10 +34,6 @@ class PostServiceImplTest { @Mock private lateinit var timelineService: TimelineService - - @Mock - private lateinit var postQueryService: PostQueryService - @Spy private var postBuilder: Post.PostBuilder = Post.PostBuilder(CharacterLimit()) @@ -58,7 +52,7 @@ class PostServiceImplTest { val now = Instant.now() val post = PostBuilder.of(createdAt = now.toEpochMilli()) - whenever(postRepository.save(eq(post))).doReturn(true) + whenever(postRepository.save(eq(post))).doReturn(post) whenever(postRepository.generateId()).doReturn(post.id) whenever(actorRepository.findById(eq(post.actorId))).doReturn(UserBuilder.localUserOf(id = post.actorId)) whenever(timelineService.publishTimeline(eq(post), eq(true))).doReturn(Unit) @@ -91,7 +85,7 @@ class PostServiceImplTest { val post = PostBuilder.of() whenever(actorRepository.findById(eq(post.actorId))).doReturn(UserBuilder.remoteUserOf(id = post.actorId)) - whenever(postRepository.save(eq(post))).doReturn(true) + whenever(postRepository.save(eq(post))).doReturn(post) whenever(timelineService.publishTimeline(eq(post), eq(false))).doReturn(Unit) @@ -109,23 +103,8 @@ class PostServiceImplTest { val post = PostBuilder.of() whenever(actorRepository.findById(eq(post.actorId))).doReturn(UserBuilder.remoteUserOf(id = post.actorId)) - whenever(postRepository.save(eq(post))).doReturn(false) - - val createLocal = postServiceImpl.createRemote(post) - - assertThat(createLocal).isEqualTo(post) - - verify(postRepository, times(1)).save(eq(post)) - verify(timelineService, times(0)).publishTimeline(any(), any()) - } - - @Test - fun `createRemote 既に作成されていることを検知できず例外が発生した場合はDBから取得して返す`() = runTest { - val post = PostBuilder.of() - - whenever(actorRepository.findById(eq(post.actorId))).doReturn(UserBuilder.remoteUserOf(id = post.actorId)) - whenever(postRepository.save(eq(post))).doAnswer { throw ExposedSQLException(null, emptyList(), mock()) } - whenever(postQueryService.findByApId(eq(post.apId))).doReturn(post) + whenever(postRepository.save(eq(post))).doAnswer { throw DuplicateException() } + whenever(postRepository.findByApId(eq(post.apId))).doReturn(post) val createLocal = postServiceImpl.createRemote(post) @@ -140,8 +119,9 @@ class PostServiceImplTest { val post = PostBuilder.of() whenever(actorRepository.findById(eq(post.actorId))).doReturn(UserBuilder.remoteUserOf(id = post.actorId)) - whenever(postRepository.save(eq(post))).doReturn(true) - whenever(timelineService.publishTimeline(eq(post), eq(false))).doThrow(DuplicateKeyException::class) + whenever(postRepository.save(eq(post))).doReturn(post) + whenever(timelineService.publishTimeline(eq(post), eq(false))).doThrow(DuplicateException::class) + whenever(postRepository.findByApId(eq(post.apId))).doReturn(post) val createLocal = postServiceImpl.createRemote(post) diff --git a/src/test/kotlin/dev/usbharu/hideout/core/service/reaction/ReactionServiceImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/core/service/reaction/ReactionServiceImplTest.kt index 89e4feae..8dc8f5ec 100644 --- a/src/test/kotlin/dev/usbharu/hideout/core/service/reaction/ReactionServiceImplTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/core/service/reaction/ReactionServiceImplTest.kt @@ -3,10 +3,8 @@ package dev.usbharu.hideout.core.service.reaction import dev.usbharu.hideout.activitypub.service.activity.like.APReactionService import dev.usbharu.hideout.application.service.id.TwitterSnowflakeIdGenerateService -import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException import dev.usbharu.hideout.core.domain.model.reaction.Reaction import dev.usbharu.hideout.core.domain.model.reaction.ReactionRepository -import dev.usbharu.hideout.core.query.ReactionQueryService import kotlinx.coroutines.test.runTest import org.jetbrains.exposed.exceptions.ExposedSQLException import org.junit.jupiter.api.Test @@ -26,9 +24,6 @@ class ReactionServiceImplTest { @Mock private lateinit var apReactionService: APReactionService - @Mock - private lateinit var reactionQueryService: ReactionQueryService - @InjectMocks private lateinit var reactionServiceImpl: ReactionServiceImpl @@ -37,7 +32,9 @@ class ReactionServiceImplTest { val post = PostBuilder.of() - whenever(reactionQueryService.reactionAlreadyExist(eq(post.id), eq(post.actorId), eq(0))).doReturn(false) + whenever(reactionRepository.existByPostIdAndActorIdAndEmojiId(eq(post.id), eq(post.actorId), eq(0))).doReturn( + false + ) val generateId = TwitterSnowflakeIdGenerateService.generateId() whenever(reactionRepository.generateId()).doReturn(generateId) @@ -50,7 +47,9 @@ class ReactionServiceImplTest { fun `receiveReaction リアクションが既に作成されていることを検知出来ずに例外が発生した場合は何もしない`() = runTest { val post = PostBuilder.of() - whenever(reactionQueryService.reactionAlreadyExist(eq(post.id), eq(post.actorId), eq(0))).doReturn(false) + whenever(reactionRepository.existByPostIdAndActorIdAndEmojiId(eq(post.id), eq(post.actorId), eq(0))).doReturn( + false + ) val generateId = TwitterSnowflakeIdGenerateService.generateId() whenever( reactionRepository.save( @@ -79,7 +78,9 @@ class ReactionServiceImplTest { @Test fun `receiveReaction リアクションが既に作成されている場合は何もしない`() = runTest() { val post = PostBuilder.of() - whenever(reactionQueryService.reactionAlreadyExist(eq(post.id), eq(post.actorId), eq(0))).doReturn(true) + whenever(reactionRepository.existByPostIdAndActorIdAndEmojiId(eq(post.id), eq(post.actorId), eq(0))).doReturn( + true + ) reactionServiceImpl.receiveReaction("❤", "example.com", post.actorId, post.id) @@ -89,8 +90,8 @@ class ReactionServiceImplTest { @Test fun `sendReaction リアクションが存在しないとき保存して配送する`() = runTest { val post = PostBuilder.of() - whenever(reactionQueryService.findByPostIdAndActorIdAndEmojiId(eq(post.id), eq(post.actorId), eq(0))).doThrow( - FailedToGetResourcesException::class + whenever(reactionRepository.findByPostIdAndActorIdAndEmojiId(eq(post.id), eq(post.actorId), eq(0))).doReturn( + null ) val generateId = TwitterSnowflakeIdGenerateService.generateId() whenever(reactionRepository.generateId()).doReturn(generateId) @@ -105,7 +106,7 @@ class ReactionServiceImplTest { fun `sendReaction リアクションが存在するときは削除して保存して配送する`() = runTest { val post = PostBuilder.of() val id = TwitterSnowflakeIdGenerateService.generateId() - whenever(reactionQueryService.findByPostIdAndActorIdAndEmojiId(eq(post.id), eq(post.actorId), eq(0))).doReturn( + whenever(reactionRepository.findByPostIdAndActorIdAndEmojiId(eq(post.id), eq(post.actorId), eq(0))).doReturn( Reaction(id, 0, post.id, post.actorId) ) val generateId = TwitterSnowflakeIdGenerateService.generateId() @@ -123,7 +124,7 @@ class ReactionServiceImplTest { @Test fun `removeReaction リアクションが存在する場合削除して配送`() = runTest { val post = PostBuilder.of() - whenever(reactionQueryService.findByPostIdAndActorIdAndEmojiId(eq(post.id), eq(post.actorId), eq(0))).doReturn( + whenever(reactionRepository.findByPostIdAndActorIdAndEmojiId(eq(post.id), eq(post.actorId), eq(0))).doReturn( Reaction(0, 0, post.id, post.actorId) ) diff --git a/src/test/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImplTest.kt index 4f6fa718..44e43a17 100644 --- a/src/test/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImplTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImplTest.kt @@ -9,7 +9,6 @@ import dev.usbharu.hideout.application.config.ApplicationConfig 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.query.ActorQueryService import dev.usbharu.hideout.core.service.follow.SendFollowDto import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Test @@ -29,9 +28,6 @@ class RelationshipServiceImplTest { @Spy private val applicationConfig = ApplicationConfig(URL("https://example.com")) - @Mock - private lateinit var actorQueryService: ActorQueryService - @Mock private lateinit var relationshipRepository: RelationshipRepository @@ -58,7 +54,7 @@ class RelationshipServiceImplTest { @Test fun `followRequest ローカルの場合followRequestフラグがtrueで永続化される`() = runTest { - whenever(actorQueryService.findById(eq(5678))).doReturn(UserBuilder.localUserOf(domain = "example.com")) + whenever(actorRepository.findById(eq(5678))).doReturn(UserBuilder.localUserOf(domain = "example.com")) relationshipServiceImpl.followRequest(1234, 5678) @@ -80,9 +76,9 @@ class RelationshipServiceImplTest { @Test fun `followRequest リモートの場合Followアクティビティが配送される`() = runTest { val localUser = UserBuilder.localUserOf(domain = "example.com") - whenever(actorQueryService.findById(eq(1234))).doReturn(localUser) + whenever(actorRepository.findById(eq(1234))).doReturn(localUser) val remoteUser = UserBuilder.remoteUserOf(domain = "remote.example.com") - whenever(actorQueryService.findById(eq(5678))).doReturn(remoteUser) + whenever(actorRepository.findById(eq(5678))).doReturn(remoteUser) relationshipServiceImpl.followRequest(1234, 5678) @@ -145,9 +141,9 @@ class RelationshipServiceImplTest { @Test fun `followRequest 既にフォローしている場合は念の為フォロー承認を自動で行う`() = runTest { val remoteUser = UserBuilder.remoteUserOf(domain = "remote.example.com") - whenever(actorQueryService.findById(eq(1234))).doReturn(remoteUser) + whenever(actorRepository.findById(eq(1234))).doReturn(remoteUser) val localUser = UserBuilder.localUserOf(domain = "example.com") - whenever(actorQueryService.findById(eq(5678))).doReturn(localUser) + whenever(actorRepository.findById(eq(5678))).doReturn(localUser) whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn( Relationship( actorId = 1234, @@ -213,8 +209,8 @@ class RelationshipServiceImplTest { @Test fun `block ローカルユーザーの場合永続化される`() = runTest { - whenever(actorQueryService.findById(eq(1234))).doReturn(UserBuilder.localUserOf(domain = "example.com")) - whenever(actorQueryService.findById(eq(5678))).doReturn(UserBuilder.localUserOf(domain = "example.com")) + whenever(actorRepository.findById(eq(1234))).doReturn(UserBuilder.localUserOf(domain = "example.com")) + whenever(actorRepository.findById(eq(5678))).doReturn(UserBuilder.localUserOf(domain = "example.com")) relationshipServiceImpl.block(1234, 5678) @@ -236,9 +232,9 @@ class RelationshipServiceImplTest { @Test fun `block リモートユーザーの場合永続化されて配送される`() = runTest { val localUser = UserBuilder.localUserOf(domain = "example.com") - whenever(actorQueryService.findById(eq(1234))).doReturn(localUser) + whenever(actorRepository.findById(eq(1234))).doReturn(localUser) val remoteUser = UserBuilder.remoteUserOf(domain = "remote.example.com") - whenever(actorQueryService.findById(eq(5678))).doReturn(remoteUser) + whenever(actorRepository.findById(eq(5678))).doReturn(remoteUser) relationshipServiceImpl.block(1234, 5678) @@ -261,8 +257,8 @@ class RelationshipServiceImplTest { @Test fun `acceptFollowRequest ローカルユーザーの場合永続化される`() = runTest { - whenever(actorQueryService.findById(eq(1234))).doReturn(UserBuilder.localUserOf(domain = "example.com")) - whenever(actorQueryService.findById(eq(5678))).doReturn(UserBuilder.localUserOf(domain = "example.com")) + whenever(actorRepository.findById(eq(1234))).doReturn(UserBuilder.localUserOf(domain = "example.com")) + whenever(actorRepository.findById(eq(5678))).doReturn(UserBuilder.localUserOf(domain = "example.com")) whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(5678), eq(1234))).doReturn( Relationship( @@ -296,9 +292,9 @@ class RelationshipServiceImplTest { @Test fun `acceptFollowRequest リモートユーザーの場合永続化されて配送される`() = runTest { val localUser = UserBuilder.localUserOf(domain = "example.com") - whenever(actorQueryService.findById(eq(1234))).doReturn(localUser) + whenever(actorRepository.findById(eq(1234))).doReturn(localUser) val remoteUser = UserBuilder.remoteUserOf(domain = "remote.example.com") - whenever(actorQueryService.findById(eq(5678))).doReturn(remoteUser) + whenever(actorRepository.findById(eq(5678))).doReturn(remoteUser) whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(5678), eq(1234))).doReturn( Relationship( @@ -353,8 +349,8 @@ class RelationshipServiceImplTest { @Test fun `acceptFollowRequest フォローリクエストが存在せずforceがtrueのときフォローを承認する`() = runTest { - whenever(actorQueryService.findById(eq(1234))).doReturn(UserBuilder.localUserOf(domain = "example.com")) - whenever(actorQueryService.findById(eq(5678))).doReturn(UserBuilder.remoteUserOf(domain = "remote.example.com")) + whenever(actorRepository.findById(eq(1234))).doReturn(UserBuilder.localUserOf(domain = "example.com")) + whenever(actorRepository.findById(eq(5678))).doReturn(UserBuilder.remoteUserOf(domain = "remote.example.com")) whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(5678), eq(1234))).doReturn( Relationship( @@ -417,7 +413,7 @@ class RelationshipServiceImplTest { @Test fun `rejectFollowRequest ローカルユーザーの場合永続化される`() = runTest { - whenever(actorQueryService.findById(eq(5678))).doReturn(UserBuilder.localUserOf(domain = "example.com")) + whenever(actorRepository.findById(eq(5678))).doReturn(UserBuilder.localUserOf(domain = "example.com")) whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(5678), eq(1234))).doReturn( Relationship( @@ -453,10 +449,10 @@ class RelationshipServiceImplTest { @Test fun `rejectFollowRequest リモートユーザーの場合永続化されて配送される`() = runTest { val localUser = UserBuilder.localUserOf(domain = "example.com") - whenever(actorQueryService.findById(eq(1234))).doReturn(localUser) + whenever(actorRepository.findById(eq(1234))).doReturn(localUser) val remoteUser = UserBuilder.remoteUserOf(domain = "remote.example.com") - whenever(actorQueryService.findById(eq(5678))).doReturn(remoteUser) + whenever(actorRepository.findById(eq(5678))).doReturn(remoteUser) whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(5678), eq(1234))).doReturn( Relationship( @@ -537,8 +533,8 @@ class RelationshipServiceImplTest { @Test fun `unfollow ローカルユーザーの場合永続化される`() = runTest { - whenever(actorQueryService.findById(eq(1234))).doReturn(UserBuilder.remoteUserOf(domain = "remote.example.com")) - whenever(actorQueryService.findById(eq(5678))).doReturn(UserBuilder.localUserOf(domain = "example.com")) + whenever(actorRepository.findById(eq(1234))).doReturn(UserBuilder.remoteUserOf(domain = "remote.example.com")) + whenever(actorRepository.findById(eq(5678))).doReturn(UserBuilder.localUserOf(domain = "example.com")) whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn( Relationship( actorId = 1234, @@ -573,10 +569,10 @@ class RelationshipServiceImplTest { @Test fun `unfollow リモートユーザー場合永続化されて配送される`() = runTest { val localUser = UserBuilder.localUserOf(domain = "example.com") - whenever(actorQueryService.findById(eq(1234))).doReturn(localUser) + whenever(actorRepository.findById(eq(1234))).doReturn(localUser) val remoteUser = UserBuilder.remoteUserOf(domain = "remote.example.com") - whenever(actorQueryService.findById(eq(5678))).doReturn(remoteUser) + whenever(actorRepository.findById(eq(5678))).doReturn(remoteUser) whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn( Relationship( @@ -618,6 +614,9 @@ class RelationshipServiceImplTest { @Test fun `unfollow フォローしていなかった場合は何もしない`() = runTest { + whenever(actorRepository.findById(eq(1234))).doReturn(UserBuilder.localUserOf(id = 1234)) + whenever(actorRepository.findById(eq(5678))).doReturn(UserBuilder.localUserOf(id = 5678)) + whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn( Relationship( actorId = 1234, @@ -637,7 +636,7 @@ class RelationshipServiceImplTest { @Test fun `unblock ローカルユーザーの場合永続化される`() = runTest { - whenever(actorQueryService.findById(eq(5678))).doReturn(UserBuilder.localUserOf(domain = "example.com")) + whenever(actorRepository.findById(eq(5678))).doReturn(UserBuilder.localUserOf(domain = "example.com")) whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn( Relationship( actorId = 1234, @@ -672,10 +671,10 @@ class RelationshipServiceImplTest { @Test fun `unblock リモートユーザーの場合永続化されて配送される`() = runTest { val localUser = UserBuilder.localUserOf(domain = "example.com") - whenever(actorQueryService.findById(eq(1234))).doReturn(localUser) + whenever(actorRepository.findById(eq(1234))).doReturn(localUser) val remoteUser = UserBuilder.remoteUserOf(domain = "remote.example.com") - whenever(actorQueryService.findById(eq(5678))).doReturn(remoteUser) + whenever(actorRepository.findById(eq(5678))).doReturn(remoteUser) whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn( Relationship( diff --git a/src/test/kotlin/dev/usbharu/hideout/core/service/timeline/TimelineServiceTest.kt b/src/test/kotlin/dev/usbharu/hideout/core/service/timeline/TimelineServiceTest.kt index 29b195ae..940591f3 100644 --- a/src/test/kotlin/dev/usbharu/hideout/core/service/timeline/TimelineServiceTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/core/service/timeline/TimelineServiceTest.kt @@ -2,10 +2,10 @@ package dev.usbharu.hideout.core.service.timeline import dev.usbharu.hideout.application.service.id.TwitterSnowflakeIdGenerateService import dev.usbharu.hideout.core.domain.model.actor.Actor +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.post.Visibility import dev.usbharu.hideout.core.domain.model.timeline.Timeline import dev.usbharu.hideout.core.domain.model.timeline.TimelineRepository -import dev.usbharu.hideout.core.query.ActorQueryService import dev.usbharu.hideout.core.query.FollowerQueryService import kotlinx.coroutines.test.runTest import org.assertj.core.api.Assertions.assertThat @@ -27,7 +27,7 @@ class TimelineServiceTest { private lateinit var followerQueryService: FollowerQueryService @Mock - private lateinit var actorQueryService: ActorQueryService + private lateinit var actorRepository: ActorRepository @Mock private lateinit var timelineRepository: TimelineRepository @@ -45,7 +45,7 @@ class TimelineServiceTest { val localUserOf = UserBuilder.localUserOf(id = post.actorId) whenever(followerQueryService.findFollowersById(eq(post.actorId))).doReturn(listOf) - whenever(actorQueryService.findById(eq(post.actorId))).doReturn(localUserOf) + whenever(actorRepository.findById(eq(post.actorId))).doReturn(localUserOf) whenever(timelineRepository.generateId()).doReturn(TwitterSnowflakeIdGenerateService.generateId()) diff --git a/src/test/kotlin/dev/usbharu/hideout/core/service/user/ActorServiceTest.kt b/src/test/kotlin/dev/usbharu/hideout/core/service/user/ActorServiceTest.kt index d41e7e32..430c4ecd 100644 --- a/src/test/kotlin/dev/usbharu/hideout/core/service/user/ActorServiceTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/core/service/user/ActorServiceTest.kt @@ -4,11 +4,9 @@ package dev.usbharu.hideout.core.service.user import dev.usbharu.hideout.application.config.ApplicationConfig import dev.usbharu.hideout.application.config.CharacterLimit -import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException import dev.usbharu.hideout.core.domain.model.actor.Actor import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.post.Post -import dev.usbharu.hideout.core.query.DeletedActorQueryService import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Test @@ -38,13 +36,11 @@ class ActorServiceTest { UserServiceImpl( actorRepository = actorRepository, userAuthService = userAuthService, - actorQueryService = mock(), actorBuilder = actorBuilder, applicationConfig = testApplicationConfig, instanceService = mock(), userDetailRepository = mock(), deletedActorRepository = mock(), - deletedActorQueryService = mock(), reactionRepository = mock(), relationshipRepository = mock(), postService = mock(), @@ -73,25 +69,16 @@ class ActorServiceTest { val actorRepository = mock { onBlocking { nextId() } doReturn 113345L } - val deletedActorQueryService = mock { - onBlocking { - findByNameAndDomain( - eq("test"), - eq("remote.example.com") - ) - } doAnswer { throw FailedToGetResourcesException() } - } + val userService = UserServiceImpl( actorRepository = actorRepository, userAuthService = mock(), - actorQueryService = mock(), actorBuilder = actorBuilder, applicationConfig = testApplicationConfig, instanceService = mock(), userDetailRepository = mock(), deletedActorRepository = mock(), - deletedActorQueryService = deletedActorQueryService, reactionRepository = mock(), relationshipRepository = mock(), postService = mock(), diff --git a/src/test/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiServiceImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiServiceImplTest.kt index 652997c5..192eeb3f 100644 --- a/src/test/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiServiceImplTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiServiceImplTest.kt @@ -4,7 +4,6 @@ import dev.usbharu.hideout.application.external.Transaction import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository import dev.usbharu.hideout.core.query.FollowerQueryService -import dev.usbharu.hideout.core.query.RelationshipQueryService import dev.usbharu.hideout.core.service.media.MediaService import dev.usbharu.hideout.core.service.relationship.RelationshipService import dev.usbharu.hideout.core.service.user.UserService @@ -49,10 +48,7 @@ class AccountApiServiceImplTest { @Mock private lateinit var relationshipRepository: RelationshipRepository - - @Mock - private lateinit var relationshipQueryService: RelationshipQueryService - + @Mock private lateinit var mediaService: MediaService