feat: メディア付き投稿を配送可能に

This commit is contained in:
usbharu 2023-10-10 16:07:21 +09:00
parent db517cf288
commit 216ee78da0
7 changed files with 105 additions and 8 deletions

View File

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

View File

@ -2,6 +2,7 @@ package dev.usbharu.hideout.domain.model.ap
open class Note : Object {
var attributedTo: String? = null
var attachment: List<Document> = emptyList()
var content: String? = null
var published: String? = null
var to: List<String> = emptyList()
@ -22,7 +23,8 @@ open class Note : Object {
to: List<String> = emptyList(),
cc: List<String> = emptyList(),
sensitive: Boolean = false,
inReplyTo: String? = null
inReplyTo: String? = null,
attachment: List<Document> = 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()}"
}
}

View File

@ -84,7 +84,7 @@ class ObjectDeserializer : JsonDeserializer<Object>() {
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()

View File

@ -18,6 +18,7 @@ object DeliverPostJob : HideoutJob("DeliverPostJob") {
val post: Prop<DeliverPostJob, String> = string("post")
val actor: Prop<DeliverPostJob, String> = string("actor")
val inbox: Prop<DeliverPostJob, String> = string("inbox")
val media: Prop<DeliverPostJob, String> = string("media")
}
@Component

View File

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

View File

@ -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<Media> {
return dev.usbharu.hideout.repository.Media.innerJoin(PostsMedia, onColumn = { id }, otherColumn = { mediaId })
.select { PostsMedia.postId eq postId }
.map { it.toMedia() }
}
}

View File

@ -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<DeliverPostJob>) {
val actor = props[DeliverPostJob.actor]
val postEntity = objectMapper.readValue<Post>(props[DeliverPostJob.post])
val mediaList =
objectMapper.readValue<List<dev.usbharu.hideout.domain.model.hideout.entity.Media>>(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)