From 216ee78da0457674064f5830c780d9b4298e3082 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Tue, 10 Oct 2023 16:07:21 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=E3=83=A1=E3=83=87=E3=82=A3=E3=82=A2?= =?UTF-8?q?=E4=BB=98=E3=81=8D=E6=8A=95=E7=A8=BF=E3=82=92=E9=85=8D=E9=80=81?= =?UTF-8?q?=E5=8F=AF=E8=83=BD=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hideout/domain/model/ap/Document.kt | 48 +++++++++++++++++++ .../usbharu/hideout/domain/model/ap/Note.kt | 27 ++++++++--- .../domain/model/ap/ObjectDeserializer.kt | 2 +- .../hideout/domain/model/job/HideoutJob.kt | 1 + .../hideout/query/MediaQueryService.kt | 7 +++ .../hideout/query/MediaQueryServiceImpl.kt | 17 +++++++ .../hideout/service/ap/APNoteService.kt | 11 ++++- 7 files changed, 105 insertions(+), 8 deletions(-) create mode 100644 src/main/kotlin/dev/usbharu/hideout/domain/model/ap/Document.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/query/MediaQueryService.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/query/MediaQueryServiceImpl.kt diff --git a/src/main/kotlin/dev/usbharu/hideout/domain/model/ap/Document.kt b/src/main/kotlin/dev/usbharu/hideout/domain/model/ap/Document.kt new file mode 100644 index 00000000..693c6405 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/domain/model/ap/Document.kt @@ -0,0 +1,48 @@ +package dev.usbharu.hideout.domain.model.ap + +class Document : Object { + + var mediaType: String? = null + var url: String? = null + + protected constructor() : super() + constructor( + type: List = emptyList(), + name: String? = null, + mediaType: String, + url: String + ) : super( + type = add(type, "Document"), + name = name, + actor = null, + id = null + ) { + this.mediaType = mediaType + this.url = url + } + + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is Document) return false + if (!super.equals(other)) return false + + if (mediaType != other.mediaType) return false + if (url != other.url) return false + + return true + } + + override fun hashCode(): Int { + var result = super.hashCode() + result = 31 * result + (mediaType?.hashCode() ?: 0) + result = 31 * result + (url?.hashCode() ?: 0) + return result + } + + override fun toString(): String { + return "Document(mediaType=$mediaType, url=$url) ${super.toString()}" + } + + +} diff --git a/src/main/kotlin/dev/usbharu/hideout/domain/model/ap/Note.kt b/src/main/kotlin/dev/usbharu/hideout/domain/model/ap/Note.kt index 21c2f25c..7ca19a0d 100644 --- a/src/main/kotlin/dev/usbharu/hideout/domain/model/ap/Note.kt +++ b/src/main/kotlin/dev/usbharu/hideout/domain/model/ap/Note.kt @@ -2,6 +2,7 @@ package dev.usbharu.hideout.domain.model.ap open class Note : Object { var attributedTo: String? = null + var attachment: List = emptyList() var content: String? = null var published: String? = null var to: List = emptyList() @@ -22,7 +23,8 @@ open class Note : Object { to: List = emptyList(), cc: List = emptyList(), sensitive: Boolean = false, - inReplyTo: String? = null + inReplyTo: String? = null, + attachment: List = emptyList() ) : super( type = add(type, "Note"), name = name, @@ -35,30 +37,43 @@ open class Note : Object { this.cc = cc this.sensitive = sensitive this.inReplyTo = inReplyTo + this.attachment = attachment } + override fun equals(other: Any?): Boolean { if (this === other) return true if (other !is Note) return false if (!super.equals(other)) return false - if (id != other.id) return false if (attributedTo != other.attributedTo) return false + if (attachment != other.attachment) return false if (content != other.content) return false if (published != other.published) return false - return to == other.to + if (to != other.to) return false + if (cc != other.cc) return false + if (sensitive != other.sensitive) return false + if (inReplyTo != other.inReplyTo) return false + + return true } override fun hashCode(): Int { var result = super.hashCode() - result = 31 * result + (id?.hashCode() ?: 0) result = 31 * result + (attributedTo?.hashCode() ?: 0) + result = 31 * result + attachment.hashCode() result = 31 * result + (content?.hashCode() ?: 0) result = 31 * result + (published?.hashCode() ?: 0) result = 31 * result + to.hashCode() + result = 31 * result + cc.hashCode() + result = 31 * result + sensitive.hashCode() + result = 31 * result + (inReplyTo?.hashCode() ?: 0) return result } - override fun toString(): String = - "Note(id=$id, attributedTo=$attributedTo, content=$content, published=$published, to=$to) ${super.toString()}" + override fun toString(): String { + return "Note(attributedTo=$attributedTo, attachment=$attachment, content=$content, published=$published, to=$to, cc=$cc, sensitive=$sensitive, inReplyTo=$inReplyTo) ${super.toString()}" + } + + } diff --git a/src/main/kotlin/dev/usbharu/hideout/domain/model/ap/ObjectDeserializer.kt b/src/main/kotlin/dev/usbharu/hideout/domain/model/ap/ObjectDeserializer.kt index 66af888a..72fabd37 100644 --- a/src/main/kotlin/dev/usbharu/hideout/domain/model/ap/ObjectDeserializer.kt +++ b/src/main/kotlin/dev/usbharu/hideout/domain/model/ap/ObjectDeserializer.kt @@ -84,7 +84,7 @@ class ObjectDeserializer : JsonDeserializer() { ExtendedActivityVocabulary.Service -> TODO() ExtendedActivityVocabulary.Article -> TODO() ExtendedActivityVocabulary.Audio -> TODO() - ExtendedActivityVocabulary.Document -> TODO() + ExtendedActivityVocabulary.Document -> p.codec.treeToValue(treeNode, Document::class.java) ExtendedActivityVocabulary.Event -> TODO() ExtendedActivityVocabulary.Image -> p.codec.treeToValue(treeNode, Image::class.java) ExtendedActivityVocabulary.Page -> TODO() diff --git a/src/main/kotlin/dev/usbharu/hideout/domain/model/job/HideoutJob.kt b/src/main/kotlin/dev/usbharu/hideout/domain/model/job/HideoutJob.kt index 252dcb17..a4bc0510 100644 --- a/src/main/kotlin/dev/usbharu/hideout/domain/model/job/HideoutJob.kt +++ b/src/main/kotlin/dev/usbharu/hideout/domain/model/job/HideoutJob.kt @@ -18,6 +18,7 @@ object DeliverPostJob : HideoutJob("DeliverPostJob") { val post: Prop = string("post") val actor: Prop = string("actor") val inbox: Prop = string("inbox") + val media: Prop = string("media") } @Component diff --git a/src/main/kotlin/dev/usbharu/hideout/query/MediaQueryService.kt b/src/main/kotlin/dev/usbharu/hideout/query/MediaQueryService.kt new file mode 100644 index 00000000..183e808f --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/query/MediaQueryService.kt @@ -0,0 +1,7 @@ +package dev.usbharu.hideout.query + +import dev.usbharu.hideout.domain.model.hideout.entity.Media + +interface MediaQueryService { + suspend fun findByPostId(postId: Long): List +} diff --git a/src/main/kotlin/dev/usbharu/hideout/query/MediaQueryServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/query/MediaQueryServiceImpl.kt new file mode 100644 index 00000000..ecb687bb --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/query/MediaQueryServiceImpl.kt @@ -0,0 +1,17 @@ +package dev.usbharu.hideout.query + +import dev.usbharu.hideout.domain.model.hideout.entity.Media +import dev.usbharu.hideout.repository.PostsMedia +import dev.usbharu.hideout.repository.toMedia +import org.jetbrains.exposed.sql.innerJoin +import org.jetbrains.exposed.sql.select +import org.springframework.stereotype.Repository + +@Repository +class MediaQueryServiceImpl : MediaQueryService { + override suspend fun findByPostId(postId: Long): List { + return dev.usbharu.hideout.repository.Media.innerJoin(PostsMedia, onColumn = { id }, otherColumn = { mediaId }) + .select { PostsMedia.postId eq postId } + .map { it.toMedia() } + } +} diff --git a/src/main/kotlin/dev/usbharu/hideout/service/ap/APNoteService.kt b/src/main/kotlin/dev/usbharu/hideout/service/ap/APNoteService.kt index 8c365ba1..89c31eae 100644 --- a/src/main/kotlin/dev/usbharu/hideout/service/ap/APNoteService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/service/ap/APNoteService.kt @@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.module.kotlin.readValue import dev.usbharu.hideout.config.ApplicationConfig import dev.usbharu.hideout.domain.model.ap.Create +import dev.usbharu.hideout.domain.model.ap.Document import dev.usbharu.hideout.domain.model.ap.Note import dev.usbharu.hideout.domain.model.hideout.entity.Post import dev.usbharu.hideout.domain.model.hideout.entity.Visibility @@ -13,6 +14,7 @@ import dev.usbharu.hideout.exception.ap.IllegalActivityPubObjectException import dev.usbharu.hideout.plugins.getAp import dev.usbharu.hideout.plugins.postAp import dev.usbharu.hideout.query.FollowerQueryService +import dev.usbharu.hideout.query.MediaQueryService import dev.usbharu.hideout.query.PostQueryService import dev.usbharu.hideout.query.UserQueryService import dev.usbharu.hideout.repository.PostRepository @@ -46,6 +48,7 @@ class APNoteServiceImpl( private val userQueryService: UserQueryService, private val followerQueryService: FollowerQueryService, private val postQueryService: PostQueryService, + private val mediaQueryService: MediaQueryService, @Qualifier("activitypub") private val objectMapper: ObjectMapper, private val applicationConfig: ApplicationConfig, private val postService: PostService @@ -62,11 +65,13 @@ class APNoteServiceImpl( val followers = followerQueryService.findFollowersById(post.userId) val userEntity = userQueryService.findById(post.userId) val note = objectMapper.writeValueAsString(post) + val mediaList = objectMapper.writeValueAsString(mediaQueryService.findByPostId(post.id)) followers.forEach { followerEntity -> jobQueueParentService.schedule(DeliverPostJob) { props[DeliverPostJob.actor] = userEntity.url props[DeliverPostJob.post] = note props[DeliverPostJob.inbox] = followerEntity.inbox + props[DeliverPostJob.media] = mediaList } } } @@ -74,13 +79,17 @@ class APNoteServiceImpl( override suspend fun createNoteJob(props: JobProps) { val actor = props[DeliverPostJob.actor] val postEntity = objectMapper.readValue(props[DeliverPostJob.post]) + val mediaList = + objectMapper.readValue>(props[DeliverPostJob.media]) val note = Note( name = "Note", id = postEntity.url, attributedTo = actor, content = postEntity.text, published = Instant.ofEpochMilli(postEntity.createdAt).toString(), - to = listOf(public, "$actor/follower") + to = listOf(public, "$actor/follower"), + attachment = mediaList.map { Document(mediaType = "image/jpeg", url = it.url) } + ) val inbox = props[DeliverPostJob.inbox] logger.debug("createNoteJob: actor={}, note={}, inbox={}", actor, postEntity, inbox)