refactor: FailedToGetResourceExceptionを使わないように

This commit is contained in:
usbharu 2023-12-19 17:52:08 +09:00
parent aa2741d614
commit 3571ab22e3
36 changed files with 185 additions and 210 deletions

View File

@ -5,12 +5,10 @@ import dev.usbharu.hideout.activitypub.domain.model.Note
import dev.usbharu.hideout.activitypub.query.NoteQueryService import dev.usbharu.hideout.activitypub.query.NoteQueryService
import dev.usbharu.hideout.activitypub.service.objects.note.APNoteServiceImpl.Companion.public import dev.usbharu.hideout.activitypub.service.objects.note.APNoteServiceImpl.Companion.public
import dev.usbharu.hideout.application.infrastructure.exposed.QueryMapper 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.Post
import dev.usbharu.hideout.core.domain.model.post.PostRepository import dev.usbharu.hideout.core.domain.model.post.PostRepository
import dev.usbharu.hideout.core.domain.model.post.Visibility import dev.usbharu.hideout.core.domain.model.post.Visibility
import dev.usbharu.hideout.core.infrastructure.exposedrepository.* import dev.usbharu.hideout.core.infrastructure.exposedrepository.*
import dev.usbharu.hideout.util.singleOr
import org.jetbrains.exposed.sql.Query import org.jetbrains.exposed.sql.Query
import org.jetbrains.exposed.sql.ResultRow import org.jetbrains.exposed.sql.ResultRow
import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.select
@ -21,39 +19,38 @@ import java.time.Instant
@Repository @Repository
class NoteQueryServiceImpl(private val postRepository: PostRepository, private val postQueryMapper: QueryMapper<Post>) : class NoteQueryServiceImpl(private val postRepository: PostRepository, private val postQueryMapper: QueryMapper<Post>) :
NoteQueryService { NoteQueryService {
override suspend fun findById(id: Long): Pair<Note, Post> { override suspend fun findById(id: Long): Pair<Note, Post>? {
return Posts return Posts
.leftJoin(Actors) .leftJoin(Actors)
.leftJoin(PostsMedia) .leftJoin(PostsMedia)
.leftJoin(Media) .leftJoin(Media)
.select { Posts.id eq id } .select { Posts.id eq id }
.let { .let {
it.toNote() to postQueryMapper.map(it) (it.toNote() ?: return null) to (postQueryMapper.map(it)
.singleOr { FailedToGetResourcesException("id: $id does not exist.") } .singleOrNull() ?: return null)
} }
} }
override suspend fun findByApid(apId: String): Pair<Note, Post> { override suspend fun findByApid(apId: String): Pair<Note, Post>? {
return Posts return Posts
.leftJoin(Actors) .leftJoin(Actors)
.leftJoin(PostsMedia) .leftJoin(PostsMedia)
.leftJoin(Media) .leftJoin(Media)
.select { Posts.apId eq apId } .select { Posts.apId eq apId }
.let { .let {
it.toNote() to postQueryMapper.map(it) (it.toNote() ?: return null) to (postQueryMapper.map(it)
.singleOr { FailedToGetResourcesException("apid: $apId does not exist.") } .singleOrNull() ?: return null)
} }
} }
private suspend fun ResultRow.toNote(mediaList: List<dev.usbharu.hideout.core.domain.model.media.Media>): Note { private suspend fun ResultRow.toNote(mediaList: List<dev.usbharu.hideout.core.domain.model.media.Media>): Note {
val replyId = this[Posts.replyId] val replyId = this[Posts.replyId]
val replyTo = if (replyId != null) { val replyTo = if (replyId != null) {
try { val url = postRepository.findById(replyId)?.url
postRepository.findById(replyId)?.url ?: throw FailedToGetResourcesException() if (url == null) {
} catch (e: FailedToGetResourcesException) { logger.warn("Failed to get replyId: $replyId")
logger.warn("Failed to get replyId: $replyId", e)
null
} }
url
} else { } else {
null null
} }
@ -76,11 +73,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] } return this.groupBy { it[Posts.id] }
.map { it.value } .map { it.value }
.map { it.first().toNote(it.mapNotNull { resultRow -> resultRow.toMediaOrNull() }) } .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<String>, List<String>> { private fun visibility(visibility: Visibility, followers: String?): Pair<List<String>, List<String>> {

View File

@ -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.domain.model.Person
import dev.usbharu.hideout.activitypub.service.objects.user.APUserService 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.HttpStatus
import org.springframework.http.ResponseEntity import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.RestController 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<Person> { override suspend fun userAp(username: String): ResponseEntity<Person> {
val person = try { val person = try {
apUserService.getPersonByName(username) apUserService.getPersonByName(username)
} catch (_: FailedToGetResourcesException) { } catch (_: UserNotFoundException) {
return ResponseEntity.notFound().build() return ResponseEntity.notFound().build()
} }
person.context += listOf("https://www.w3.org/ns/activitystreams") person.context += listOf("https://www.w3.org/ns/activitystreams")

View File

@ -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.domain.model.webfinger.WebFinger
import dev.usbharu.hideout.activitypub.service.webfinger.WebFingerApiService import dev.usbharu.hideout.activitypub.service.webfinger.WebFingerApiService
import dev.usbharu.hideout.application.config.ApplicationConfig 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 dev.usbharu.hideout.util.AcctUtil
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
@ -29,7 +29,7 @@ class WebFingerController(
} }
val user = try { val user = try {
webFingerApiService.findByNameAndDomain(acct.username, acct.domain ?: applicationConfig.url.host) webFingerApiService.findByNameAndDomain(acct.username, acct.domain ?: applicationConfig.url.host)
} catch (_: FailedToGetResourcesException) { } catch (_: UserNotFoundException) {
return@runBlocking ResponseEntity.notFound().build() return@runBlocking ResponseEntity.notFound().build()
} }
val webFinger = WebFinger( val webFinger = WebFinger(

View File

@ -4,6 +4,6 @@ import dev.usbharu.hideout.activitypub.domain.model.Note
import dev.usbharu.hideout.core.domain.model.post.Post import dev.usbharu.hideout.core.domain.model.post.Post
interface NoteQueryService { interface NoteQueryService {
suspend fun findById(id: Long): Pair<Note, Post> suspend fun findById(id: Long): Pair<Note, Post>?
suspend fun findByApid(apId: String): Pair<Note, Post> suspend fun findByApid(apId: String): Pair<Note, Post>?
} }

View File

@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.ObjectMapper
import dev.usbharu.hideout.activitypub.domain.model.Create import dev.usbharu.hideout.activitypub.domain.model.Create
import dev.usbharu.hideout.activitypub.query.NoteQueryService import dev.usbharu.hideout.activitypub.query.NoteQueryService
import dev.usbharu.hideout.application.config.ApplicationConfig 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.exception.resource.UserNotFoundException
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository 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.Post
@ -31,7 +32,7 @@ class ApSendCreateServiceImpl(
logger.debug("DELIVER Deliver Note Create ${followers.size} accounts.") logger.debug("DELIVER Deliver Note Create ${followers.size} accounts.")
val userEntity = actorRepository.findById(post.actorId) ?: throw UserNotFoundException.withId(post.actorId) val userEntity = actorRepository.findById(post.actorId) ?: throw UserNotFoundException.withId(post.actorId)
val note = noteQueryService.findById(post.id).first val note = noteQueryService.findById(post.id)?.first ?: throw PostNotFoundException.withId(post.id)
val create = Create( val create = Create(
name = "Create Note", name = "Create Note",
apObject = note, apObject = note,

View File

@ -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.ActivityPubProcessContext
import dev.usbharu.hideout.activitypub.service.common.ActivityType import dev.usbharu.hideout.activitypub.service.common.ActivityType
import dev.usbharu.hideout.application.external.Transaction 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.actor.ActorRepository
import dev.usbharu.hideout.core.query.PostQueryService import dev.usbharu.hideout.core.domain.model.post.PostRepository
import dev.usbharu.hideout.core.service.post.PostService import dev.usbharu.hideout.core.service.post.PostService
import dev.usbharu.hideout.core.service.user.UserService import dev.usbharu.hideout.core.service.user.UserService
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
@ -18,10 +17,10 @@ import org.springframework.stereotype.Service
@Service @Service
class APDeleteProcessor( class APDeleteProcessor(
transaction: Transaction, transaction: Transaction,
private val postQueryService: PostQueryService,
private val userService: UserService, private val userService: UserService,
private val postService: PostService, private val postService: PostService,
private val actorRepository: ActorRepository private val actorRepository: ActorRepository,
private val postRepository: PostRepository
) : ) :
AbstractActivityPubProcessor<Delete>(transaction) { AbstractActivityPubProcessor<Delete>(transaction) {
override suspend fun internalProcess(activity: ActivityPubProcessContext<Delete>) { override suspend fun internalProcess(activity: ActivityPubProcessContext<Delete>) {
@ -38,12 +37,13 @@ class APDeleteProcessor(
actor?.let { userService.deleteRemoteActor(it.id) } actor?.let { userService.deleteRemoteActor(it.id) }
try { val post = postRepository.findByApId(deleteId)
val post = postQueryService.findByApId(deleteId) if (post == null) {
postService.deleteRemote(post) logger.warn("FAILED Delete id: {} is not found.", deleteId)
} catch (e: FailedToGetResourcesException) { return
logger.warn("FAILED delete id: {} is not found.", deleteId, e)
} }
postService.deleteRemote(post)
} }
override fun isSupported(activityType: ActivityType): Boolean = activityType == ActivityType.Delete override fun isSupported(activityType: ActivityType): Boolean = activityType == ActivityType.Delete

View File

@ -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.note.APNoteService
import dev.usbharu.hideout.activitypub.service.objects.user.APUserService import dev.usbharu.hideout.activitypub.service.objects.user.APUserService
import dev.usbharu.hideout.application.external.Transaction import dev.usbharu.hideout.application.external.Transaction
import dev.usbharu.hideout.core.query.PostQueryService
import dev.usbharu.hideout.core.service.reaction.ReactionService import dev.usbharu.hideout.core.service.reaction.ReactionService
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
@ -17,7 +16,6 @@ class APLikeProcessor(
transaction: Transaction, transaction: Transaction,
private val apUserService: APUserService, private val apUserService: APUserService,
private val apNoteService: APNoteService, private val apNoteService: APNoteService,
private val postQueryService: PostQueryService,
private val reactionService: ReactionService private val reactionService: ReactionService
) : ) :
AbstractActivityPubProcessor<Like>(transaction) { AbstractActivityPubProcessor<Like>(transaction) {
@ -30,23 +28,21 @@ class APLikeProcessor(
val personWithEntity = apUserService.fetchPersonWithEntity(actor) val personWithEntity = apUserService.fetchPersonWithEntity(actor)
try { try {
apNoteService.fetchNote(target) 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) { } catch (e: FailedToGetActivityPubResourceException) {
logger.debug("FAILED failed to get {}", target) logger.debug("FAILED failed to get {}", target)
logger.trace("", e) logger.trace("", e)
return 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 override fun isSupported(activityType: ActivityType): Boolean = activityType == ActivityType.Like

View File

@ -1,6 +1,7 @@
package dev.usbharu.hideout.activitypub.service.activity.like package dev.usbharu.hideout.activitypub.service.activity.like
import com.fasterxml.jackson.databind.ObjectMapper 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.exception.resource.UserNotFoundException
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
import dev.usbharu.hideout.core.domain.model.reaction.Reaction import dev.usbharu.hideout.core.domain.model.reaction.Reaction
@ -29,7 +30,7 @@ class APReactionServiceImpl(
val followers = followerQueryService.findFollowersById(like.actorId) val followers = followerQueryService.findFollowersById(like.actorId)
val user = actorRepository.findById(like.actorId) ?: throw UserNotFoundException.withId(like.actorId) val user = actorRepository.findById(like.actorId) ?: throw UserNotFoundException.withId(like.actorId)
val post = val post =
postQueryService.findById(like.postId) postQueryService.findById(like.postId) ?: throw PostNotFoundException.withId(like.postId)
followers.forEach { follower -> followers.forEach { follower ->
jobQueueParentService.schedule(DeliverReactionJob) { jobQueueParentService.schedule(DeliverReactionJob) {
props[DeliverReactionJob.actor] = user.url props[DeliverReactionJob.actor] = user.url
@ -45,7 +46,7 @@ class APReactionServiceImpl(
val followers = followerQueryService.findFollowersById(like.actorId) val followers = followerQueryService.findFollowersById(like.actorId)
val user = actorRepository.findById(like.actorId) ?: throw UserNotFoundException.withId(like.actorId) val user = actorRepository.findById(like.actorId) ?: throw UserNotFoundException.withId(like.actorId)
val post = val post =
postQueryService.findById(like.postId) postQueryService.findById(like.postId) ?: throw PostNotFoundException.withId(like.postId)
followers.forEach { follower -> followers.forEach { follower ->
jobQueueParentService.schedule(DeliverRemoveReactionJob) { jobQueueParentService.schedule(DeliverRemoveReactionJob) {
props[DeliverRemoveReactionJob.actor] = user.url props[DeliverRemoveReactionJob.actor] = user.url

View File

@ -7,6 +7,7 @@ import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessContext
import dev.usbharu.hideout.activitypub.service.common.ActivityType import dev.usbharu.hideout.activitypub.service.common.ActivityType
import dev.usbharu.hideout.activitypub.service.objects.user.APUserService import dev.usbharu.hideout.activitypub.service.objects.user.APUserService
import dev.usbharu.hideout.application.external.Transaction import dev.usbharu.hideout.application.external.Transaction
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.UserNotFoundException
import dev.usbharu.hideout.core.domain.exception.resource.local.LocalUserNotFoundException 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.actor.ActorRepository
@ -79,7 +80,8 @@ class APUndoProcessor(
"Like" -> { "Like" -> {
val like = undo.apObject as Like val like = undo.apObject as Like
val post = postQueryService.findByUrl(like.apObject) val post =
postQueryService.findByUrl(like.apObject) ?: throw PostNotFoundException.withUrl(like.apObject)
val signer = val signer =
actorRepository.findById(post.actorId) ?: throw LocalUserNotFoundException.withId(post.actorId) actorRepository.findById(post.actorId) ?: throw LocalUserNotFoundException.withId(post.actorId)

View File

@ -6,7 +6,6 @@ import dev.usbharu.hideout.activitypub.query.NoteQueryService
import dev.usbharu.hideout.activitypub.service.common.APResourceResolveService import dev.usbharu.hideout.activitypub.service.common.APResourceResolveService
import dev.usbharu.hideout.activitypub.service.common.resolve import dev.usbharu.hideout.activitypub.service.common.resolve
import dev.usbharu.hideout.activitypub.service.objects.user.APUserService 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.Post
import dev.usbharu.hideout.core.domain.model.post.PostRepository import dev.usbharu.hideout.core.domain.model.post.PostRepository
import dev.usbharu.hideout.core.domain.model.post.Visibility import dev.usbharu.hideout.core.domain.model.post.Visibility
@ -20,8 +19,9 @@ import org.springframework.stereotype.Service
import java.time.Instant import java.time.Instant
interface APNoteService { interface APNoteService {
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 fetchNote(note: Note, targetActor: String? = null): Note
suspend fun fetchNoteWithEntity(url: String, targetActor: String? = null): Pair<Note, Post>
} }
@Service @Service
@ -40,13 +40,14 @@ class APNoteServiceImpl(
private val logger = LoggerFactory.getLogger(APNoteServiceImpl::class.java) 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<Note, Post> {
logger.debug("START Fetch Note url: {}", url) 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) logger.debug("SUCCESS Found in local url: {}", url)
return post.first return post
} catch (_: FailedToGetResourcesException) {
} }
logger.info("AP GET url: {}", url) logger.info("AP GET url: {}", url)
@ -54,9 +55,7 @@ class APNoteServiceImpl(
apResourceResolveService.resolve<Note>(url, null as Long?) apResourceResolveService.resolve<Note>(url, null as Long?)
} catch (e: ClientRequestException) { } catch (e: ClientRequestException) {
logger.warn( logger.warn(
"FAILED Failed to retrieve ActivityPub resource. HTTP Status Code: {} url: {}", "FAILED Failed to retrieve ActivityPub resource. HTTP Status Code: {} url: {}", e.response.status, url
e.response.status,
url
) )
throw FailedToGetActivityPubResourceException("Could not retrieve $url.", e) throw FailedToGetActivityPubResourceException("Could not retrieve $url.", e)
} }
@ -66,45 +65,33 @@ class APNoteServiceImpl(
} }
private suspend fun saveIfMissing( private suspend fun saveIfMissing(
note: Note, note: Note, targetActor: String?, url: String
targetActor: String?, ): Pair<Note, Post> {
url: String return noteQueryService.findByApid(note.id) ?: saveNote(note, targetActor, url)
): Note {
requireNotNull(note.id) { "id is null" }
return try {
noteQueryService.findByApid(note.id).first
} catch (e: 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<Note, Post> {
val person = apUserService.fetchPersonWithEntity( val person = apUserService.fetchPersonWithEntity(
note.attributedTo, note.attributedTo, targetActor
targetActor
) )
if (postRepository.existByApIdWithLock(note.id)) { val post = postRepository.findByApId(note.id)
return note
if (post != null) {
return note to post
} }
logger.debug("VISIBILITY url: {} to: {} cc: {}", note.id, note.to, note.cc) logger.debug("VISIBILITY url: {} to: {} cc: {}", note.id, note.to, note.cc)
val visibility = val visibility = if (note.to.contains(public)) {
if (note.to.contains(public)) { Visibility.PUBLIC
Visibility.PUBLIC } else if (note.to.contains(person.second.followers) && note.cc.contains(public)) {
} else if (note.to.contains(person.second.followers) && note.cc.contains(public)) { Visibility.UNLISTED
Visibility.UNLISTED } else if (note.to.contains(person.second.followers)) {
} else if (note.to.contains(person.second.followers)) { Visibility.FOLLOWERS
Visibility.FOLLOWERS } else {
} else { Visibility.DIRECT
Visibility.DIRECT }
}
logger.debug("VISIBILITY is {} url: {}", visibility.name, note.id) logger.debug("VISIBILITY is {} url: {}", visibility.name, note.id)
@ -113,22 +100,15 @@ class APNoteServiceImpl(
postQueryService.findByUrl(it) postQueryService.findByUrl(it)
} }
val mediaList = note.attachment val mediaList = note.attachment.map {
.filter { it.url != null } mediaService.uploadRemoteMedia(
.map { RemoteMedia(
mediaService.uploadRemoteMedia( it.name, it.url, it.mediaType, description = it.name
RemoteMedia(
it.name,
it.url,
it.mediaType,
description = it.name
)
) )
} )
.map { it.id } }.map { it.id }
// TODO: リモートのメディア処理を追加 val createRemote = postService.createRemote(
postService.createRemote(
postBuilder.of( postBuilder.of(
id = postRepository.generateId(), id = postRepository.generateId(),
actorId = person.second.id, actorId = person.second.id,
@ -142,11 +122,11 @@ class APNoteServiceImpl(
mediaIds = mediaList mediaIds = mediaList
) )
) )
return note return note to createRemote
} }
override suspend fun fetchNote(note: Note, targetActor: String?): Note = override suspend fun fetchNote(note: Note, targetActor: String?): Note =
saveIfMissing(note, targetActor, note.id) saveIfMissing(note, targetActor, note.id).first
companion object { companion object {
const val public: String = "https://www.w3.org/ns/activitystreams#Public" const val public: String = "https://www.w3.org/ns/activitystreams#Public"

View File

@ -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.domain.model.Note
import dev.usbharu.hideout.activitypub.query.NoteQueryService import dev.usbharu.hideout.activitypub.query.NoteQueryService
import dev.usbharu.hideout.application.external.Transaction 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.Post
import dev.usbharu.hideout.core.domain.model.post.Visibility import dev.usbharu.hideout.core.domain.model.post.Visibility
import dev.usbharu.hideout.core.query.FollowerQueryService import dev.usbharu.hideout.core.query.FollowerQueryService
@ -17,12 +16,13 @@ class NoteApApiServiceImpl(
private val transaction: Transaction private val transaction: Transaction
) : NoteApApiService { ) : NoteApApiService {
override suspend fun getNote(postId: Long, userId: Long?): Note? = transaction.transaction { override suspend fun getNote(postId: Long, userId: Long?): Note? = transaction.transaction {
val findById = try { val findById = noteQueryService.findById(postId)
noteQueryService.findById(postId)
} catch (e: FailedToGetResourcesException) { if (findById == null) {
logger.warn("Note not found.", e) logger.warn("Note not found. $postId $userId")
return@transaction null return@transaction null
} }
when (findById.second.visibility) { when (findById.second.visibility) {
Visibility.PUBLIC, Visibility.UNLISTED -> { Visibility.PUBLIC, Visibility.UNLISTED -> {
return@transaction findById.first return@transaction findById.first

View File

@ -19,5 +19,9 @@ class PostNotFoundException : NotFoundException {
private const val serialVersionUID: Long = 1315818410686905717L private const val serialVersionUID: Long = 1315818410686905717L
fun withApId(apId: String): PostNotFoundException = PostNotFoundException("apId: $apId was not found.") 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.")
} }
} }

View File

@ -3,5 +3,5 @@ package dev.usbharu.hideout.core.domain.model.deletedActor
interface DeletedActorRepository { interface DeletedActorRepository {
suspend fun save(deletedActor: DeletedActor): DeletedActor suspend fun save(deletedActor: DeletedActor): DeletedActor
suspend fun delete(deletedActor: DeletedActor) suspend fun delete(deletedActor: DeletedActor)
suspend fun findById(id: Long): DeletedActor suspend fun findById(id: Long): DeletedActor?
} }

View File

@ -3,6 +3,6 @@ package dev.usbharu.hideout.core.domain.model.instance
interface InstanceRepository { interface InstanceRepository {
suspend fun generateId(): Long suspend fun generateId(): Long
suspend fun save(instance: Instance): Instance 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 delete(instance: Instance)
} }

View File

@ -3,6 +3,6 @@ package dev.usbharu.hideout.core.domain.model.media
interface MediaRepository { interface MediaRepository {
suspend fun generateId(): Long suspend fun generateId(): Long
suspend fun save(media: Media): Media 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 delete(id: Long)
} }

View File

@ -1,23 +1,19 @@
package dev.usbharu.hideout.core.infrastructure.exposedquery 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.domain.model.deletedActor.DeletedActor
import dev.usbharu.hideout.core.infrastructure.exposedrepository.DeletedActors import dev.usbharu.hideout.core.infrastructure.exposedrepository.DeletedActors
import dev.usbharu.hideout.core.infrastructure.exposedrepository.toDeletedActor import dev.usbharu.hideout.core.infrastructure.exposedrepository.toDeletedActor
import dev.usbharu.hideout.core.query.DeletedActorQueryService import dev.usbharu.hideout.core.query.DeletedActorQueryService
import dev.usbharu.hideout.util.singleOr
import org.jetbrains.exposed.sql.and import org.jetbrains.exposed.sql.and
import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.select
import org.springframework.stereotype.Repository import org.springframework.stereotype.Repository
@Repository @Repository
class DeletedActorQueryServiceImpl : DeletedActorQueryService { class DeletedActorQueryServiceImpl : DeletedActorQueryService {
override suspend fun findByNameAndDomain(name: String, domain: String): DeletedActor { override suspend fun findByNameAndDomain(name: String, domain: String): DeletedActor? {
return DeletedActors return DeletedActors
.select { DeletedActors.name eq name and (DeletedActors.domain eq domain) } .select { DeletedActors.name eq name and (DeletedActors.domain eq domain) }
.singleOr { .singleOrNull()
FailedToGetResourcesException("name: $name domain: $domain was not exist or duplicate.", it) ?.toDeletedActor()
}
.toDeletedActor()
} }
} }

View File

@ -1,16 +1,14 @@
package dev.usbharu.hideout.core.infrastructure.exposedquery 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.Instance
import dev.usbharu.hideout.core.infrastructure.exposedrepository.toInstance import dev.usbharu.hideout.core.infrastructure.exposedrepository.toInstance
import dev.usbharu.hideout.core.query.InstanceQueryService import dev.usbharu.hideout.core.query.InstanceQueryService
import dev.usbharu.hideout.util.singleOr
import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.select
import org.springframework.stereotype.Repository import org.springframework.stereotype.Repository
import dev.usbharu.hideout.core.domain.model.instance.Instance as InstanceEntity import dev.usbharu.hideout.core.domain.model.instance.Instance as InstanceEntity
@Repository @Repository
class InstanceQueryServiceImpl : InstanceQueryService { class InstanceQueryServiceImpl : InstanceQueryService {
override suspend fun findByUrl(url: String): InstanceEntity = Instance.select { Instance.url eq url } override suspend fun findByUrl(url: String): InstanceEntity? = Instance.select { Instance.url eq url }
.singleOr { FailedToGetResourcesException("$url is doesn't exist", it) }.toInstance() .singleOrNull()?.toInstance()
} }

View File

@ -1,11 +1,9 @@
package dev.usbharu.hideout.core.infrastructure.exposedquery 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.Media
import dev.usbharu.hideout.core.infrastructure.exposedrepository.PostsMedia import dev.usbharu.hideout.core.infrastructure.exposedrepository.PostsMedia
import dev.usbharu.hideout.core.infrastructure.exposedrepository.toMedia import dev.usbharu.hideout.core.infrastructure.exposedrepository.toMedia
import dev.usbharu.hideout.core.query.MediaQueryService import dev.usbharu.hideout.core.query.MediaQueryService
import dev.usbharu.hideout.util.singleOr
import org.jetbrains.exposed.sql.innerJoin import org.jetbrains.exposed.sql.innerJoin
import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.select
import org.springframework.stereotype.Repository import org.springframework.stereotype.Repository
@ -23,9 +21,9 @@ class MediaQueryServiceImpl : MediaQueryService {
.map { it.toMedia() } .map { it.toMedia() }
} }
override suspend fun findByRemoteUrl(remoteUrl: String): MediaEntity { override suspend fun findByRemoteUrl(remoteUrl: String): MediaEntity? {
return Media.select { Media.remoteUrl eq remoteUrl }.forUpdate() return Media.select { Media.remoteUrl eq remoteUrl }.forUpdate()
.singleOr { FailedToGetResourcesException("remoteUrl: $remoteUrl is duplicate or not exist.", it) } .singleOrNull()
.toMedia() ?.toMedia()
} }
} }

View File

@ -2,12 +2,10 @@ package dev.usbharu.hideout.core.infrastructure.exposedquery
import dev.usbharu.hideout.application.infrastructure.exposed.QueryMapper import dev.usbharu.hideout.application.infrastructure.exposed.QueryMapper
import dev.usbharu.hideout.application.infrastructure.exposed.ResultRowMapper 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.domain.model.post.Post
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts
import dev.usbharu.hideout.core.infrastructure.exposedrepository.PostsMedia import dev.usbharu.hideout.core.infrastructure.exposedrepository.PostsMedia
import dev.usbharu.hideout.core.query.PostQueryService import dev.usbharu.hideout.core.query.PostQueryService
import dev.usbharu.hideout.util.singleOr
import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.select
import org.springframework.stereotype.Repository import org.springframework.stereotype.Repository
@ -16,23 +14,23 @@ class PostQueryServiceImpl(
private val postResultRowMapper: ResultRowMapper<Post>, private val postResultRowMapper: ResultRowMapper<Post>,
private val postQueryMapper: QueryMapper<Post> private val postQueryMapper: QueryMapper<Post>
) : PostQueryService { ) : PostQueryService {
override suspend fun findById(id: Long): Post = override suspend fun findById(id: Long): Post? =
Posts.leftJoin(PostsMedia) Posts.leftJoin(PostsMedia)
.select { Posts.id eq id } .select { Posts.id eq id }
.singleOr { FailedToGetResourcesException("id: $id is duplicate or does not exist.", it) } .singleOrNull()
.let(postResultRowMapper::map) ?.let(postResultRowMapper::map)
override suspend fun findByUrl(url: String): Post = override suspend fun findByUrl(url: String): Post? =
Posts.leftJoin(PostsMedia) Posts.leftJoin(PostsMedia)
.select { Posts.url eq url } .select { Posts.url eq url }
.let(postQueryMapper::map) .let(postQueryMapper::map)
.singleOr { FailedToGetResourcesException("url: $url is duplicate or does not exist.", it) } .singleOrNull()
override suspend fun findByApId(string: String): Post = override suspend fun findByApId(string: String): Post? =
Posts.leftJoin(PostsMedia) Posts.leftJoin(PostsMedia)
.select { Posts.apId eq string } .select { Posts.apId eq string }
.let(postQueryMapper::map) .let(postQueryMapper::map)
.singleOr { FailedToGetResourcesException("apId: $string is duplicate or does not exist.", it) } .singleOrNull()
override suspend fun findByActorId(actorId: Long): List<Post> = override suspend fun findByActorId(actorId: Long): List<Post> =
Posts.leftJoin(PostsMedia).select { Posts.actorId eq actorId }.let(postQueryMapper::map) Posts.leftJoin(PostsMedia).select { Posts.actorId eq actorId }.let(postQueryMapper::map)

View File

@ -1,11 +1,9 @@
package dev.usbharu.hideout.core.infrastructure.exposedquery 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.domain.model.reaction.Reaction
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Reactions import dev.usbharu.hideout.core.infrastructure.exposedrepository.Reactions
import dev.usbharu.hideout.core.infrastructure.exposedrepository.toReaction import dev.usbharu.hideout.core.infrastructure.exposedrepository.toReaction
import dev.usbharu.hideout.core.query.ReactionQueryService import dev.usbharu.hideout.core.query.ReactionQueryService
import dev.usbharu.hideout.util.singleOr
import org.jetbrains.exposed.sql.and import org.jetbrains.exposed.sql.and
import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.select
import org.springframework.stereotype.Repository import org.springframework.stereotype.Repository
@ -19,20 +17,15 @@ class ReactionQueryServiceImpl : ReactionQueryService {
} }
@Suppress("FunctionMaxLength") @Suppress("FunctionMaxLength")
override suspend fun findByPostIdAndActorIdAndEmojiId(postId: Long, actorId: Long, emojiId: Long): Reaction { override suspend fun findByPostIdAndActorIdAndEmojiId(postId: Long, actorId: Long, emojiId: Long): Reaction? {
return Reactions return Reactions
.select { .select {
Reactions.postId.eq(postId).and(Reactions.actorId.eq(actorId)).and( Reactions.postId.eq(postId).and(Reactions.actorId.eq(actorId)).and(
Reactions.emojiId.eq(emojiId) Reactions.emojiId.eq(emojiId)
) )
} }
.singleOr { .singleOrNull()
FailedToGetResourcesException( ?.toReaction()
"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 { override suspend fun reactionAlreadyExist(postId: Long, actorId: Long, emojiId: Long): Boolean {

View File

@ -1,18 +1,18 @@
package dev.usbharu.hideout.core.infrastructure.exposedrepository 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.DeletedActor
import dev.usbharu.hideout.core.domain.model.deletedActor.DeletedActorRepository 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.*
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.jetbrains.exposed.sql.javatime.timestamp import org.jetbrains.exposed.sql.javatime.timestamp
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Repository import org.springframework.stereotype.Repository
@Repository @Repository
class DeletedActorRepositoryImpl : DeletedActorRepository { class DeletedActorRepositoryImpl : DeletedActorRepository, AbstractRepository() {
override suspend fun save(deletedActor: DeletedActor): DeletedActor { override suspend fun save(deletedActor: DeletedActor): DeletedActor = query {
val singleOrNull = DeletedActors.select { DeletedActors.id eq deletedActor.id }.singleOrNull() val singleOrNull = DeletedActors.select { DeletedActors.id eq deletedActor.id }.forUpdate().singleOrNull()
if (singleOrNull == null) { if (singleOrNull == null) {
DeletedActors.insert { DeletedActors.insert {
@ -30,18 +30,24 @@ class DeletedActorRepositoryImpl : DeletedActorRepository {
it[DeletedActors.deletedAt] = deletedActor.deletedAt 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 } DeletedActors.deleteWhere { DeletedActors.id eq deletedActor.id }
} }
override suspend fun findById(id: Long): DeletedActor { override suspend fun findById(id: Long): DeletedActor? = query {
val singleOr = DeletedActors.select { DeletedActors.id eq id } return@query DeletedActors.select { DeletedActors.id eq id }
.singleOr { FailedToGetResourcesException("id: $id was not exist or duplicate", it) } .singleOrNull()
?.let { it.toDeletedActor() }
}
return deletedActor(singleOr) override val logger: Logger
get() = Companion.logger
companion object {
private val logger = LoggerFactory.getLogger(DeletedActorRepositoryImpl::class.java)
} }
} }

View File

@ -1,9 +1,7 @@
package dev.usbharu.hideout.core.infrastructure.exposedrepository package dev.usbharu.hideout.core.infrastructure.exposedrepository
import dev.usbharu.hideout.application.service.id.IdGenerateService 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.core.domain.model.instance.InstanceRepository
import dev.usbharu.hideout.util.singleOr
import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.jetbrains.exposed.sql.javatime.timestamp import org.jetbrains.exposed.sql.javatime.timestamp
@ -48,9 +46,9 @@ class InstanceRepositoryImpl(private val idGenerateService: IdGenerateService) :
return instance return instance
} }
override suspend fun findById(id: Long): InstanceEntity { override suspend fun findById(id: Long): InstanceEntity? {
return Instance.select { Instance.id eq id } return Instance.select { Instance.id eq id }
.singleOr { FailedToGetResourcesException("id: $id doesn't exist.") }.toInstance() .singleOrNull()?.toInstance()
} }
override suspend fun delete(instance: InstanceEntity) { override suspend fun delete(instance: InstanceEntity) {

View File

@ -1,12 +1,10 @@
package dev.usbharu.hideout.core.infrastructure.exposedrepository package dev.usbharu.hideout.core.infrastructure.exposedrepository
import dev.usbharu.hideout.application.service.id.IdGenerateService 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.domain.model.media.MediaRepository
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Media.mimeType import dev.usbharu.hideout.core.infrastructure.exposedrepository.Media.mimeType
import dev.usbharu.hideout.core.service.media.FileType import dev.usbharu.hideout.core.service.media.FileType
import dev.usbharu.hideout.core.service.media.MimeType import dev.usbharu.hideout.core.service.media.MimeType
import dev.usbharu.hideout.util.singleOr
import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.springframework.stereotype.Repository import org.springframework.stereotype.Repository
@ -47,14 +45,13 @@ class MediaRepositoryImpl(private val idGenerateService: IdGenerateService) : Me
return media return media
} }
override suspend fun findById(id: Long): EntityMedia { override suspend fun findById(id: Long): EntityMedia? {
return Media return Media
.select { .select {
Media.id eq id Media.id eq id
} }
.singleOr { .singleOrNull()
FailedToGetResourcesException("id: $id was not found.") ?.toMedia()
}.toMedia()
} }
override suspend fun delete(id: Long) { override suspend fun delete(id: Long) {

View File

@ -3,5 +3,5 @@ package dev.usbharu.hideout.core.query
import dev.usbharu.hideout.core.domain.model.deletedActor.DeletedActor import dev.usbharu.hideout.core.domain.model.deletedActor.DeletedActor
interface DeletedActorQueryService { interface DeletedActorQueryService {
suspend fun findByNameAndDomain(name: String, domain: String): DeletedActor suspend fun findByNameAndDomain(name: String, domain: String): DeletedActor?
} }

View File

@ -3,5 +3,5 @@ package dev.usbharu.hideout.core.query
import dev.usbharu.hideout.core.domain.model.instance.Instance import dev.usbharu.hideout.core.domain.model.instance.Instance
interface InstanceQueryService { interface InstanceQueryService {
suspend fun findByUrl(url: String): Instance suspend fun findByUrl(url: String): Instance?
} }

View File

@ -4,5 +4,5 @@ import dev.usbharu.hideout.core.domain.model.media.Media
interface MediaQueryService { interface MediaQueryService {
suspend fun findByPostId(postId: Long): List<Media> suspend fun findByPostId(postId: Long): List<Media>
suspend fun findByRemoteUrl(remoteUrl: String): Media suspend fun findByRemoteUrl(remoteUrl: String): Media?
} }

View File

@ -5,8 +5,8 @@ import org.springframework.stereotype.Repository
@Repository @Repository
interface PostQueryService { interface PostQueryService {
suspend fun findById(id: Long): Post suspend fun findById(id: Long): Post?
suspend fun findByUrl(url: String): Post suspend fun findByUrl(url: String): Post?
suspend fun findByApId(string: String): Post suspend fun findByApId(string: String): Post?
suspend fun findByActorId(actorId: Long): List<Post> suspend fun findByActorId(actorId: Long): List<Post>
} }

View File

@ -8,7 +8,7 @@ interface ReactionQueryService {
suspend fun findByPostId(postId: Long, actorId: Long? = null): List<Reaction> suspend fun findByPostId(postId: Long, actorId: Long? = null): List<Reaction>
@Suppress("FunctionMaxLength") @Suppress("FunctionMaxLength")
suspend fun findByPostIdAndActorIdAndEmojiId(postId: Long, actorId: Long, emojiId: Long): Reaction suspend fun findByPostIdAndActorIdAndEmojiId(postId: Long, actorId: Long, emojiId: Long): Reaction?
suspend fun reactionAlreadyExist(postId: Long, actorId: Long, emojiId: Long): Boolean suspend fun reactionAlreadyExist(postId: Long, actorId: Long, emojiId: Long): Boolean

View File

@ -1,7 +1,6 @@
package dev.usbharu.hideout.core.service.instance package dev.usbharu.hideout.core.service.instance
import com.fasterxml.jackson.databind.ObjectMapper 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.Instance
import dev.usbharu.hideout.core.domain.model.instance.InstanceRepository 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.Nodeinfo
@ -30,13 +29,14 @@ class InstanceServiceImpl(
val u = URL(url) val u = URL(url)
val resolveInstanceUrl = u.protocol + "://" + u.host val resolveInstanceUrl = u.protocol + "://" + u.host
try { val instance = instanceQueryService.findByUrl(resolveInstanceUrl)
return instanceQueryService.findByUrl(resolveInstanceUrl)
} catch (e: FailedToGetResourcesException) { if (instance != null) {
logger.info("Instance not found. try fetch instance info. url: {}", resolveInstanceUrl) return instance
logger.debug("Failed to get resources. url: {}", resolveInstanceUrl, e)
} }
logger.info("Instance not found. try fetch instance info. url: {}", resolveInstanceUrl)
val nodeinfoJson = resourceResolveService.resolve("$resolveInstanceUrl/.well-known/nodeinfo").bodyAsText() val nodeinfoJson = resourceResolveService.resolve("$resolveInstanceUrl/.well-known/nodeinfo").bodyAsText()
val nodeinfo = objectMapper.readValue(nodeinfoJson, Nodeinfo::class.java) val nodeinfo = objectMapper.readValue(nodeinfoJson, Nodeinfo::class.java)
val nodeinfoPathMap = nodeinfo.links.associate { it.rel to it.href } val nodeinfoPathMap = nodeinfo.links.associate { it.rel to it.href }

View File

@ -1,6 +1,5 @@
package dev.usbharu.hideout.core.service.media 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.MediaSaveException
import dev.usbharu.hideout.core.domain.exception.media.UnsupportedMediaException 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.Media
@ -102,11 +101,11 @@ class MediaServiceImpl(
override suspend fun uploadRemoteMedia(remoteMedia: RemoteMedia): Media { override suspend fun uploadRemoteMedia(remoteMedia: RemoteMedia): Media {
logger.info("MEDIA Remote media. filename:${remoteMedia.name} url:${remoteMedia.url}") logger.info("MEDIA Remote media. filename:${remoteMedia.name} url:${remoteMedia.url}")
try {
val findByRemoteUrl = mediaQueryService.findByRemoteUrl(remoteMedia.url) val findByRemoteUrl = mediaQueryService.findByRemoteUrl(remoteMedia.url)
if (findByRemoteUrl != null) {
logger.warn("DUPLICATED Remote media is duplicated. url: {}", remoteMedia.url) logger.warn("DUPLICATED Remote media is duplicated. url: {}", remoteMedia.url)
return findByRemoteUrl return findByRemoteUrl
} catch (_: FailedToGetResourcesException) {
} }
remoteMediaDownloadService.download(remoteMedia.url).withDelete().use { remoteMediaDownloadService.download(remoteMedia.url).withDelete().use {

View File

@ -1,7 +1,6 @@
package dev.usbharu.hideout.core.service.reaction package dev.usbharu.hideout.core.service.reaction
import dev.usbharu.hideout.activitypub.service.activity.like.APReactionService 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.Reaction
import dev.usbharu.hideout.core.domain.model.reaction.ReactionRepository import dev.usbharu.hideout.core.domain.model.reaction.ReactionRepository
import dev.usbharu.hideout.core.query.ReactionQueryService import dev.usbharu.hideout.core.query.ReactionQueryService
@ -29,30 +28,38 @@ class ReactionServiceImpl(
override suspend fun receiveRemoveReaction(actorId: Long, postId: Long) { override suspend fun receiveRemoveReaction(actorId: Long, postId: Long) {
val reaction = reactionQueryService.findByPostIdAndActorIdAndEmojiId(postId, actorId, 0) val reaction = reactionQueryService.findByPostIdAndActorIdAndEmojiId(postId, actorId, 0)
if (reaction == null) {
LOGGER.warn("FAILED receive Remove Reaction. $actorId $postId")
return
}
reactionRepository.delete(reaction) reactionRepository.delete(reaction)
} }
override suspend fun sendReaction(name: String, actorId: Long, postId: Long) { override suspend fun sendReaction(name: String, actorId: Long, postId: Long) {
try { val findByPostIdAndUserIdAndEmojiId =
val findByPostIdAndUserIdAndEmojiId = reactionQueryService.findByPostIdAndActorIdAndEmojiId(postId, actorId, 0)
reactionQueryService.findByPostIdAndActorIdAndEmojiId(postId, actorId, 0)
apReactionService.removeReaction(findByPostIdAndUserIdAndEmojiId) if (findByPostIdAndUserIdAndEmojiId == null) {
reactionRepository.delete(findByPostIdAndUserIdAndEmojiId) LOGGER.warn("FAILED Send reaction. $postId $actorId")
} catch (_: FailedToGetResourcesException) { return
} }
apReactionService.removeReaction(findByPostIdAndUserIdAndEmojiId)
reactionRepository.delete(findByPostIdAndUserIdAndEmojiId)
val reaction = Reaction(reactionRepository.generateId(), 0, postId, actorId) val reaction = Reaction(reactionRepository.generateId(), 0, postId, actorId)
reactionRepository.save(reaction) reactionRepository.save(reaction)
apReactionService.reaction(reaction) apReactionService.reaction(reaction)
} }
override suspend fun removeReaction(actorId: Long, postId: Long) { override suspend fun removeReaction(actorId: Long, postId: Long) {
try { val findByPostIdAndUserIdAndEmojiId =
val findByPostIdAndUserIdAndEmojiId = reactionQueryService.findByPostIdAndActorIdAndEmojiId(postId, actorId, 0)
reactionQueryService.findByPostIdAndActorIdAndEmojiId(postId, actorId, 0) if (findByPostIdAndUserIdAndEmojiId == null) {
reactionRepository.delete(findByPostIdAndUserIdAndEmojiId) LOGGER.warn("FAILED Remove reaction. actorId: $actorId postId: $postId")
apReactionService.removeReaction(findByPostIdAndUserIdAndEmojiId) return
} catch (_: FailedToGetResourcesException) {
} }
reactionRepository.delete(findByPostIdAndUserIdAndEmojiId)
apReactionService.removeReaction(findByPostIdAndUserIdAndEmojiId)
} }
companion object { companion object {

View File

@ -2,7 +2,6 @@ package dev.usbharu.hideout.core.service.user
import dev.usbharu.hideout.activitypub.service.activity.delete.APSendDeleteService import dev.usbharu.hideout.activitypub.service.activity.delete.APSendDeleteService
import dev.usbharu.hideout.application.config.ApplicationConfig 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.DuplicateException
import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException 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.Actor
@ -74,11 +73,11 @@ class UserServiceImpl(
override suspend fun createRemoteUser(user: RemoteUserCreateDto): Actor { override suspend fun createRemoteUser(user: RemoteUserCreateDto): Actor {
logger.info("START Create New remote user. name: {} url: {}", user.name, user.url) logger.info("START Create New remote user. name: {} url: {}", user.name, user.url)
try { val deletedActor = deletedActorQueryService.findByNameAndDomain(user.name, user.domain)
deletedActorQueryService.findByNameAndDomain(user.name, user.domain)
if (deletedActor != null) {
logger.warn("FAILED Deleted actor. user: ${user.name} domain: ${user.domain}") logger.warn("FAILED Deleted actor. user: ${user.name} domain: ${user.domain}")
throw IllegalStateException("Cannot create Deleted actor.") throw IllegalStateException("Cannot create Deleted actor.")
} catch (_: FailedToGetResourcesException) {
} }
@Suppress("TooGenericExceptionCaught") @Suppress("TooGenericExceptionCaught")

View File

@ -1,11 +1,9 @@
package dev.usbharu.hideout.mastodon.infrastructure.exposedquery package dev.usbharu.hideout.mastodon.infrastructure.exposedquery
import dev.usbharu.hideout.application.config.ApplicationConfig 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.core.infrastructure.exposedrepository.Actors
import dev.usbharu.hideout.domain.mastodon.model.generated.Account import dev.usbharu.hideout.domain.mastodon.model.generated.Account
import dev.usbharu.hideout.mastodon.query.AccountQueryService import dev.usbharu.hideout.mastodon.query.AccountQueryService
import dev.usbharu.hideout.util.singleOr
import org.jetbrains.exposed.sql.ResultRow import org.jetbrains.exposed.sql.ResultRow
import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.select
import org.springframework.stereotype.Repository import org.springframework.stereotype.Repository
@ -13,12 +11,12 @@ import java.time.Instant
@Repository @Repository
class AccountQueryServiceImpl(private val applicationConfig: ApplicationConfig) : AccountQueryService { 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 } val query = Actors.select { Actors.id eq accountId }
return query return query
.singleOr { FailedToGetResourcesException("accountId: $accountId wad not exist or duplicate", it) } .singleOrNull()
.let { toAccount(it) } ?.let { toAccount(it) }
} }
override suspend fun findByIds(accountIds: List<Long>): List<Account> { override suspend fun findByIds(accountIds: List<Long>): List<Account> {

View File

@ -3,6 +3,6 @@ package dev.usbharu.hideout.mastodon.query
import dev.usbharu.hideout.domain.mastodon.model.generated.Account import dev.usbharu.hideout.domain.mastodon.model.generated.Account
interface AccountQueryService { interface AccountQueryService {
suspend fun findById(accountId: Long): Account suspend fun findById(accountId: Long): Account?
suspend fun findByIds(accountIds: List<Long>): List<Account> suspend fun findByIds(accountIds: List<Long>): List<Account>
} }

View File

@ -14,7 +14,8 @@ interface AccountService {
class AccountServiceImpl( class AccountServiceImpl(
private val accountQueryService: AccountQueryService private val accountQueryService: AccountQueryService
) : AccountService { ) : 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<Long>): List<Account> = accountQueryService.findByIds(ids) override suspend fun findByIds(ids: List<Long>): List<Account> = accountQueryService.findByIds(ids)
} }

View File

@ -53,13 +53,19 @@ class StatsesApiServiceImpl(
val account = accountService.findById(userId) val account = accountService.findById(userId)
val replyUser = if (post.replyId != null) { val replyUser = if (post.replyId != null) {
actorRepository.findById(postQueryService.findById(post.replyId).actorId)?.id val findById = postQueryService.findById(post.replyId)
if (findById == null) {
null
} else {
actorRepository.findById(findById.actorId)?.id
}
} else { } else {
null null
} }
// TODO: n+1解消 // TODO: n+1解消
val mediaAttachment = post.mediaIds.map { mediaId -> val mediaAttachment = post.mediaIds.mapNotNull { mediaId ->
mediaRepository.findById(mediaId) mediaRepository.findById(mediaId)
}.map { }.map {
it.toMediaAttachments() it.toMediaAttachments()