Merge pull request #177 from usbharu/bugfix/duplicate-media

Bugfix/duplicate media
This commit is contained in:
usbharu 2023-11-30 01:59:52 +09:00 committed by GitHub
commit 6af7cd67c8
9 changed files with 40 additions and 22 deletions

View File

@ -55,7 +55,7 @@ class WebFingerTest {
@Test @Test
fun `webfinger 存在しないユーザーに404`() { fun `webfinger 存在しないユーザーに404`() {
mockMvc mockMvc
.get("/.well-known/webfinger?resource=acct:test-user@example.com") .get("/.well-known/webfinger?resource=acct:invalid-user-notfound-afdjashfal@example.com")
.andExpect { status { isNotFound() } } .andExpect { status { isNotFound() } }
} }

View File

@ -52,7 +52,7 @@ class MediaTest {
@Test @Test
fun メディアをアップロードできる() = runTest { fun メディアをアップロードできる() = runTest {
whenever(mediaDataStore.save(any<MediaSaveRequest>())).doReturn(SuccessSavedMedia("", "", "")) whenever(mediaDataStore.save(any<MediaSaveRequest>())).doReturn(SuccessSavedMedia("", "a", "a"))
mockMvc mockMvc
.multipart("/api/v1/media") { .multipart("/api/v1/media") {
@ -73,7 +73,7 @@ class MediaTest {
@Test @Test
fun write_mediaスコープでメディアをアップロードできる() = runTest { fun write_mediaスコープでメディアをアップロードできる() = runTest {
whenever(mediaDataStore.save(any<MediaSaveRequest>())).doReturn(SuccessSavedMedia("", "", "")) whenever(mediaDataStore.save(any<MediaSaveRequest>())).doReturn(SuccessSavedMedia("", "b", "b"))
mockMvc mockMvc
.multipart("/api/v1/media") { .multipart("/api/v1/media") {

View File

@ -0,0 +1,2 @@
junit.jupiter.testclass.order.default=org.junit.jupiter.api.ClassOrderer$Random
junit.jupiter.testmethod.order.default=org.junit.jupiter.api.MethodOrderer$Random

View File

@ -1,17 +1,20 @@
package dev.usbharu.hideout.core.infrastructure.exposedquery package dev.usbharu.hideout.core.infrastructure.exposedquery
import dev.usbharu.hideout.core.domain.model.media.Media 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.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
import dev.usbharu.hideout.core.domain.model.media.Media as MediaEntity
@Repository @Repository
class MediaQueryServiceImpl : MediaQueryService { class MediaQueryServiceImpl : MediaQueryService {
override suspend fun findByPostId(postId: Long): List<Media> { override suspend fun findByPostId(postId: Long): List<MediaEntity> {
return dev.usbharu.hideout.core.infrastructure.exposedrepository.Media.innerJoin( return Media.innerJoin(
PostsMedia, PostsMedia,
onColumn = { id }, onColumn = { id },
otherColumn = { mediaId } otherColumn = { mediaId }
@ -19,4 +22,10 @@ class MediaQueryServiceImpl : MediaQueryService {
.select { PostsMedia.postId eq postId } .select { PostsMedia.postId eq postId }
.map { it.toMedia() } .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()
}
} }

View File

@ -99,9 +99,9 @@ fun ResultRow.toMediaOrNull(): EntityMedia? {
object Media : Table("media") { object Media : Table("media") {
val id = long("id") val id = long("id")
val name = varchar("name", 255) val name = varchar("name", 255)
val url = varchar("url", 255) val url = varchar("url", 255).uniqueIndex()
val remoteUrl = varchar("remote_url", 255).nullable() val remoteUrl = varchar("remote_url", 255).uniqueIndex().nullable()
val thumbnailUrl = varchar("thumbnail_url", 255).nullable() val thumbnailUrl = varchar("thumbnail_url", 255).uniqueIndex().nullable()
val type = integer("type") val type = integer("type")
val blurhash = varchar("blurhash", 255).nullable() val blurhash = varchar("blurhash", 255).nullable()
val mimeType = varchar("mime_type", 255) val mimeType = varchar("mime_type", 255)

View File

@ -4,4 +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
} }

View File

@ -1,9 +1,11 @@
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
import dev.usbharu.hideout.core.domain.model.media.MediaRepository 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.core.service.media.converter.MediaProcessService
import dev.usbharu.hideout.mastodon.interfaces.api.media.MediaRequest import dev.usbharu.hideout.mastodon.interfaces.api.media.MediaRequest
import dev.usbharu.hideout.util.withDelete import dev.usbharu.hideout.util.withDelete
@ -22,7 +24,8 @@ class MediaServiceImpl(
private val mediaRepository: MediaRepository, private val mediaRepository: MediaRepository,
private val mediaProcessServices: List<MediaProcessService>, private val mediaProcessServices: List<MediaProcessService>,
private val remoteMediaDownloadService: RemoteMediaDownloadService, private val remoteMediaDownloadService: RemoteMediaDownloadService,
private val renameService: MediaFileRenameService private val renameService: MediaFileRenameService,
private val mediaQueryService: MediaQueryService
) : MediaService { ) : MediaService {
@Suppress("LongMethod", "NestedBlockDepth") @Suppress("LongMethod", "NestedBlockDepth")
override suspend fun uploadLocalMedia(mediaRequest: MediaRequest): EntityMedia { override suspend fun uploadLocalMedia(mediaRequest: MediaRequest): EntityMedia {
@ -99,6 +102,13 @@ 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)
logger.warn("DUPLICATED Remote media is duplicated. url: {}", remoteMedia.url)
return findByRemoteUrl
} catch (_: FailedToGetResourcesException) {
}
remoteMediaDownloadService.download(remoteMedia.url).withDelete().use { remoteMediaDownloadService.download(remoteMedia.url).withDelete().use {
val mimeType = fileTypeDeterminationService.fileType(it.path, remoteMedia.name) val mimeType = fileTypeDeterminationService.fileType(it.path, remoteMedia.name)

View File

@ -1,10 +1,6 @@
package dev.usbharu.hideout.core.service.media package dev.usbharu.hideout.core.service.media
import io.ktor.client.* import dev.usbharu.hideout.core.service.resource.KtorResourceResolveService
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.http.*
import io.ktor.utils.io.jvm.javaio.*
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
import java.nio.file.Files import java.nio.file.Files
@ -12,16 +8,16 @@ import java.nio.file.Path
import kotlin.io.path.outputStream import kotlin.io.path.outputStream
@Service @Service
class RemoteMediaDownloadServiceImpl(private val httpClient: HttpClient) : RemoteMediaDownloadService { class RemoteMediaDownloadServiceImpl(private val resourceResolveService: KtorResourceResolveService) :
RemoteMediaDownloadService {
override suspend fun download(url: String): Path { override suspend fun download(url: String): Path {
logger.info("START Download remote file. url: {}", url) logger.info("START Download remote file. url: {}", url)
val httpResponse = httpClient.get(url) val httpResponse = resourceResolveService.resolve(url).body()
httpResponse.contentLength()
val createTempFile = Files.createTempFile("hideout-remote-download", ".tmp") val createTempFile = Files.createTempFile("hideout-remote-download", ".tmp")
logger.debug("Save to {} url: {} ", createTempFile, url) logger.debug("Save to {} url: {} ", createTempFile, url)
httpResponse.bodyAsChannel().toInputStream().use { inputStream -> httpResponse.use { inputStream ->
createTempFile.outputStream().use { createTempFile.outputStream().use {
inputStream.transferTo(it) inputStream.transferTo(it)
} }

View File

@ -46,9 +46,9 @@ create table if not exists media
( (
id bigint primary key, id bigint primary key,
"name" varchar(255) not null, "name" varchar(255) not null,
url varchar(255) not null, url varchar(255) not null unique,
remote_url varchar(255) null, remote_url varchar(255) null unique,
thumbnail_url varchar(255) null, thumbnail_url varchar(255) null unique,
"type" int not null, "type" int not null,
blurhash varchar(255) null, blurhash varchar(255) null,
mime_type varchar(255) not null, mime_type varchar(255) not null,