mirror of https://github.com/usbharu/Hideout.git
Merge pull request #167 from usbharu/activitypub-interface
Activitypub interface
This commit is contained in:
commit
4c5de19836
|
@ -0,0 +1,21 @@
|
||||||
|
package dev.usbharu.hideout.activitypub.domain.exception
|
||||||
|
|
||||||
|
import java.io.Serial
|
||||||
|
|
||||||
|
class ActivityPubProcessException : 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 = 5370068873167636639L
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package dev.usbharu.hideout.activitypub.domain.exception
|
||||||
|
|
||||||
|
class FailedProcessException : 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
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package dev.usbharu.hideout.activitypub.domain.exception
|
||||||
|
|
||||||
|
class HttpSignatureUnauthorizedException : 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
|
||||||
|
)
|
||||||
|
}
|
|
@ -13,14 +13,12 @@ import org.springframework.web.context.request.RequestContextHolder
|
||||||
import org.springframework.web.context.request.ServletRequestAttributes
|
import org.springframework.web.context.request.ServletRequestAttributes
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
|
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
class InboxControllerImpl(private val apService: APService) : InboxController {
|
class InboxControllerImpl(private val apService: APService) : InboxController {
|
||||||
@Suppress("TooGenericExceptionCaught")
|
@Suppress("TooGenericExceptionCaught")
|
||||||
override suspend fun inbox(
|
override suspend fun inbox(
|
||||||
@RequestBody string: String
|
@RequestBody string: String
|
||||||
): ResponseEntity<Unit> {
|
): ResponseEntity<Unit> {
|
||||||
|
|
||||||
val request = (requireNotNull(RequestContextHolder.getRequestAttributes()) as ServletRequestAttributes).request
|
val request = (requireNotNull(RequestContextHolder.getRequestAttributes()) as ServletRequestAttributes).request
|
||||||
|
|
||||||
val parseActivity = try {
|
val parseActivity = try {
|
||||||
|
@ -48,11 +46,14 @@ class InboxControllerImpl(private val apService: APService) : InboxController {
|
||||||
println(headers)
|
println(headers)
|
||||||
|
|
||||||
apService.processActivity(
|
apService.processActivity(
|
||||||
string, parseActivity, HttpRequest(
|
string,
|
||||||
|
parseActivity,
|
||||||
|
HttpRequest(
|
||||||
URL(url + request.queryString.orEmpty()),
|
URL(url + request.queryString.orEmpty()),
|
||||||
HttpHeaders(headers),
|
HttpHeaders(headers),
|
||||||
method
|
method
|
||||||
), headers
|
),
|
||||||
|
headers
|
||||||
)
|
)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
LOGGER.warn("FAILED Process Activity $parseActivity", e)
|
LOGGER.warn("FAILED Process Activity $parseActivity", e)
|
||||||
|
|
|
@ -1,54 +0,0 @@
|
||||||
package dev.usbharu.hideout.activitypub.service.activity.accept
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.exception.IllegalActivityPubObjectException
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.Accept
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.Follow
|
|
||||||
import dev.usbharu.hideout.application.external.Transaction
|
|
||||||
import dev.usbharu.hideout.core.query.FollowerQueryService
|
|
||||||
import dev.usbharu.hideout.core.query.UserQueryService
|
|
||||||
import dev.usbharu.hideout.core.service.user.UserService
|
|
||||||
import org.slf4j.LoggerFactory
|
|
||||||
import org.springframework.stereotype.Service
|
|
||||||
|
|
||||||
interface APAcceptService {
|
|
||||||
suspend fun receiveAccept(accept: Accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Service
|
|
||||||
class APAcceptServiceImpl(
|
|
||||||
private val userService: UserService,
|
|
||||||
private val userQueryService: UserQueryService,
|
|
||||||
private val followerQueryService: FollowerQueryService,
|
|
||||||
private val transaction: Transaction
|
|
||||||
) : APAcceptService {
|
|
||||||
override suspend fun receiveAccept(accept: Accept) {
|
|
||||||
return transaction.transaction {
|
|
||||||
LOGGER.debug("START Follow")
|
|
||||||
LOGGER.trace("{}", accept)
|
|
||||||
val value = accept.`object` ?: throw IllegalActivityPubObjectException("object is null")
|
|
||||||
if (value.type.contains("Follow").not()) {
|
|
||||||
LOGGER.warn("FAILED Activity type is not 'Follow'")
|
|
||||||
throw IllegalActivityPubObjectException("Invalid type ${value.type}")
|
|
||||||
}
|
|
||||||
|
|
||||||
val follow = value as Follow
|
|
||||||
val userUrl = follow.`object` ?: throw IllegalActivityPubObjectException("object is null")
|
|
||||||
val followerUrl = follow.actor ?: throw IllegalActivityPubObjectException("actor is null")
|
|
||||||
|
|
||||||
val user = userQueryService.findByUrl(userUrl)
|
|
||||||
val follower = userQueryService.findByUrl(followerUrl)
|
|
||||||
|
|
||||||
if (followerQueryService.alreadyFollow(user.id, follower.id)) {
|
|
||||||
LOGGER.debug("END User already follow from ${follower.url} to ${user.url}")
|
|
||||||
return@transaction
|
|
||||||
}
|
|
||||||
userService.follow(user.id, follower.id)
|
|
||||||
LOGGER.debug("SUCCESS Follow from ${follower.url} to ${user.url}.")
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val LOGGER = LoggerFactory.getLogger(APAcceptServiceImpl::class.java)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
package dev.usbharu.hideout.activitypub.service.activity.accept
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.activitypub.domain.exception.IllegalActivityPubObjectException
|
||||||
|
import dev.usbharu.hideout.activitypub.domain.model.Accept
|
||||||
|
import dev.usbharu.hideout.activitypub.domain.model.Follow
|
||||||
|
import dev.usbharu.hideout.activitypub.service.common.AbstractActivityPubProcessor
|
||||||
|
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.FollowerQueryService
|
||||||
|
import dev.usbharu.hideout.core.query.UserQueryService
|
||||||
|
import dev.usbharu.hideout.core.service.user.UserService
|
||||||
|
|
||||||
|
class ApAcceptProcessor(
|
||||||
|
private val transaction: Transaction,
|
||||||
|
private val userQueryService: UserQueryService,
|
||||||
|
private val followerQueryService: FollowerQueryService,
|
||||||
|
private val userService: UserService
|
||||||
|
) :
|
||||||
|
AbstractActivityPubProcessor<Accept>(transaction) {
|
||||||
|
|
||||||
|
override suspend fun internalProcess(activity: ActivityPubProcessContext<Accept>) {
|
||||||
|
val value = activity.activity.`object` ?: throw IllegalActivityPubObjectException("object is null")
|
||||||
|
|
||||||
|
if (value.type.contains("Follow").not()) {
|
||||||
|
logger.warn("FAILED Activity type is not Follow.")
|
||||||
|
throw IllegalActivityPubObjectException("Invalid type ${value.type}")
|
||||||
|
}
|
||||||
|
|
||||||
|
val follow = value as Follow
|
||||||
|
|
||||||
|
val userUrl = follow.`object` ?: throw IllegalActivityPubObjectException("object is null")
|
||||||
|
val followerUrl = follow.actor ?: throw IllegalActivityPubObjectException("actor is null")
|
||||||
|
|
||||||
|
val user = userQueryService.findByUrl(userUrl)
|
||||||
|
val follower = userQueryService.findByUrl(followerUrl)
|
||||||
|
|
||||||
|
if (followerQueryService.alreadyFollow(user.id, follower.id)) {
|
||||||
|
logger.debug("END User already follow from ${follower.url} to ${user.url}.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
userService.follow(user.id, follower.id)
|
||||||
|
logger.debug("SUCCESS Follow from ${follower.url} to ${user.url}.")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isSupported(activityType: ActivityType): Boolean = activityType == ActivityType.Accept
|
||||||
|
|
||||||
|
override fun type(): Class<Accept> = Accept::class.java
|
||||||
|
}
|
|
@ -1,40 +0,0 @@
|
||||||
package dev.usbharu.hideout.activitypub.service.activity.create
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.exception.IllegalActivityPubObjectException
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.Create
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.Note
|
|
||||||
import dev.usbharu.hideout.activitypub.service.objects.note.APNoteService
|
|
||||||
import dev.usbharu.hideout.application.external.Transaction
|
|
||||||
import org.slf4j.LoggerFactory
|
|
||||||
import org.springframework.stereotype.Service
|
|
||||||
|
|
||||||
interface APCreateService {
|
|
||||||
suspend fun receiveCreate(create: Create)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Service
|
|
||||||
class APCreateServiceImpl(
|
|
||||||
private val apNoteService: APNoteService,
|
|
||||||
private val transaction: Transaction
|
|
||||||
) : APCreateService {
|
|
||||||
override suspend fun receiveCreate(create: Create) {
|
|
||||||
LOGGER.debug("START Create new remote note.")
|
|
||||||
LOGGER.trace("{}", create)
|
|
||||||
|
|
||||||
val value = create.`object` ?: throw IllegalActivityPubObjectException("object is null")
|
|
||||||
if (value.type.contains("Note").not()) {
|
|
||||||
LOGGER.warn("FAILED Object type is not 'Note'")
|
|
||||||
throw IllegalActivityPubObjectException("object is not Note")
|
|
||||||
}
|
|
||||||
|
|
||||||
return transaction.transaction {
|
|
||||||
val note = value as Note
|
|
||||||
apNoteService.fetchNote(note)
|
|
||||||
LOGGER.debug("SUCCESS Create new remote note. ${note.id} by ${note.attributedTo}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val LOGGER = LoggerFactory.getLogger(APCreateServiceImpl::class.java)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
package dev.usbharu.hideout.activitypub.service.activity.create
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.activitypub.domain.model.Create
|
||||||
|
import dev.usbharu.hideout.activitypub.domain.model.Note
|
||||||
|
import dev.usbharu.hideout.activitypub.service.common.AbstractActivityPubProcessor
|
||||||
|
import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessContext
|
||||||
|
import dev.usbharu.hideout.activitypub.service.common.ActivityType
|
||||||
|
import dev.usbharu.hideout.activitypub.service.objects.note.APNoteService
|
||||||
|
import dev.usbharu.hideout.application.external.Transaction
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class CreateActivityProcessor(transaction: Transaction, private val apNoteService: APNoteService) :
|
||||||
|
AbstractActivityPubProcessor<Create>(transaction, false) {
|
||||||
|
override suspend fun internalProcess(activity: ActivityPubProcessContext<Create>) {
|
||||||
|
apNoteService.fetchNote(activity.activity.`object` as Note)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isSupported(activityType: ActivityType): Boolean = activityType == ActivityType.Create
|
||||||
|
|
||||||
|
override fun type(): Class<Create> = Create::class.java
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
package dev.usbharu.hideout.activitypub.service.activity.delete
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.activitypub.domain.exception.IllegalActivityPubObjectException
|
||||||
|
import dev.usbharu.hideout.activitypub.domain.model.Delete
|
||||||
|
import dev.usbharu.hideout.activitypub.service.common.AbstractActivityPubProcessor
|
||||||
|
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.domain.model.post.PostRepository
|
||||||
|
import dev.usbharu.hideout.core.query.PostQueryService
|
||||||
|
|
||||||
|
class APDeleteProcessor(
|
||||||
|
transaction: Transaction,
|
||||||
|
private val postQueryService: PostQueryService,
|
||||||
|
private val postRepository: PostRepository
|
||||||
|
) :
|
||||||
|
AbstractActivityPubProcessor<Delete>(transaction) {
|
||||||
|
override suspend fun internalProcess(activity: ActivityPubProcessContext<Delete>) {
|
||||||
|
val deleteId = activity.activity.`object`?.id ?: throw IllegalActivityPubObjectException("object.id is null")
|
||||||
|
|
||||||
|
val post = try {
|
||||||
|
postQueryService.findByApId(deleteId)
|
||||||
|
} catch (e: FailedToGetResourcesException) {
|
||||||
|
logger.warn("FAILED delete id: {} is not found.", deleteId)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
postRepository.delete(post.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isSupported(activityType: ActivityType): Boolean = activityType == ActivityType.Delete
|
||||||
|
|
||||||
|
override fun type(): Class<Delete> = Delete::class.java
|
||||||
|
}
|
|
@ -1,7 +0,0 @@
|
||||||
package dev.usbharu.hideout.activitypub.service.activity.delete
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.Delete
|
|
||||||
|
|
||||||
interface APReceiveDeleteService {
|
|
||||||
suspend fun receiveDelete(delete: Delete)
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
package dev.usbharu.hideout.activitypub.service.activity.delete
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.exception.IllegalActivityPubObjectException
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.Delete
|
|
||||||
import dev.usbharu.hideout.application.external.Transaction
|
|
||||||
import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException
|
|
||||||
import dev.usbharu.hideout.core.domain.model.post.PostRepository
|
|
||||||
import dev.usbharu.hideout.core.query.PostQueryService
|
|
||||||
import org.springframework.stereotype.Service
|
|
||||||
|
|
||||||
@Service
|
|
||||||
class APReceiveDeleteServiceImpl(
|
|
||||||
private val postQueryService: PostQueryService,
|
|
||||||
private val postRepository: PostRepository,
|
|
||||||
private val transaction: Transaction
|
|
||||||
) : APReceiveDeleteService {
|
|
||||||
override suspend fun receiveDelete(delete: Delete) = transaction.transaction {
|
|
||||||
val deleteId = delete.`object`?.id ?: throw IllegalActivityPubObjectException("object.id is null")
|
|
||||||
|
|
||||||
val post = try {
|
|
||||||
postQueryService.findByApId(deleteId)
|
|
||||||
} catch (_: FailedToGetResourcesException) {
|
|
||||||
return@transaction
|
|
||||||
}
|
|
||||||
postRepository.delete(post.id)
|
|
||||||
return@transaction
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
package dev.usbharu.hideout.activitypub.service.activity.follow
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
|
import dev.usbharu.hideout.activitypub.domain.exception.IllegalActivityPubObjectException
|
||||||
|
import dev.usbharu.hideout.activitypub.domain.model.Follow
|
||||||
|
import dev.usbharu.hideout.activitypub.service.common.AbstractActivityPubProcessor
|
||||||
|
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.external.job.ReceiveFollowJob
|
||||||
|
import dev.usbharu.hideout.core.external.job.ReceiveFollowJobParam
|
||||||
|
import dev.usbharu.hideout.core.service.job.JobQueueParentService
|
||||||
|
|
||||||
|
class APFollowProcessor(
|
||||||
|
transaction: Transaction,
|
||||||
|
private val jobQueueParentService: JobQueueParentService,
|
||||||
|
private val objectMapper: ObjectMapper
|
||||||
|
) :
|
||||||
|
AbstractActivityPubProcessor<Follow>(transaction) {
|
||||||
|
override suspend fun internalProcess(activity: ActivityPubProcessContext<Follow>) {
|
||||||
|
logger.info("FOLLOW from: {} to {}", activity.activity.actor, activity.activity.`object`)
|
||||||
|
|
||||||
|
// inboxをジョブキューに乗せているので既に不要だが、フォロー承認制アカウントを実装する際に必要なので残す
|
||||||
|
val jobProps = ReceiveFollowJobParam(
|
||||||
|
activity.activity.actor ?: throw IllegalActivityPubObjectException("actor is null"),
|
||||||
|
objectMapper.writeValueAsString(activity.activity),
|
||||||
|
activity.activity.`object` ?: throw IllegalActivityPubObjectException("object is null")
|
||||||
|
)
|
||||||
|
jobQueueParentService.scheduleTypeSafe(ReceiveFollowJob, jobProps)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isSupported(activityType: ActivityType): Boolean = activityType == ActivityType.Follow
|
||||||
|
|
||||||
|
override fun type(): Class<Follow> = Follow::class.java
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
package dev.usbharu.hideout.activitypub.service.activity.follow
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
|
import com.fasterxml.jackson.module.kotlin.readValue
|
||||||
|
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.activitypub.service.objects.user.APUserService
|
||||||
|
import dev.usbharu.hideout.application.external.Transaction
|
||||||
|
import dev.usbharu.hideout.core.external.job.ReceiveFollowJob
|
||||||
|
import dev.usbharu.hideout.core.external.job.ReceiveFollowJobParam
|
||||||
|
import dev.usbharu.hideout.core.query.UserQueryService
|
||||||
|
import dev.usbharu.hideout.core.service.job.JobProcessor
|
||||||
|
import dev.usbharu.hideout.core.service.user.UserService
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class APReceiveFollowJobProcessor(
|
||||||
|
private val transaction: Transaction,
|
||||||
|
private val userQueryService: UserQueryService,
|
||||||
|
private val apUserService: APUserService,
|
||||||
|
private val objectMapper: ObjectMapper,
|
||||||
|
private val apRequestService: APRequestService,
|
||||||
|
private val userService: UserService
|
||||||
|
) :
|
||||||
|
JobProcessor<ReceiveFollowJobParam, ReceiveFollowJob> {
|
||||||
|
override suspend fun process(param: ReceiveFollowJobParam) = transaction.transaction {
|
||||||
|
val person = apUserService.fetchPerson(param.actor, param.targetActor)
|
||||||
|
val follow = objectMapper.readValue<Follow>(param.follow)
|
||||||
|
|
||||||
|
logger.info("START Follow from: {} to {}", param.targetActor, param.actor)
|
||||||
|
|
||||||
|
val signer = userQueryService.findByUrl(param.targetActor)
|
||||||
|
|
||||||
|
val urlString = person.inbox ?: throw IllegalArgumentException("inbox is not found.")
|
||||||
|
|
||||||
|
apRequestService.apPost(
|
||||||
|
url = urlString,
|
||||||
|
body = Accept(
|
||||||
|
name = "Follow",
|
||||||
|
`object` = follow,
|
||||||
|
actor = param.targetActor
|
||||||
|
),
|
||||||
|
signer = signer
|
||||||
|
)
|
||||||
|
|
||||||
|
val targetEntity = userQueryService.findByUrl(param.targetActor)
|
||||||
|
val followActorEntity =
|
||||||
|
userQueryService.findByUrl(follow.actor ?: throw IllegalArgumentException("actor is null"))
|
||||||
|
|
||||||
|
userService.followRequest(targetEntity.id, followActorEntity.id)
|
||||||
|
logger.info("SUCCESS Follow from: {} to: {}", param.targetActor, param.actor)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun job(): ReceiveFollowJob = ReceiveFollowJob
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val logger = LoggerFactory.getLogger(APReceiveFollowJobProcessor::class.java)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +0,0 @@
|
||||||
package dev.usbharu.hideout.activitypub.service.activity.follow
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.core.external.job.ReceiveFollowJob
|
|
||||||
import kjob.core.job.JobProps
|
|
||||||
|
|
||||||
interface APReceiveFollowJobService {
|
|
||||||
suspend fun receiveFollowJob(props: JobProps<ReceiveFollowJob>)
|
|
||||||
}
|
|
|
@ -1,61 +0,0 @@
|
||||||
package dev.usbharu.hideout.activitypub.service.activity.follow
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper
|
|
||||||
import com.fasterxml.jackson.module.kotlin.readValue
|
|
||||||
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.activitypub.service.objects.user.APUserService
|
|
||||||
import dev.usbharu.hideout.application.external.Transaction
|
|
||||||
import dev.usbharu.hideout.core.external.job.ReceiveFollowJob
|
|
||||||
import dev.usbharu.hideout.core.query.UserQueryService
|
|
||||||
import dev.usbharu.hideout.core.service.user.UserService
|
|
||||||
import kjob.core.job.JobProps
|
|
||||||
import org.slf4j.LoggerFactory
|
|
||||||
import org.springframework.beans.factory.annotation.Qualifier
|
|
||||||
import org.springframework.stereotype.Component
|
|
||||||
|
|
||||||
@Component
|
|
||||||
class APReceiveFollowJobServiceImpl(
|
|
||||||
private val apUserService: APUserService,
|
|
||||||
private val userQueryService: UserQueryService,
|
|
||||||
private val apRequestService: APRequestService,
|
|
||||||
private val userService: UserService,
|
|
||||||
@Qualifier("activitypub") private val objectMapper: ObjectMapper,
|
|
||||||
private val transaction: Transaction
|
|
||||||
) : APReceiveFollowJobService {
|
|
||||||
override suspend fun receiveFollowJob(props: JobProps<ReceiveFollowJob>) {
|
|
||||||
transaction.transaction {
|
|
||||||
val actor = props[ReceiveFollowJob.actor]
|
|
||||||
val targetActor = props[ReceiveFollowJob.targetActor]
|
|
||||||
val person = apUserService.fetchPerson(actor, targetActor)
|
|
||||||
val follow = objectMapper.readValue<Follow>(props[ReceiveFollowJob.follow])
|
|
||||||
logger.info("START Follow from: {} to: {}", targetActor, actor)
|
|
||||||
|
|
||||||
val signer = userQueryService.findByUrl(targetActor)
|
|
||||||
|
|
||||||
val urlString = person.inbox ?: throw IllegalArgumentException("inbox is not found")
|
|
||||||
|
|
||||||
apRequestService.apPost(
|
|
||||||
url = urlString,
|
|
||||||
body = Accept(
|
|
||||||
name = "Follow",
|
|
||||||
`object` = follow,
|
|
||||||
actor = targetActor
|
|
||||||
),
|
|
||||||
signer = signer
|
|
||||||
)
|
|
||||||
|
|
||||||
val targetEntity = userQueryService.findByUrl(targetActor)
|
|
||||||
val followActorEntity =
|
|
||||||
userQueryService.findByUrl(follow.actor ?: throw java.lang.IllegalArgumentException("Actor is null"))
|
|
||||||
|
|
||||||
userService.followRequest(targetEntity.id, followActorEntity.id)
|
|
||||||
logger.info("SUCCESS Follow from: {} to: {}", targetActor, actor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val logger = LoggerFactory.getLogger(APReceiveFollowJobServiceImpl::class.java)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
package dev.usbharu.hideout.activitypub.service.activity.like
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.activitypub.domain.exception.FailedToGetActivityPubResourceException
|
||||||
|
import dev.usbharu.hideout.activitypub.domain.exception.IllegalActivityPubObjectException
|
||||||
|
import dev.usbharu.hideout.activitypub.domain.model.Like
|
||||||
|
import dev.usbharu.hideout.activitypub.service.common.AbstractActivityPubProcessor
|
||||||
|
import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessContext
|
||||||
|
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
|
||||||
|
|
||||||
|
class APLikeProcessor(
|
||||||
|
transaction: Transaction,
|
||||||
|
private val apUserService: APUserService,
|
||||||
|
private val apNoteService: APNoteService,
|
||||||
|
private val postQueryService: PostQueryService,
|
||||||
|
private val reactionService: ReactionService
|
||||||
|
) :
|
||||||
|
AbstractActivityPubProcessor<Like>(transaction) {
|
||||||
|
override suspend fun internalProcess(activity: ActivityPubProcessContext<Like>) {
|
||||||
|
val actor = activity.activity.actor ?: throw IllegalActivityPubObjectException("actor is null")
|
||||||
|
val content = activity.activity.content ?: throw IllegalActivityPubObjectException("content is null")
|
||||||
|
|
||||||
|
val target = activity.activity.`object` ?: throw IllegalActivityPubObjectException("object is null")
|
||||||
|
|
||||||
|
val personWithEntity = apUserService.fetchPersonWithEntity(actor)
|
||||||
|
|
||||||
|
try {
|
||||||
|
apNoteService.fetchNoteAsync(target).await()
|
||||||
|
} 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
|
||||||
|
|
||||||
|
override fun type(): Class<Like> = Like::class.java
|
||||||
|
}
|
|
@ -1,63 +0,0 @@
|
||||||
package dev.usbharu.hideout.activitypub.service.activity.like
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.exception.FailedToGetActivityPubResourceException
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.exception.IllegalActivityPubObjectException
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.Like
|
|
||||||
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.slf4j.LoggerFactory
|
|
||||||
import org.springframework.stereotype.Service
|
|
||||||
|
|
||||||
interface APLikeService {
|
|
||||||
suspend fun receiveLike(like: Like)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Service
|
|
||||||
class APLikeServiceImpl(
|
|
||||||
private val reactionService: ReactionService,
|
|
||||||
private val apUserService: APUserService,
|
|
||||||
private val apNoteService: APNoteService,
|
|
||||||
private val postQueryService: PostQueryService,
|
|
||||||
private val transaction: Transaction
|
|
||||||
) : APLikeService {
|
|
||||||
override suspend fun receiveLike(like: Like) {
|
|
||||||
LOGGER.debug("START Add Like")
|
|
||||||
LOGGER.trace("{}", like)
|
|
||||||
|
|
||||||
val actor = like.actor ?: throw IllegalActivityPubObjectException("actor is null")
|
|
||||||
val content = like.content ?: throw IllegalActivityPubObjectException("content is null")
|
|
||||||
like.`object` ?: throw IllegalActivityPubObjectException("object is null")
|
|
||||||
transaction.transaction {
|
|
||||||
LOGGER.trace("FETCH Liked Person $actor")
|
|
||||||
val person = apUserService.fetchPersonWithEntity(actor)
|
|
||||||
LOGGER.trace("{}", person.second)
|
|
||||||
|
|
||||||
LOGGER.trace("FETCH Liked Note ${like.`object`}")
|
|
||||||
try {
|
|
||||||
apNoteService.fetchNoteAsync(like.`object` ?: return@transaction).await()
|
|
||||||
} catch (e: FailedToGetActivityPubResourceException) {
|
|
||||||
LOGGER.debug("FAILED Failed to Get ${like.`object`}")
|
|
||||||
LOGGER.trace("", e)
|
|
||||||
return@transaction
|
|
||||||
}
|
|
||||||
val post = postQueryService.findByUrl(like.`object` ?: return@transaction)
|
|
||||||
LOGGER.trace("{}", post)
|
|
||||||
|
|
||||||
reactionService.receiveReaction(
|
|
||||||
content,
|
|
||||||
actor.substringAfter("://").substringBefore("/"),
|
|
||||||
person.second.id,
|
|
||||||
post.id
|
|
||||||
)
|
|
||||||
LOGGER.debug("SUCCESS Add Like($content) from ${person.second.url} to ${post.url}")
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val LOGGER = LoggerFactory.getLogger(APLikeServiceImpl::class.java)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
package dev.usbharu.hideout.activitypub.service.activity.like
|
||||||
|
|
||||||
|
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.external.job.DeliverReactionJob
|
||||||
|
import dev.usbharu.hideout.core.external.job.DeliverReactionJobParam
|
||||||
|
import dev.usbharu.hideout.core.query.UserQueryService
|
||||||
|
import dev.usbharu.hideout.core.service.job.JobProcessor
|
||||||
|
|
||||||
|
class ApReactionJobProcessor(
|
||||||
|
private val userQueryService: UserQueryService,
|
||||||
|
private val apRequestService: APRequestService,
|
||||||
|
private val applicationConfig: ApplicationConfig,
|
||||||
|
private val transaction: Transaction
|
||||||
|
) : JobProcessor<DeliverReactionJobParam, DeliverReactionJob> {
|
||||||
|
override suspend fun process(param: DeliverReactionJobParam): Unit = transaction.transaction {
|
||||||
|
val signer = userQueryService.findByUrl(param.actor)
|
||||||
|
|
||||||
|
apRequestService.apPost(
|
||||||
|
param.inbox,
|
||||||
|
Like(
|
||||||
|
name = "Like",
|
||||||
|
actor = param.actor,
|
||||||
|
`object` = param.postUrl,
|
||||||
|
id = "${applicationConfig.url}/liek/note/${param.id}",
|
||||||
|
content = param.reaction
|
||||||
|
),
|
||||||
|
signer
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun job(): DeliverReactionJob = DeliverReactionJob
|
||||||
|
}
|
|
@ -1,10 +0,0 @@
|
||||||
package dev.usbharu.hideout.activitypub.service.activity.like
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.core.external.job.DeliverReactionJob
|
|
||||||
import dev.usbharu.hideout.core.external.job.DeliverRemoveReactionJob
|
|
||||||
import kjob.core.job.JobProps
|
|
||||||
|
|
||||||
interface ApReactionJobService {
|
|
||||||
suspend fun reactionJob(props: JobProps<DeliverReactionJob>)
|
|
||||||
suspend fun removeReactionJob(props: JobProps<DeliverRemoveReactionJob>)
|
|
||||||
}
|
|
|
@ -1,66 +0,0 @@
|
||||||
package dev.usbharu.hideout.activitypub.service.activity.like
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper
|
|
||||||
import com.fasterxml.jackson.module.kotlin.readValue
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.Like
|
|
||||||
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.core.external.job.DeliverReactionJob
|
|
||||||
import dev.usbharu.hideout.core.external.job.DeliverRemoveReactionJob
|
|
||||||
import dev.usbharu.hideout.core.query.UserQueryService
|
|
||||||
import kjob.core.job.JobProps
|
|
||||||
import org.springframework.beans.factory.annotation.Qualifier
|
|
||||||
import org.springframework.stereotype.Service
|
|
||||||
import java.time.Instant
|
|
||||||
|
|
||||||
@Service
|
|
||||||
class ApReactionJobServiceImpl(
|
|
||||||
private val userQueryService: UserQueryService,
|
|
||||||
private val apRequestService: APRequestService,
|
|
||||||
private val applicationConfig: ApplicationConfig,
|
|
||||||
@Qualifier("activitypub") private val objectMapper: ObjectMapper
|
|
||||||
) : ApReactionJobService {
|
|
||||||
override suspend fun reactionJob(props: JobProps<DeliverReactionJob>) {
|
|
||||||
val inbox = props[DeliverReactionJob.inbox]
|
|
||||||
val actor = props[DeliverReactionJob.actor]
|
|
||||||
val postUrl = props[DeliverReactionJob.postUrl]
|
|
||||||
val id = props[DeliverReactionJob.id]
|
|
||||||
val content = props[DeliverReactionJob.reaction]
|
|
||||||
|
|
||||||
val signer = userQueryService.findByUrl(actor)
|
|
||||||
|
|
||||||
apRequestService.apPost(
|
|
||||||
inbox,
|
|
||||||
Like(
|
|
||||||
name = "Like",
|
|
||||||
actor = actor,
|
|
||||||
`object` = postUrl,
|
|
||||||
id = "${applicationConfig.url}/like/note/$id",
|
|
||||||
content = content
|
|
||||||
),
|
|
||||||
signer
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun removeReactionJob(props: JobProps<DeliverRemoveReactionJob>) {
|
|
||||||
val inbox = props[DeliverRemoveReactionJob.inbox]
|
|
||||||
val actor = props[DeliverRemoveReactionJob.actor]
|
|
||||||
val like = objectMapper.readValue<Like>(props[DeliverRemoveReactionJob.like])
|
|
||||||
val id = props[DeliverRemoveReactionJob.id]
|
|
||||||
|
|
||||||
val signer = userQueryService.findByUrl(actor)
|
|
||||||
|
|
||||||
apRequestService.apPost(
|
|
||||||
inbox,
|
|
||||||
Undo(
|
|
||||||
name = "Undo Reaction",
|
|
||||||
actor = actor,
|
|
||||||
`object` = like,
|
|
||||||
id = "${applicationConfig.url}/undo/note/$id",
|
|
||||||
published = Instant.now()
|
|
||||||
),
|
|
||||||
signer
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
package dev.usbharu.hideout.activitypub.service.activity.like
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
|
import com.fasterxml.jackson.module.kotlin.readValue
|
||||||
|
import dev.usbharu.hideout.activitypub.domain.model.Like
|
||||||
|
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.external.job.DeliverRemoveReactionJob
|
||||||
|
import dev.usbharu.hideout.core.external.job.DeliverRemoveReactionJobParam
|
||||||
|
import dev.usbharu.hideout.core.query.UserQueryService
|
||||||
|
import dev.usbharu.hideout.core.service.job.JobProcessor
|
||||||
|
import java.time.Instant
|
||||||
|
|
||||||
|
class ApRemoveReactionJobProcessor(
|
||||||
|
private val userQueryService: UserQueryService,
|
||||||
|
private val transaction: Transaction,
|
||||||
|
private val objectMapper: ObjectMapper,
|
||||||
|
private val apRequestService: APRequestService,
|
||||||
|
private val applicationConfig: ApplicationConfig
|
||||||
|
) : JobProcessor<DeliverRemoveReactionJobParam, DeliverRemoveReactionJob> {
|
||||||
|
override suspend fun process(param: DeliverRemoveReactionJobParam): Unit = transaction.transaction {
|
||||||
|
val like = objectMapper.readValue<Like>(param.like)
|
||||||
|
|
||||||
|
val signer = userQueryService.findByUrl(param.actor)
|
||||||
|
|
||||||
|
apRequestService.apPost(
|
||||||
|
param.inbox,
|
||||||
|
Undo(
|
||||||
|
name = "Undo Reaction",
|
||||||
|
actor = param.actor,
|
||||||
|
`object` = like,
|
||||||
|
id = "${applicationConfig.url}/undo/like/${param.id}",
|
||||||
|
published = Instant.now()
|
||||||
|
),
|
||||||
|
signer
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun job(): DeliverRemoveReactionJob = DeliverRemoveReactionJob
|
||||||
|
}
|
|
@ -2,25 +2,23 @@ package dev.usbharu.hideout.activitypub.service.activity.undo
|
||||||
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.Follow
|
import dev.usbharu.hideout.activitypub.domain.model.Follow
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.Undo
|
import dev.usbharu.hideout.activitypub.domain.model.Undo
|
||||||
|
import dev.usbharu.hideout.activitypub.service.common.AbstractActivityPubProcessor
|
||||||
|
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.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.UserQueryService
|
import dev.usbharu.hideout.core.query.UserQueryService
|
||||||
import dev.usbharu.hideout.core.service.user.UserService
|
import dev.usbharu.hideout.core.service.user.UserService
|
||||||
import org.springframework.stereotype.Service
|
|
||||||
|
|
||||||
interface APUndoService {
|
class APUndoProcessor(
|
||||||
suspend fun receiveUndo(undo: Undo)
|
transaction: Transaction,
|
||||||
}
|
|
||||||
|
|
||||||
@Service
|
|
||||||
@Suppress("UnsafeCallOnNullableType")
|
|
||||||
class APUndoServiceImpl(
|
|
||||||
private val userService: UserService,
|
|
||||||
private val apUserService: APUserService,
|
private val apUserService: APUserService,
|
||||||
private val userQueryService: UserQueryService,
|
private val userQueryService: UserQueryService,
|
||||||
private val transaction: Transaction
|
private val userService: UserService
|
||||||
) : APUndoService {
|
) :
|
||||||
override suspend fun receiveUndo(undo: Undo) {
|
AbstractActivityPubProcessor<Undo>(transaction) {
|
||||||
|
override suspend fun internalProcess(activity: ActivityPubProcessContext<Undo>) {
|
||||||
|
val undo = activity.activity
|
||||||
if (undo.actor == null) {
|
if (undo.actor == null) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -37,12 +35,10 @@ class APUndoServiceImpl(
|
||||||
if (follow.`object` == null) {
|
if (follow.`object` == null) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
transaction.transaction {
|
apUserService.fetchPerson(undo.actor!!, follow.`object`)
|
||||||
apUserService.fetchPerson(undo.actor!!, follow.`object`)
|
val follower = userQueryService.findByUrl(undo.actor!!)
|
||||||
val follower = userQueryService.findByUrl(undo.actor!!)
|
val target = userQueryService.findByUrl(follow.`object`!!)
|
||||||
val target = userQueryService.findByUrl(follow.`object`!!)
|
userService.unfollow(target.id, follower.id)
|
||||||
userService.unfollow(target.id, follower.id)
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,4 +46,8 @@ class APUndoServiceImpl(
|
||||||
}
|
}
|
||||||
TODO()
|
TODO()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun isSupported(activityType: ActivityType): Boolean = activityType == ActivityType.Undo
|
||||||
|
|
||||||
|
override fun type(): Class<Undo> = Undo::class.java
|
||||||
}
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package dev.usbharu.hideout.activitypub.service.common
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.activitypub.domain.exception.ActivityPubProcessException
|
||||||
|
import dev.usbharu.hideout.activitypub.domain.exception.FailedProcessException
|
||||||
|
import dev.usbharu.hideout.activitypub.domain.exception.HttpSignatureUnauthorizedException
|
||||||
|
import dev.usbharu.hideout.activitypub.domain.model.objects.Object
|
||||||
|
import dev.usbharu.hideout.application.external.Transaction
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
|
||||||
|
abstract class AbstractActivityPubProcessor<T : Object>(
|
||||||
|
private val transaction: Transaction,
|
||||||
|
private val allowUnauthorized: Boolean = false
|
||||||
|
) : ActivityPubProcessor<T> {
|
||||||
|
protected val logger = LoggerFactory.getLogger(this::class.java)
|
||||||
|
|
||||||
|
override suspend fun process(activity: ActivityPubProcessContext<T>) {
|
||||||
|
if (activity.isAuthorized.not() && allowUnauthorized.not()) {
|
||||||
|
throw HttpSignatureUnauthorizedException()
|
||||||
|
}
|
||||||
|
logger.info("START ActivityPub process")
|
||||||
|
try {
|
||||||
|
transaction.transaction {
|
||||||
|
internalProcess(activity)
|
||||||
|
}
|
||||||
|
} catch (e: ActivityPubProcessException) {
|
||||||
|
logger.warn("FAILED ActivityPub process", e)
|
||||||
|
throw FailedProcessException("Failed process", e)
|
||||||
|
}
|
||||||
|
logger.info("SUCCESS ActivityPub process")
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract suspend fun internalProcess(activity: ActivityPubProcessContext<T>)
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package dev.usbharu.hideout.activitypub.service.common
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode
|
||||||
|
import dev.usbharu.hideout.activitypub.domain.model.objects.Object
|
||||||
|
import dev.usbharu.httpsignature.common.HttpRequest
|
||||||
|
import dev.usbharu.httpsignature.verify.Signature
|
||||||
|
|
||||||
|
data class ActivityPubProcessContext<T : Object>(
|
||||||
|
val activity: T,
|
||||||
|
val jsonNode: JsonNode,
|
||||||
|
val httpRequest: HttpRequest,
|
||||||
|
val signature: Signature?,
|
||||||
|
val isAuthorized: Boolean
|
||||||
|
)
|
|
@ -0,0 +1,11 @@
|
||||||
|
package dev.usbharu.hideout.activitypub.service.common
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.activitypub.domain.model.objects.Object
|
||||||
|
|
||||||
|
interface ActivityPubProcessor<T : Object> {
|
||||||
|
suspend fun process(activity: ActivityPubProcessContext<T>)
|
||||||
|
|
||||||
|
fun isSupported(activityType: ActivityType): Boolean
|
||||||
|
|
||||||
|
fun type(): Class<T>
|
||||||
|
}
|
|
@ -1,8 +0,0 @@
|
||||||
package dev.usbharu.hideout.activitypub.service.common
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.core.external.job.HideoutJob
|
|
||||||
import kjob.core.dsl.JobContextWithProps
|
|
||||||
|
|
||||||
interface ApJobService {
|
|
||||||
suspend fun <T : HideoutJob> processActivity(job: JobContextWithProps<T>, hideoutJob: HideoutJob)
|
|
||||||
}
|
|
|
@ -1,122 +0,0 @@
|
||||||
package dev.usbharu.hideout.activitypub.service.common
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper
|
|
||||||
import com.fasterxml.jackson.module.kotlin.readValue
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.Follow
|
|
||||||
import dev.usbharu.hideout.activitypub.service.activity.accept.APAcceptServiceImpl
|
|
||||||
import dev.usbharu.hideout.activitypub.service.activity.create.APCreateServiceImpl
|
|
||||||
import dev.usbharu.hideout.activitypub.service.activity.delete.APReceiveDeleteServiceImpl
|
|
||||||
import dev.usbharu.hideout.activitypub.service.activity.follow.APReceiveFollowJobService
|
|
||||||
import dev.usbharu.hideout.activitypub.service.activity.follow.APReceiveFollowServiceImpl
|
|
||||||
import dev.usbharu.hideout.activitypub.service.activity.like.APLikeServiceImpl
|
|
||||||
import dev.usbharu.hideout.activitypub.service.activity.like.ApReactionJobService
|
|
||||||
import dev.usbharu.hideout.activitypub.service.activity.undo.APUndoServiceImpl
|
|
||||||
import dev.usbharu.hideout.activitypub.service.objects.note.ApNoteJobService
|
|
||||||
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.*
|
|
||||||
import dev.usbharu.hideout.core.query.UserQueryService
|
|
||||||
import dev.usbharu.hideout.util.RsaUtil
|
|
||||||
import dev.usbharu.httpsignature.common.HttpHeaders
|
|
||||||
import dev.usbharu.httpsignature.common.HttpRequest
|
|
||||||
import dev.usbharu.httpsignature.common.PublicKey
|
|
||||||
import dev.usbharu.httpsignature.verify.DefaultSignatureHeaderParser
|
|
||||||
import dev.usbharu.httpsignature.verify.RsaSha256HttpSignatureVerifier
|
|
||||||
import kjob.core.dsl.JobContextWithProps
|
|
||||||
import kjob.core.job.JobProps
|
|
||||||
import org.slf4j.LoggerFactory
|
|
||||||
import org.springframework.beans.factory.annotation.Qualifier
|
|
||||||
import org.springframework.stereotype.Service
|
|
||||||
|
|
||||||
@Service
|
|
||||||
class ApJobServiceImpl(
|
|
||||||
private val apReceiveFollowJobService: APReceiveFollowJobService,
|
|
||||||
private val apNoteJobService: ApNoteJobService,
|
|
||||||
private val apReactionJobService: ApReactionJobService,
|
|
||||||
private val APAcceptServiceImpl: APAcceptServiceImpl,
|
|
||||||
private val APReceiveFollowServiceImpl: APReceiveFollowServiceImpl,
|
|
||||||
private val APCreateServiceImpl: APCreateServiceImpl,
|
|
||||||
private val APLikeServiceImpl: APLikeServiceImpl,
|
|
||||||
private val APUndoServiceImpl: APUndoServiceImpl,
|
|
||||||
private val APReceiveDeleteServiceImpl: APReceiveDeleteServiceImpl,
|
|
||||||
@Qualifier("activitypub") private val objectMapper: ObjectMapper,
|
|
||||||
private val httpSignatureVerifier: RsaSha256HttpSignatureVerifier,
|
|
||||||
private val signatureHeaderParser: DefaultSignatureHeaderParser,
|
|
||||||
private val apUserService: APUserService,
|
|
||||||
private val userQueryService: UserQueryService,
|
|
||||||
private val transaction: Transaction
|
|
||||||
) : ApJobService {
|
|
||||||
@Suppress("REDUNDANT_ELSE_IN_WHEN")
|
|
||||||
override suspend fun <T : HideoutJob> processActivity(job: JobContextWithProps<T>, hideoutJob: HideoutJob) {
|
|
||||||
logger.debug("processActivity: ${hideoutJob.name}")
|
|
||||||
|
|
||||||
@Suppress("ElseCaseInsteadOfExhaustiveWhen")
|
|
||||||
// Springで作成されるプロキシの都合上パターンマッチングが壊れるので必須
|
|
||||||
when (hideoutJob) {
|
|
||||||
is InboxJob -> {
|
|
||||||
val httpRequestString = (job.props as JobProps<InboxJob>)[InboxJob.httpRequest]
|
|
||||||
println(httpRequestString)
|
|
||||||
val headerString = (job.props as JobProps<InboxJob>)[InboxJob.headers]
|
|
||||||
|
|
||||||
val readValue = objectMapper.readValue<Map<String, List<String>>>(headerString)
|
|
||||||
|
|
||||||
val httpRequest =
|
|
||||||
objectMapper.readValue<HttpRequest>(httpRequestString).copy(headers = HttpHeaders(readValue))
|
|
||||||
val signature = signatureHeaderParser.parse(httpRequest.headers)
|
|
||||||
|
|
||||||
val publicKey = transaction.transaction {
|
|
||||||
try {
|
|
||||||
userQueryService.findByKeyId(signature.keyId)
|
|
||||||
} catch (e: FailedToGetResourcesException) {
|
|
||||||
apUserService.fetchPersonWithEntity(signature.keyId).second
|
|
||||||
}.publicKey
|
|
||||||
}
|
|
||||||
|
|
||||||
httpSignatureVerifier.verify(
|
|
||||||
httpRequest,
|
|
||||||
PublicKey(RsaUtil.decodeRsaPublicKeyPem(publicKey), signature.keyId)
|
|
||||||
)
|
|
||||||
|
|
||||||
val typeString = (job.props as JobProps<InboxJob>)[InboxJob.type]
|
|
||||||
val json = (job.props as JobProps<InboxJob>)[InboxJob.json]
|
|
||||||
val type = ActivityType.valueOf(typeString)
|
|
||||||
when (type) {
|
|
||||||
ActivityType.Accept -> APAcceptServiceImpl.receiveAccept(objectMapper.readValue(json))
|
|
||||||
ActivityType.Follow ->
|
|
||||||
APReceiveFollowServiceImpl
|
|
||||||
.receiveFollow(objectMapper.readValue(json, Follow::class.java))
|
|
||||||
|
|
||||||
ActivityType.Create -> APCreateServiceImpl.receiveCreate(objectMapper.readValue(json))
|
|
||||||
ActivityType.Like -> APLikeServiceImpl.receiveLike(objectMapper.readValue(json))
|
|
||||||
ActivityType.Undo -> APUndoServiceImpl.receiveUndo(objectMapper.readValue(json))
|
|
||||||
ActivityType.Delete -> APReceiveDeleteServiceImpl.receiveDelete(objectMapper.readValue(json))
|
|
||||||
|
|
||||||
else -> {
|
|
||||||
throw IllegalArgumentException("$type is not supported.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
is ReceiveFollowJob -> {
|
|
||||||
apReceiveFollowJobService.receiveFollowJob(
|
|
||||||
job.props as JobProps<ReceiveFollowJob>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
is DeliverPostJob -> apNoteJobService.createNoteJob(job.props as JobProps<DeliverPostJob>)
|
|
||||||
is DeliverReactionJob -> apReactionJobService.reactionJob(job.props as JobProps<DeliverReactionJob>)
|
|
||||||
is DeliverRemoveReactionJob -> apReactionJobService.removeReactionJob(
|
|
||||||
job.props as JobProps<DeliverRemoveReactionJob>
|
|
||||||
)
|
|
||||||
|
|
||||||
else -> {
|
|
||||||
throw IllegalStateException("WTF")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val logger = LoggerFactory.getLogger(ApJobServiceImpl::class.java)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,136 @@
|
||||||
|
package dev.usbharu.hideout.activitypub.service.inbox
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
|
import com.fasterxml.jackson.module.kotlin.readValue
|
||||||
|
import dev.usbharu.hideout.activitypub.domain.model.objects.Object
|
||||||
|
import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessContext
|
||||||
|
import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessor
|
||||||
|
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.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.UserQueryService
|
||||||
|
import dev.usbharu.hideout.core.service.job.JobProcessor
|
||||||
|
import dev.usbharu.hideout.util.RsaUtil
|
||||||
|
import dev.usbharu.httpsignature.common.HttpHeaders
|
||||||
|
import dev.usbharu.httpsignature.common.HttpRequest
|
||||||
|
import dev.usbharu.httpsignature.common.PublicKey
|
||||||
|
import dev.usbharu.httpsignature.verify.HttpSignatureVerifier
|
||||||
|
import dev.usbharu.httpsignature.verify.Signature
|
||||||
|
import dev.usbharu.httpsignature.verify.SignatureHeaderParser
|
||||||
|
import kjob.core.job.JobProps
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class InboxJobProcessor(
|
||||||
|
private val activityPubProcessorList: List<ActivityPubProcessor<Object>>,
|
||||||
|
private val objectMapper: ObjectMapper,
|
||||||
|
private val signatureHeaderParser: SignatureHeaderParser,
|
||||||
|
private val signatureVerifier: HttpSignatureVerifier,
|
||||||
|
private val userQueryService: UserQueryService,
|
||||||
|
private val apUserService: APUserService,
|
||||||
|
private val transaction: Transaction
|
||||||
|
) : JobProcessor<InboxJobParam, InboxJob> {
|
||||||
|
suspend fun process(props: JobProps<InboxJob>) {
|
||||||
|
val type = ActivityType.valueOf(props[InboxJob.type])
|
||||||
|
val jsonString = objectMapper.readTree(props[InboxJob.json])
|
||||||
|
val httpRequestString = props[InboxJob.httpRequest]
|
||||||
|
val headersString = props[InboxJob.headers]
|
||||||
|
|
||||||
|
logger.info("START Process inbox. type: {}", type)
|
||||||
|
logger.trace("type: {} \njson: \n{}", type, jsonString.toPrettyString())
|
||||||
|
|
||||||
|
val map = objectMapper.readValue<Map<String, List<String>>>(headersString)
|
||||||
|
|
||||||
|
val httpRequest =
|
||||||
|
objectMapper.readValue<HttpRequest>(httpRequestString).copy(headers = HttpHeaders(map))
|
||||||
|
|
||||||
|
logger.trace("request: {}\nheaders: {}", httpRequest, map)
|
||||||
|
|
||||||
|
val signature = parseSignatureHeader(httpRequest.headers)
|
||||||
|
|
||||||
|
logger.debug("Has signature? {}", signature != null)
|
||||||
|
|
||||||
|
val verify = signature?.let { verifyHttpSignature(httpRequest, it) } ?: false
|
||||||
|
|
||||||
|
logger.debug("Is verifying success? {}", verify)
|
||||||
|
|
||||||
|
val activityPubProcessor = activityPubProcessorList.firstOrNull { it.isSupported(type) }
|
||||||
|
|
||||||
|
if (activityPubProcessor == null) {
|
||||||
|
logger.warn("ActivityType {} is not support.", type)
|
||||||
|
throw IllegalStateException("ActivityPubProcessor not found.")
|
||||||
|
}
|
||||||
|
|
||||||
|
val value = objectMapper.treeToValue(jsonString, activityPubProcessor.type())
|
||||||
|
activityPubProcessor.process(ActivityPubProcessContext(value, jsonString, httpRequest, signature, verify))
|
||||||
|
|
||||||
|
logger.info("SUCCESS Process inbox. type: {}", type)
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun verifyHttpSignature(httpRequest: HttpRequest, signature: Signature): Boolean {
|
||||||
|
val user = try {
|
||||||
|
userQueryService.findByKeyId(signature.keyId)
|
||||||
|
} catch (_: FailedToGetResourcesException) {
|
||||||
|
apUserService.fetchPersonWithEntity(signature.keyId).second
|
||||||
|
}
|
||||||
|
|
||||||
|
val verify = signatureVerifier.verify(
|
||||||
|
httpRequest,
|
||||||
|
PublicKey(RsaUtil.decodeRsaPublicKeyPem(user.publicKey), signature.keyId)
|
||||||
|
)
|
||||||
|
|
||||||
|
return verify.success
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseSignatureHeader(httpHeaders: HttpHeaders): Signature? {
|
||||||
|
return try {
|
||||||
|
signatureHeaderParser.parse(httpHeaders)
|
||||||
|
} catch (e: RuntimeException) {
|
||||||
|
logger.trace("FAILED parse signature header", e)
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun process(param: InboxJobParam) = transaction.transaction {
|
||||||
|
val jsonNode = objectMapper.readTree(param.json)
|
||||||
|
|
||||||
|
logger.info("START Process inbox. type: {}", param.type)
|
||||||
|
logger.trace("type: {}\njson: \n{}", param.type, jsonNode.toPrettyString())
|
||||||
|
|
||||||
|
val map = objectMapper.readValue<Map<String, List<String>>>(param.headers)
|
||||||
|
|
||||||
|
val httpRequest = objectMapper.readValue<HttpRequest>(param.httpRequest).copy(headers = HttpHeaders(map))
|
||||||
|
|
||||||
|
logger.trace("Request: {}\nheaders: {}", httpRequest, map)
|
||||||
|
|
||||||
|
val signature = parseSignatureHeader(httpRequest.headers)
|
||||||
|
|
||||||
|
logger.debug("Has signature? {}", signature != null)
|
||||||
|
|
||||||
|
val verify = signature?.let { verifyHttpSignature(httpRequest, it) } ?: false
|
||||||
|
|
||||||
|
logger.debug("Is verifying success? {}", verify)
|
||||||
|
|
||||||
|
val activityPubProcessor = activityPubProcessorList.firstOrNull { it.isSupported(param.type) }
|
||||||
|
|
||||||
|
if (activityPubProcessor == null) {
|
||||||
|
logger.warn("ActivityType {} is not support.", param.type)
|
||||||
|
throw IllegalStateException("ActivityPubProcessor not found.")
|
||||||
|
}
|
||||||
|
|
||||||
|
val value = objectMapper.treeToValue(jsonNode, activityPubProcessor.type())
|
||||||
|
activityPubProcessor.process(ActivityPubProcessContext(value, jsonNode, httpRequest, signature, verify))
|
||||||
|
|
||||||
|
logger.info("SUCCESS Process inbox. type: {}", param.type)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun job(): InboxJob = InboxJob
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val logger = LoggerFactory.getLogger(InboxJobProcessor::class.java)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
package dev.usbharu.hideout.activitypub.service.objects.note
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
|
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.external.job.DeliverPostJob
|
||||||
|
import dev.usbharu.hideout.core.external.job.DeliverPostJobParam
|
||||||
|
import dev.usbharu.hideout.core.query.UserQueryService
|
||||||
|
import dev.usbharu.hideout.core.service.job.JobProcessor
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
|
||||||
|
class ApNoteJobProcessor(
|
||||||
|
private val transaction: Transaction,
|
||||||
|
private val objectMapper: ObjectMapper,
|
||||||
|
private val userQueryService: UserQueryService,
|
||||||
|
private val apRequestService: APRequestService
|
||||||
|
) : JobProcessor<DeliverPostJobParam, DeliverPostJob> {
|
||||||
|
override suspend fun process(param: DeliverPostJobParam) {
|
||||||
|
val create = objectMapper.readValue<Create>(param.create)
|
||||||
|
transaction.transaction {
|
||||||
|
val signer = userQueryService.findByUrl(param.actor)
|
||||||
|
|
||||||
|
logger.debug("CreateNoteJob: actor: {} create: {} inbox: {}", param.actor, create, param.inbox)
|
||||||
|
|
||||||
|
apRequestService.apPost(
|
||||||
|
param.inbox,
|
||||||
|
create,
|
||||||
|
signer
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun job(): DeliverPostJob = DeliverPostJob
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val logger = LoggerFactory.getLogger(ApNoteJobProcessor::class.java)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +0,0 @@
|
||||||
package dev.usbharu.hideout.activitypub.service.objects.note
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.core.external.job.DeliverPostJob
|
|
||||||
import kjob.core.job.JobProps
|
|
||||||
|
|
||||||
interface ApNoteJobService {
|
|
||||||
suspend fun createNoteJob(props: JobProps<DeliverPostJob>)
|
|
||||||
}
|
|
|
@ -1,41 +0,0 @@
|
||||||
package dev.usbharu.hideout.activitypub.service.objects.note
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper
|
|
||||||
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.external.job.DeliverPostJob
|
|
||||||
import dev.usbharu.hideout.core.query.UserQueryService
|
|
||||||
import kjob.core.job.JobProps
|
|
||||||
import org.slf4j.LoggerFactory
|
|
||||||
import org.springframework.beans.factory.annotation.Qualifier
|
|
||||||
import org.springframework.stereotype.Component
|
|
||||||
|
|
||||||
@Component
|
|
||||||
class ApNoteJobServiceImpl(
|
|
||||||
private val userQueryService: UserQueryService,
|
|
||||||
private val apRequestService: APRequestService,
|
|
||||||
@Qualifier("activitypub") private val objectMapper: ObjectMapper,
|
|
||||||
private val transaction: Transaction
|
|
||||||
) : ApNoteJobService {
|
|
||||||
override suspend fun createNoteJob(props: JobProps<DeliverPostJob>) {
|
|
||||||
val actor = props[DeliverPostJob.actor]
|
|
||||||
val create = objectMapper.readValue<Create>(props[DeliverPostJob.create])
|
|
||||||
transaction.transaction {
|
|
||||||
val signer = userQueryService.findByUrl(actor)
|
|
||||||
|
|
||||||
val inbox = props[DeliverPostJob.inbox]
|
|
||||||
logger.debug("createNoteJob: actor={}, create={}, inbox={}", actor, create, inbox)
|
|
||||||
apRequestService.apPost(
|
|
||||||
inbox,
|
|
||||||
create,
|
|
||||||
signer
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val logger = LoggerFactory.getLogger(ApNoteJobServiceImpl::class.java)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -77,6 +77,7 @@ class APUserServiceImpl(
|
||||||
override suspend fun fetchPerson(url: String, targetActor: String?): Person =
|
override suspend fun fetchPerson(url: String, targetActor: String?): Person =
|
||||||
fetchPersonWithEntity(url, targetActor).first
|
fetchPersonWithEntity(url, targetActor).first
|
||||||
|
|
||||||
|
@Suppress("LongMethod")
|
||||||
override suspend fun fetchPersonWithEntity(url: String, targetActor: String?): Pair<Person, User> {
|
override suspend fun fetchPersonWithEntity(url: String, targetActor: String?): Pair<Person, User> {
|
||||||
return try {
|
return try {
|
||||||
val userEntity = userQueryService.findByUrl(url)
|
val userEntity = userQueryService.findByUrl(url)
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package dev.usbharu.hideout.application.config
|
package dev.usbharu.hideout.application.config
|
||||||
|
|
||||||
import dev.usbharu.hideout.activitypub.service.common.ApJobService
|
|
||||||
import dev.usbharu.hideout.core.external.job.HideoutJob
|
import dev.usbharu.hideout.core.external.job.HideoutJob
|
||||||
import dev.usbharu.hideout.core.service.job.JobQueueParentService
|
import dev.usbharu.hideout.core.service.job.JobQueueParentService
|
||||||
import dev.usbharu.hideout.core.service.job.JobQueueWorkerService
|
import dev.usbharu.hideout.core.service.job.JobQueueWorkerService
|
||||||
|
@ -11,7 +10,10 @@ import org.springframework.boot.ApplicationRunner
|
||||||
import org.springframework.stereotype.Component
|
import org.springframework.stereotype.Component
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
class JobQueueRunner(private val jobQueueParentService: JobQueueParentService, private val jobs: List<HideoutJob>) :
|
class JobQueueRunner(
|
||||||
|
private val jobQueueParentService: JobQueueParentService,
|
||||||
|
private val jobs: List<HideoutJob<*, *>>
|
||||||
|
) :
|
||||||
ApplicationRunner {
|
ApplicationRunner {
|
||||||
override fun run(args: ApplicationArguments?) {
|
override fun run(args: ApplicationArguments?) {
|
||||||
LOGGER.info("Init job queue. ${jobs.size}")
|
LOGGER.info("Init job queue. ${jobs.size}")
|
||||||
|
@ -26,24 +28,10 @@ class JobQueueRunner(private val jobQueueParentService: JobQueueParentService, p
|
||||||
@Component
|
@Component
|
||||||
class JobQueueWorkerRunner(
|
class JobQueueWorkerRunner(
|
||||||
private val jobQueueWorkerService: JobQueueWorkerService,
|
private val jobQueueWorkerService: JobQueueWorkerService,
|
||||||
private val jobs: List<HideoutJob>,
|
|
||||||
private val apJobService: ApJobService
|
|
||||||
) : ApplicationRunner {
|
) : ApplicationRunner {
|
||||||
override fun run(args: ApplicationArguments?) {
|
override fun run(args: ApplicationArguments?) {
|
||||||
LOGGER.info("Init job queue worker.")
|
LOGGER.info("Init job queue worker.")
|
||||||
jobQueueWorkerService.init(
|
jobQueueWorkerService.init<Any?, HideoutJob<*, *>>(emptyList())
|
||||||
jobs.map {
|
|
||||||
it to {
|
|
||||||
execute {
|
|
||||||
LOGGER.debug("excute job ${it.name}")
|
|
||||||
apJobService.processActivity(
|
|
||||||
job = this,
|
|
||||||
hideoutJob = it
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
@file:Suppress("Filename")
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.domain.model.instance
|
package dev.usbharu.hideout.core.domain.model.instance
|
||||||
|
|
||||||
@Suppress("ClassNaming")
|
@Suppress("ClassNaming")
|
||||||
|
|
|
@ -1,46 +1,159 @@
|
||||||
package dev.usbharu.hideout.core.external.job
|
package dev.usbharu.hideout.core.external.job
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.activitypub.service.common.ActivityType
|
||||||
import kjob.core.Job
|
import kjob.core.Job
|
||||||
import kjob.core.Prop
|
import kjob.core.Prop
|
||||||
|
import kjob.core.dsl.ScheduleContext
|
||||||
|
import kjob.core.job.JobProps
|
||||||
import org.springframework.stereotype.Component
|
import org.springframework.stereotype.Component
|
||||||
|
|
||||||
sealed class HideoutJob(name: String = "") : Job(name)
|
abstract class HideoutJob<out T, out R : HideoutJob<T, R>>(name: String = "") : Job(name) {
|
||||||
|
abstract fun convert(value: @UnsafeVariance T): ScheduleContext<@UnsafeVariance R>.(@UnsafeVariance R) -> Unit
|
||||||
|
fun convertUnsafe(props: JobProps<*>): T = convert(props as JobProps<R>)
|
||||||
|
abstract fun convert(props: JobProps<@UnsafeVariance R>): T
|
||||||
|
}
|
||||||
|
|
||||||
|
data class ReceiveFollowJobParam(
|
||||||
|
val actor: String,
|
||||||
|
val follow: String,
|
||||||
|
val targetActor: String
|
||||||
|
)
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
object ReceiveFollowJob : HideoutJob("ReceiveFollowJob") {
|
object ReceiveFollowJob : HideoutJob<ReceiveFollowJobParam, ReceiveFollowJob>("ReceiveFollowJob") {
|
||||||
val actor: Prop<ReceiveFollowJob, String> = string("actor")
|
val actor: Prop<ReceiveFollowJob, String> = string("actor")
|
||||||
val follow: Prop<ReceiveFollowJob, String> = string("follow")
|
val follow: Prop<ReceiveFollowJob, String> = string("follow")
|
||||||
val targetActor: Prop<ReceiveFollowJob, String> = string("targetActor")
|
val targetActor: Prop<ReceiveFollowJob, String> = string("targetActor")
|
||||||
|
|
||||||
|
override fun convert(value: ReceiveFollowJobParam): ScheduleContext<ReceiveFollowJob>.(ReceiveFollowJob) -> Unit = {
|
||||||
|
props[follow] = value.follow
|
||||||
|
props[actor] = value.actor
|
||||||
|
props[targetActor] = value.targetActor
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun convert(props: JobProps<ReceiveFollowJob>): ReceiveFollowJobParam = ReceiveFollowJobParam(
|
||||||
|
actor = props[actor],
|
||||||
|
follow = props[follow],
|
||||||
|
targetActor = props[targetActor]
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class DeliverPostJobParam(
|
||||||
|
val create: String,
|
||||||
|
val inbox: String,
|
||||||
|
val actor: String
|
||||||
|
)
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
object DeliverPostJob : HideoutJob("DeliverPostJob") {
|
object DeliverPostJob : HideoutJob<DeliverPostJobParam, DeliverPostJob>("DeliverPostJob") {
|
||||||
val create = string("create")
|
val create = string("create")
|
||||||
val inbox = string("inbox")
|
val inbox = string("inbox")
|
||||||
val actor = string("actor")
|
val actor = string("actor")
|
||||||
|
override fun convert(value: DeliverPostJobParam): ScheduleContext<DeliverPostJob>.(DeliverPostJob) -> Unit = {
|
||||||
|
props[create] = value.create
|
||||||
|
props[inbox] = value.inbox
|
||||||
|
props[actor] = value.actor
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun convert(props: JobProps<DeliverPostJob>): DeliverPostJobParam = DeliverPostJobParam(
|
||||||
|
create = props[create],
|
||||||
|
inbox = props[inbox],
|
||||||
|
actor = props[actor]
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class DeliverReactionJobParam(
|
||||||
|
val reaction: String,
|
||||||
|
val postUrl: String,
|
||||||
|
val actor: String,
|
||||||
|
val inbox: String,
|
||||||
|
val id: String
|
||||||
|
)
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
object DeliverReactionJob : HideoutJob("DeliverReactionJob") {
|
object DeliverReactionJob : HideoutJob<DeliverReactionJobParam, DeliverReactionJob>("DeliverReactionJob") {
|
||||||
val reaction: Prop<DeliverReactionJob, String> = string("reaction")
|
val reaction: Prop<DeliverReactionJob, String> = string("reaction")
|
||||||
val postUrl: Prop<DeliverReactionJob, String> = string("postUrl")
|
val postUrl: Prop<DeliverReactionJob, String> = string("postUrl")
|
||||||
val actor: Prop<DeliverReactionJob, String> = string("actor")
|
val actor: Prop<DeliverReactionJob, String> = string("actor")
|
||||||
val inbox: Prop<DeliverReactionJob, String> = string("inbox")
|
val inbox: Prop<DeliverReactionJob, String> = string("inbox")
|
||||||
val id: Prop<DeliverReactionJob, String> = string("id")
|
val id: Prop<DeliverReactionJob, String> = string("id")
|
||||||
|
override fun convert(
|
||||||
|
value: DeliverReactionJobParam
|
||||||
|
): ScheduleContext<DeliverReactionJob>.(DeliverReactionJob) -> Unit =
|
||||||
|
{
|
||||||
|
props[reaction] = value.reaction
|
||||||
|
props[postUrl] = value.postUrl
|
||||||
|
props[actor] = value.actor
|
||||||
|
props[inbox] = value.inbox
|
||||||
|
props[id] = value.id
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun convert(props: JobProps<DeliverReactionJob>): DeliverReactionJobParam = DeliverReactionJobParam(
|
||||||
|
props[reaction],
|
||||||
|
props[postUrl],
|
||||||
|
props[actor],
|
||||||
|
props[inbox],
|
||||||
|
props[id]
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class DeliverRemoveReactionJobParam(
|
||||||
|
val id: String,
|
||||||
|
val inbox: String,
|
||||||
|
val actor: String,
|
||||||
|
val like: String
|
||||||
|
)
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
object DeliverRemoveReactionJob : HideoutJob("DeliverRemoveReactionJob") {
|
object DeliverRemoveReactionJob :
|
||||||
|
HideoutJob<DeliverRemoveReactionJobParam, DeliverRemoveReactionJob>("DeliverRemoveReactionJob") {
|
||||||
val id: Prop<DeliverRemoveReactionJob, String> = string("id")
|
val id: Prop<DeliverRemoveReactionJob, String> = string("id")
|
||||||
val inbox: Prop<DeliverRemoveReactionJob, String> = string("inbox")
|
val inbox: Prop<DeliverRemoveReactionJob, String> = string("inbox")
|
||||||
val actor: Prop<DeliverRemoveReactionJob, String> = string("actor")
|
val actor: Prop<DeliverRemoveReactionJob, String> = string("actor")
|
||||||
val like: Prop<DeliverRemoveReactionJob, String> = string("like")
|
val like: Prop<DeliverRemoveReactionJob, String> = string("like")
|
||||||
|
|
||||||
|
override fun convert(value: DeliverRemoveReactionJobParam): ScheduleContext<DeliverRemoveReactionJob>.(DeliverRemoveReactionJob) -> Unit =
|
||||||
|
{
|
||||||
|
props[id] = value.id
|
||||||
|
props[inbox] = value.inbox
|
||||||
|
props[actor] = value.actor
|
||||||
|
props[like] = value.like
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun convert(props: JobProps<DeliverRemoveReactionJob>): DeliverRemoveReactionJobParam =
|
||||||
|
DeliverRemoveReactionJobParam(
|
||||||
|
id = props[id],
|
||||||
|
inbox = props[inbox],
|
||||||
|
actor = props[actor],
|
||||||
|
like = props[like]
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class InboxJobParam(
|
||||||
|
val json: String,
|
||||||
|
val type: ActivityType,
|
||||||
|
val httpRequest: String,
|
||||||
|
val headers: String
|
||||||
|
)
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
object InboxJob : HideoutJob("InboxJob") {
|
object InboxJob : HideoutJob<InboxJobParam, InboxJob>("InboxJob") {
|
||||||
val json = string("json")
|
val json = string("json")
|
||||||
val type = string("type")
|
val type = string("type")
|
||||||
val httpRequest = string("http_request")
|
val httpRequest = string("http_request")
|
||||||
val headers = string("headers")
|
val headers = string("headers")
|
||||||
|
|
||||||
|
override fun convert(value: InboxJobParam): ScheduleContext<InboxJob>.(InboxJob) -> Unit = {
|
||||||
|
props[json] = value.json
|
||||||
|
props[type] = value.type.name
|
||||||
|
props[httpRequest] = value.httpRequest
|
||||||
|
props[headers] = value.headers
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun convert(props: JobProps<InboxJob>): InboxJobParam = InboxJobParam(
|
||||||
|
props[json],
|
||||||
|
ActivityType.valueOf(props[type]),
|
||||||
|
props[httpRequest],
|
||||||
|
props[headers]
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package dev.usbharu.hideout.core.infrastructure.kjobexposed
|
package dev.usbharu.hideout.core.infrastructure.kjobexposed
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.core.external.job.HideoutJob
|
||||||
import dev.usbharu.hideout.core.service.job.JobQueueParentService
|
import dev.usbharu.hideout.core.service.job.JobQueueParentService
|
||||||
import kjob.core.Job
|
import kjob.core.Job
|
||||||
import kjob.core.KJob
|
import kjob.core.KJob
|
||||||
|
@ -29,4 +30,9 @@ class KJobJobQueueParentService() : JobQueueParentService {
|
||||||
logger.debug("schedule job={}", job.name)
|
logger.debug("schedule job={}", job.name)
|
||||||
kjob.schedule(job, block)
|
kjob.schedule(job, block)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun <T, J : HideoutJob<T, J>> scheduleTypeSafe(job: J, jobProps: T) {
|
||||||
|
val convert: ScheduleContext<J>.(J) -> Unit = job.convert(jobProps)
|
||||||
|
kjob.schedule(job, convert)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,19 @@
|
||||||
package dev.usbharu.hideout.core.infrastructure.kjobexposed
|
package dev.usbharu.hideout.core.infrastructure.kjobexposed
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.core.external.job.HideoutJob
|
||||||
|
import dev.usbharu.hideout.core.service.job.JobProcessor
|
||||||
import dev.usbharu.hideout.core.service.job.JobQueueWorkerService
|
import dev.usbharu.hideout.core.service.job.JobQueueWorkerService
|
||||||
|
import kjob.core.dsl.JobContextWithProps
|
||||||
import kjob.core.dsl.JobRegisterContext
|
import kjob.core.dsl.JobRegisterContext
|
||||||
import kjob.core.dsl.KJobFunctions
|
import kjob.core.dsl.KJobFunctions
|
||||||
import kjob.core.kjob
|
import kjob.core.kjob
|
||||||
import org.jetbrains.exposed.sql.transactions.TransactionManager
|
import org.jetbrains.exposed.sql.transactions.TransactionManager
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
import dev.usbharu.hideout.core.external.job.HideoutJob as HJ
|
|
||||||
import kjob.core.dsl.JobContextWithProps as JCWP
|
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@ConditionalOnProperty(name = ["hideout.use-mongodb"], havingValue = "false", matchIfMissing = true)
|
@ConditionalOnProperty(name = ["hideout.use-mongodb"], havingValue = "false", matchIfMissing = true)
|
||||||
class KJobJobQueueWorkerService() : JobQueueWorkerService {
|
class KJobJobQueueWorkerService(private val jobQueueProcessorList: List<JobProcessor<*, *>>) : JobQueueWorkerService {
|
||||||
|
|
||||||
val kjob by lazy {
|
val kjob by lazy {
|
||||||
kjob(ExposedKJob) {
|
kjob(ExposedKJob) {
|
||||||
|
@ -23,11 +24,21 @@ class KJobJobQueueWorkerService() : JobQueueWorkerService {
|
||||||
}.start()
|
}.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun init(
|
override fun <T, R : HideoutJob<T, R>> init(
|
||||||
defines: List<Pair<HJ, JobRegisterContext<HJ, JCWP<HJ>>.(HJ) -> KJobFunctions<HJ, JCWP<HJ>>>>
|
defines:
|
||||||
|
List<Pair<R, JobRegisterContext<R, JobContextWithProps<R>>.(R) -> KJobFunctions<R, JobContextWithProps<R>>>>
|
||||||
) {
|
) {
|
||||||
defines.forEach { job ->
|
defines.forEach { job ->
|
||||||
kjob.register(job.first, job.second)
|
kjob.register(job.first, job.second)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (jobProcessor in jobQueueProcessorList) {
|
||||||
|
kjob.register(jobProcessor.job()) {
|
||||||
|
execute {
|
||||||
|
val param = it.convertUnsafe(props)
|
||||||
|
jobProcessor.process(param)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
package dev.usbharu.hideout.core.infrastructure.kjobmongodb
|
package dev.usbharu.hideout.core.infrastructure.kjobmongodb
|
||||||
|
|
||||||
import com.mongodb.reactivestreams.client.MongoClient
|
import com.mongodb.reactivestreams.client.MongoClient
|
||||||
|
import dev.usbharu.hideout.core.external.job.HideoutJob
|
||||||
import dev.usbharu.hideout.core.service.job.JobQueueWorkerService
|
import dev.usbharu.hideout.core.service.job.JobQueueWorkerService
|
||||||
|
import kjob.core.dsl.JobContextWithProps
|
||||||
import kjob.core.dsl.JobRegisterContext
|
import kjob.core.dsl.JobRegisterContext
|
||||||
import kjob.core.dsl.KJobFunctions
|
import kjob.core.dsl.KJobFunctions
|
||||||
import kjob.core.kjob
|
import kjob.core.kjob
|
||||||
import kjob.mongo.Mongo
|
import kjob.mongo.Mongo
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
import dev.usbharu.hideout.core.external.job.HideoutJob as HJ
|
|
||||||
import kjob.core.dsl.JobContextWithProps as JCWP
|
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@ConditionalOnProperty(name = ["hideout.use-mongodb"], havingValue = "true", matchIfMissing = false)
|
@ConditionalOnProperty(name = ["hideout.use-mongodb"], havingValue = "true", matchIfMissing = false)
|
||||||
|
@ -23,8 +23,9 @@ class KJobMongoJobQueueWorkerService(private val mongoClient: MongoClient) : Job
|
||||||
}.start()
|
}.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun init(
|
override fun <T, R : HideoutJob<T, R>> init(
|
||||||
defines: List<Pair<HJ, JobRegisterContext<HJ, JCWP<HJ>>.(HJ) -> KJobFunctions<HJ, JCWP<HJ>>>>
|
defines:
|
||||||
|
List<Pair<R, JobRegisterContext<R, JobContextWithProps<R>>.(R) -> KJobFunctions<R, JobContextWithProps<R>>>>
|
||||||
) {
|
) {
|
||||||
defines.forEach { job ->
|
defines.forEach { job ->
|
||||||
kjob.register(job.first, job.second)
|
kjob.register(job.first, job.second)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package dev.usbharu.hideout.core.infrastructure.kjobmongodb
|
package dev.usbharu.hideout.core.infrastructure.kjobmongodb
|
||||||
|
|
||||||
import com.mongodb.reactivestreams.client.MongoClient
|
import com.mongodb.reactivestreams.client.MongoClient
|
||||||
|
import dev.usbharu.hideout.core.external.job.HideoutJob
|
||||||
import dev.usbharu.hideout.core.service.job.JobQueueParentService
|
import dev.usbharu.hideout.core.service.job.JobQueueParentService
|
||||||
import kjob.core.Job
|
import kjob.core.Job
|
||||||
import kjob.core.dsl.ScheduleContext
|
import kjob.core.dsl.ScheduleContext
|
||||||
|
@ -23,10 +24,15 @@ class KjobMongoJobQueueParentService(private val mongoClient: MongoClient) : Job
|
||||||
|
|
||||||
override fun init(jobDefines: List<Job>) = Unit
|
override fun init(jobDefines: List<Job>) = Unit
|
||||||
|
|
||||||
|
@Deprecated("use type safe → scheduleTypeSafe")
|
||||||
override suspend fun <J : Job> schedule(job: J, block: ScheduleContext<J>.(J) -> Unit) {
|
override suspend fun <J : Job> schedule(job: J, block: ScheduleContext<J>.(J) -> Unit) {
|
||||||
kjob.schedule(job, block)
|
kjob.schedule(job, block)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun <T, J : HideoutJob<T, J>> scheduleTypeSafe(job: J, jobProps: T) {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
override fun close() {
|
override fun close() {
|
||||||
kjob.shutdown()
|
kjob.shutdown()
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ class HttpSignatureFilter(
|
||||||
transaction.transaction {
|
transaction.transaction {
|
||||||
try {
|
try {
|
||||||
userQueryService.findByKeyId(signature.keyId)
|
userQueryService.findByKeyId(signature.keyId)
|
||||||
} catch (e: FailedToGetResourcesException) {
|
} catch (_: FailedToGetResourcesException) {
|
||||||
apUserService.fetchPerson(signature.keyId)
|
apUserService.fetchPerson(signature.keyId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -272,7 +272,9 @@ class ExposedOAuth2AuthorizationService(
|
||||||
oidcIdTokenValue,
|
oidcIdTokenValue,
|
||||||
oidcTokenIssuedAt,
|
oidcTokenIssuedAt,
|
||||||
oidcTokenExpiresAt,
|
oidcTokenExpiresAt,
|
||||||
oidcTokenMetadata.getValue(OAuth2Authorization.Token.CLAIMS_METADATA_NAME) as MutableMap<String, Any>?
|
@Suppress("CastToNullableType")
|
||||||
|
oidcTokenMetadata.getValue(OAuth2Authorization.Token.CLAIMS_METADATA_NAME)
|
||||||
|
as MutableMap<String, Any>?
|
||||||
)
|
)
|
||||||
|
|
||||||
builder.token(oidcIdToken) { it.putAll(oidcTokenMetadata) }
|
builder.token(oidcIdToken) { it.putAll(oidcTokenMetadata) }
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
package dev.usbharu.hideout.core.service.job
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.core.external.job.HideoutJob
|
||||||
|
|
||||||
|
interface JobProcessor<in T, out R : HideoutJob<@UnsafeVariance T, R>> {
|
||||||
|
suspend fun process(param: @UnsafeVariance T)
|
||||||
|
fun job(): R
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
package dev.usbharu.hideout.core.service.job
|
package dev.usbharu.hideout.core.service.job
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.core.external.job.HideoutJob
|
||||||
import kjob.core.Job
|
import kjob.core.Job
|
||||||
import kjob.core.dsl.ScheduleContext
|
import kjob.core.dsl.ScheduleContext
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
|
@ -8,5 +9,8 @@ import org.springframework.stereotype.Service
|
||||||
interface JobQueueParentService {
|
interface JobQueueParentService {
|
||||||
|
|
||||||
fun init(jobDefines: List<Job>)
|
fun init(jobDefines: List<Job>)
|
||||||
|
|
||||||
|
@Deprecated("use type safe → scheduleTypeSafe")
|
||||||
suspend fun <J : Job> schedule(job: J, block: ScheduleContext<J>.(J) -> Unit = {})
|
suspend fun <J : Job> schedule(job: J, block: ScheduleContext<J>.(J) -> Unit = {})
|
||||||
|
suspend fun <T, J : HideoutJob<T, J>> scheduleTypeSafe(job: J, jobProps: T)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ import kjob.core.dsl.JobRegisterContext as JRC
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
interface JobQueueWorkerService {
|
interface JobQueueWorkerService {
|
||||||
fun init(
|
fun <T, R : HJ<T, R>> init(
|
||||||
defines: List<Pair<HJ, JRC<HJ, JCWP<HJ>>.(HJ) -> KJobFunctions<HJ, JCWP<HJ>>>>
|
defines: List<Pair<R, JRC<R, JCWP<R>>.(R) -> KJobFunctions<R, JCWP<R>>>>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,7 @@ class MovieMediaProcessService : MediaProcessService {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("LongMethod", "NestedBlockDepth", "CognitiveComplexMethod")
|
||||||
override suspend fun process(
|
override suspend fun process(
|
||||||
mimeType: MimeType,
|
mimeType: MimeType,
|
||||||
fileName: String,
|
fileName: String,
|
||||||
|
|
|
@ -4,7 +4,6 @@ import dev.usbharu.hideout.activitypub.domain.exception.JsonParseException
|
||||||
import dev.usbharu.hideout.activitypub.service.common.APService
|
import dev.usbharu.hideout.activitypub.service.common.APService
|
||||||
import dev.usbharu.hideout.activitypub.service.common.ActivityType
|
import dev.usbharu.hideout.activitypub.service.common.ActivityType
|
||||||
import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException
|
import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException
|
||||||
import io.ktor.http.*
|
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.junit.jupiter.api.BeforeEach
|
import org.junit.jupiter.api.BeforeEach
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
|
@ -12,10 +11,7 @@ import org.junit.jupiter.api.extension.ExtendWith
|
||||||
import org.mockito.InjectMocks
|
import org.mockito.InjectMocks
|
||||||
import org.mockito.Mock
|
import org.mockito.Mock
|
||||||
import org.mockito.junit.jupiter.MockitoExtension
|
import org.mockito.junit.jupiter.MockitoExtension
|
||||||
import org.mockito.kotlin.doReturn
|
import org.mockito.kotlin.*
|
||||||
import org.mockito.kotlin.doThrow
|
|
||||||
import org.mockito.kotlin.eq
|
|
||||||
import org.mockito.kotlin.whenever
|
|
||||||
import org.springframework.http.MediaType
|
import org.springframework.http.MediaType
|
||||||
import org.springframework.test.web.servlet.MockMvc
|
import org.springframework.test.web.servlet.MockMvc
|
||||||
import org.springframework.test.web.servlet.get
|
import org.springframework.test.web.servlet.get
|
||||||
|
@ -44,11 +40,15 @@ class InboxControllerImplTest {
|
||||||
|
|
||||||
val json = """{"type":"Follow"}"""
|
val json = """{"type":"Follow"}"""
|
||||||
whenever(apService.parseActivity(eq(json))).doReturn(ActivityType.Follow)
|
whenever(apService.parseActivity(eq(json))).doReturn(ActivityType.Follow)
|
||||||
whenever(apService.processActivity(eq(json), eq(ActivityType.Follow))).doReturn(
|
whenever(
|
||||||
ActivityPubStringResponse(
|
apService.processActivity(
|
||||||
HttpStatusCode.Accepted, ""
|
eq(json),
|
||||||
|
eq(ActivityType.Follow),
|
||||||
|
any(),
|
||||||
|
any()
|
||||||
|
|
||||||
)
|
)
|
||||||
)
|
).doReturn(Unit)
|
||||||
|
|
||||||
mockMvc
|
mockMvc
|
||||||
.post("/inbox") {
|
.post("/inbox") {
|
||||||
|
@ -86,7 +86,9 @@ class InboxControllerImplTest {
|
||||||
whenever(
|
whenever(
|
||||||
apService.processActivity(
|
apService.processActivity(
|
||||||
eq(json),
|
eq(json),
|
||||||
eq(ActivityType.Follow)
|
eq(ActivityType.Follow),
|
||||||
|
any(),
|
||||||
|
any()
|
||||||
)
|
)
|
||||||
).doThrow(FailedToGetResourcesException::class)
|
).doThrow(FailedToGetResourcesException::class)
|
||||||
|
|
||||||
|
@ -113,10 +115,8 @@ class InboxControllerImplTest {
|
||||||
|
|
||||||
val json = """{"type":"Follow"}"""
|
val json = """{"type":"Follow"}"""
|
||||||
whenever(apService.parseActivity(eq(json))).doReturn(ActivityType.Follow)
|
whenever(apService.parseActivity(eq(json))).doReturn(ActivityType.Follow)
|
||||||
whenever(apService.processActivity(eq(json), eq(ActivityType.Follow))).doReturn(
|
whenever(apService.processActivity(eq(json), eq(ActivityType.Follow), any(), any())).doReturn(
|
||||||
ActivityPubStringResponse(
|
Unit
|
||||||
HttpStatusCode.Accepted, ""
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
mockMvc
|
mockMvc
|
||||||
|
@ -155,7 +155,9 @@ class InboxControllerImplTest {
|
||||||
whenever(
|
whenever(
|
||||||
apService.processActivity(
|
apService.processActivity(
|
||||||
eq(json),
|
eq(json),
|
||||||
eq(ActivityType.Follow)
|
eq(ActivityType.Follow),
|
||||||
|
any(),
|
||||||
|
any()
|
||||||
)
|
)
|
||||||
).doThrow(FailedToGetResourcesException::class)
|
).doThrow(FailedToGetResourcesException::class)
|
||||||
|
|
||||||
|
|
|
@ -1,109 +0,0 @@
|
||||||
package dev.usbharu.hideout.activitypub.service.activity.accept
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.exception.IllegalActivityPubObjectException
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.Accept
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.Follow
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.Like
|
|
||||||
import dev.usbharu.hideout.core.query.FollowerQueryService
|
|
||||||
import dev.usbharu.hideout.core.query.UserQueryService
|
|
||||||
import dev.usbharu.hideout.core.service.user.UserService
|
|
||||||
import io.ktor.http.*
|
|
||||||
import kotlinx.coroutines.test.runTest
|
|
||||||
import org.junit.jupiter.api.Assertions.assertEquals
|
|
||||||
import org.junit.jupiter.api.Test
|
|
||||||
import org.junit.jupiter.api.assertThrows
|
|
||||||
import org.mockito.kotlin.*
|
|
||||||
import utils.TestTransaction
|
|
||||||
import utils.UserBuilder
|
|
||||||
|
|
||||||
class APAcceptServiceImplTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `receiveAccept 正常なAcceptを処理できる`() = runTest {
|
|
||||||
val actor = "https://example.com"
|
|
||||||
val follower = "https://follower.example.com"
|
|
||||||
val targetUser = UserBuilder.localUserOf()
|
|
||||||
val followerUser = UserBuilder.localUserOf()
|
|
||||||
val userQueryService = mock<UserQueryService> {
|
|
||||||
onBlocking { findByUrl(eq(actor)) } doReturn targetUser
|
|
||||||
onBlocking { findByUrl(eq(follower)) } doReturn followerUser
|
|
||||||
}
|
|
||||||
val followerQueryService = mock<FollowerQueryService> {
|
|
||||||
onBlocking { alreadyFollow(eq(targetUser.id), eq(followerUser.id)) } doReturn false
|
|
||||||
}
|
|
||||||
val userService = mock<UserService>()
|
|
||||||
val apAcceptServiceImpl =
|
|
||||||
APAcceptServiceImpl(userService, userQueryService, followerQueryService, TestTransaction)
|
|
||||||
|
|
||||||
val accept = Accept(
|
|
||||||
name = "Accept",
|
|
||||||
`object` = Follow(
|
|
||||||
name = "",
|
|
||||||
`object` = actor,
|
|
||||||
actor = follower
|
|
||||||
),
|
|
||||||
actor = actor
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
val actual = apAcceptServiceImpl.receiveAccept(accept)
|
|
||||||
assertEquals(ActivityPubStringResponse(HttpStatusCode.OK, "accepted"), actual)
|
|
||||||
verify(userService, times(1)).follow(eq(targetUser.id), eq(followerUser.id))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `receiveAccept 既にフォローしている場合は無視する`() = runTest {
|
|
||||||
|
|
||||||
val actor = "https://example.com"
|
|
||||||
val follower = "https://follower.example.com"
|
|
||||||
val targetUser = UserBuilder.localUserOf()
|
|
||||||
val followerUser = UserBuilder.localUserOf()
|
|
||||||
val userQueryService = mock<UserQueryService> {
|
|
||||||
onBlocking { findByUrl(eq(actor)) } doReturn targetUser
|
|
||||||
onBlocking { findByUrl(eq(follower)) } doReturn followerUser
|
|
||||||
}
|
|
||||||
val followerQueryService = mock<FollowerQueryService> {
|
|
||||||
onBlocking { alreadyFollow(eq(targetUser.id), eq(followerUser.id)) } doReturn true
|
|
||||||
}
|
|
||||||
val userService = mock<UserService>()
|
|
||||||
val apAcceptServiceImpl =
|
|
||||||
APAcceptServiceImpl(userService, userQueryService, followerQueryService, TestTransaction)
|
|
||||||
|
|
||||||
val accept = Accept(
|
|
||||||
name = "Accept",
|
|
||||||
`object` = Follow(
|
|
||||||
name = "",
|
|
||||||
`object` = actor,
|
|
||||||
actor = follower
|
|
||||||
),
|
|
||||||
actor = actor
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
val actual = apAcceptServiceImpl.receiveAccept(accept)
|
|
||||||
assertEquals(ActivityPubStringResponse(HttpStatusCode.OK, "accepted"), actual)
|
|
||||||
verify(userService, times(0)).follow(eq(targetUser.id), eq(followerUser.id))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `revieveAccept AcceptのobjectのtypeがFollow以外の場合IllegalActivityPubObjectExceptionがthrowされる`() =
|
|
||||||
runTest {
|
|
||||||
val accept = Accept(
|
|
||||||
name = "Accept",
|
|
||||||
`object` = Like(
|
|
||||||
name = "Like",
|
|
||||||
actor = "actor",
|
|
||||||
id = "https://example.com",
|
|
||||||
`object` = "https://example.com",
|
|
||||||
content = "aaaa"
|
|
||||||
),
|
|
||||||
actor = "https://example.com"
|
|
||||||
)
|
|
||||||
|
|
||||||
val apAcceptServiceImpl = APAcceptServiceImpl(mock(), mock(), mock(), TestTransaction)
|
|
||||||
|
|
||||||
assertThrows<IllegalActivityPubObjectException> {
|
|
||||||
apAcceptServiceImpl.receiveAccept(accept)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,63 +0,0 @@
|
||||||
package dev.usbharu.hideout.activitypub.service.activity.create
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.exception.IllegalActivityPubObjectException
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.Create
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.Like
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.Note
|
|
||||||
import dev.usbharu.hideout.activitypub.service.objects.note.APNoteService
|
|
||||||
import io.ktor.http.*
|
|
||||||
import kotlinx.coroutines.test.runTest
|
|
||||||
import org.junit.jupiter.api.Assertions.assertEquals
|
|
||||||
import org.junit.jupiter.api.Test
|
|
||||||
import org.junit.jupiter.api.assertThrows
|
|
||||||
import org.mockito.kotlin.*
|
|
||||||
import utils.TestTransaction
|
|
||||||
|
|
||||||
class APCreateServiceImplTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `receiveCreate 正常なCreateを処理できる`() = runTest {
|
|
||||||
val create = Create(
|
|
||||||
name = "Create",
|
|
||||||
`object` = Note(
|
|
||||||
name = "Note",
|
|
||||||
id = "https://example.com/note",
|
|
||||||
attributedTo = "https://example.com/actor",
|
|
||||||
content = "Hello World",
|
|
||||||
published = "Date: Wed, 21 Oct 2015 07:28:00 GMT"
|
|
||||||
),
|
|
||||||
actor = "https://example.com/actor",
|
|
||||||
id = "https://example.com/create",
|
|
||||||
)
|
|
||||||
|
|
||||||
val apNoteService = mock<APNoteService>()
|
|
||||||
val apCreateServiceImpl = APCreateServiceImpl(apNoteService, TestTransaction)
|
|
||||||
|
|
||||||
val actual = ActivityPubStringResponse(HttpStatusCode.OK, "Created")
|
|
||||||
|
|
||||||
val receiveCreate = apCreateServiceImpl.receiveCreate(create)
|
|
||||||
verify(apNoteService, times(1)).fetchNote(any<Note>(), anyOrNull())
|
|
||||||
assertEquals(actual, receiveCreate)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `reveiveCreate CreateのobjectのtypeがNote以外の場合IllegalActivityPubObjectExceptionがthrowされる`() = runTest {
|
|
||||||
val create = Create(
|
|
||||||
name = "Create",
|
|
||||||
`object` = Like(
|
|
||||||
name = "Like",
|
|
||||||
id = "https://example.com/note",
|
|
||||||
actor = "https://example.com/actor",
|
|
||||||
`object` = "https://example.com/create",
|
|
||||||
content = "aaa"
|
|
||||||
),
|
|
||||||
actor = "https://example.com/actor",
|
|
||||||
id = "https://example.com/create",
|
|
||||||
)
|
|
||||||
|
|
||||||
val apCreateServiceImpl = APCreateServiceImpl(mock(), TestTransaction)
|
|
||||||
assertThrows<IllegalActivityPubObjectException> {
|
|
||||||
apCreateServiceImpl.receiveCreate(create)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,176 +0,0 @@
|
||||||
@file:OptIn(ExperimentalCoroutinesApi::class)
|
|
||||||
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.activitypub.service.activity.follow
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.Follow
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.Image
|
|
||||||
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.ApplicationConfig
|
|
||||||
import dev.usbharu.hideout.application.config.CharacterLimit
|
|
||||||
import dev.usbharu.hideout.core.domain.model.post.Post
|
|
||||||
import dev.usbharu.hideout.core.domain.model.user.User
|
|
||||||
import dev.usbharu.hideout.core.external.job.ReceiveFollowJob
|
|
||||||
import dev.usbharu.hideout.core.query.UserQueryService
|
|
||||||
import dev.usbharu.hideout.core.service.job.JobQueueParentService
|
|
||||||
import dev.usbharu.hideout.core.service.user.UserService
|
|
||||||
import kjob.core.dsl.ScheduleContext
|
|
||||||
import kjob.core.job.JobProps
|
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
|
||||||
import kotlinx.coroutines.test.runTest
|
|
||||||
import kotlinx.serialization.json.Json
|
|
||||||
import org.junit.jupiter.api.Assertions.assertEquals
|
|
||||||
import org.junit.jupiter.api.Test
|
|
||||||
import org.mockito.ArgumentMatchers.anyString
|
|
||||||
import org.mockito.kotlin.*
|
|
||||||
import utils.JsonObjectMapper.objectMapper
|
|
||||||
import utils.TestTransaction
|
|
||||||
import java.net.URL
|
|
||||||
import java.time.Instant
|
|
||||||
|
|
||||||
class APReceiveFollowServiceImplTest {
|
|
||||||
|
|
||||||
val userBuilder = User.UserBuilder(CharacterLimit(), ApplicationConfig(URL("https://example.com")))
|
|
||||||
val postBuilder = Post.PostBuilder(CharacterLimit())
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `receiveFollow フォロー受付処理`() = runTest {
|
|
||||||
val jobQueueParentService = mock<JobQueueParentService> {
|
|
||||||
onBlocking { schedule(eq(ReceiveFollowJob), any()) } doReturn Unit
|
|
||||||
}
|
|
||||||
val activityPubFollowService =
|
|
||||||
APReceiveFollowServiceImpl(
|
|
||||||
jobQueueParentService,
|
|
||||||
objectMapper
|
|
||||||
)
|
|
||||||
activityPubFollowService.receiveFollow(
|
|
||||||
Follow(
|
|
||||||
emptyList(),
|
|
||||||
"Follow",
|
|
||||||
"https://example.com",
|
|
||||||
"https://follower.example.com"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
verify(jobQueueParentService, times(1)).schedule(eq(ReceiveFollowJob), any())
|
|
||||||
argumentCaptor<ScheduleContext<ReceiveFollowJob>.(ReceiveFollowJob) -> Unit> {
|
|
||||||
verify(jobQueueParentService, times(1)).schedule(eq(ReceiveFollowJob), capture())
|
|
||||||
val scheduleContext = ScheduleContext<ReceiveFollowJob>(Json)
|
|
||||||
firstValue.invoke(scheduleContext, ReceiveFollowJob)
|
|
||||||
val actor = scheduleContext.props.props[ReceiveFollowJob.actor.name]
|
|
||||||
val targetActor = scheduleContext.props.props[ReceiveFollowJob.targetActor.name]
|
|
||||||
val follow = scheduleContext.props.props[ReceiveFollowJob.follow.name] as String
|
|
||||||
assertEquals("https://follower.example.com", actor)
|
|
||||||
assertEquals("https://example.com", targetActor)
|
|
||||||
//language=JSON
|
|
||||||
assertEquals(
|
|
||||||
Json.parseToJsonElement(
|
|
||||||
"""{
|
|
||||||
"type": "Follow",
|
|
||||||
"name": "Follow",
|
|
||||||
"actor": "https://follower.example.com",
|
|
||||||
"object": "https://example.com"
|
|
||||||
|
|
||||||
}"""
|
|
||||||
),
|
|
||||||
Json.parseToJsonElement(follow)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `receiveFollowJob フォロー受付処理のJob`() = runTest {
|
|
||||||
val person = Person(
|
|
||||||
type = emptyList(),
|
|
||||||
name = "follower",
|
|
||||||
id = "https://follower.example.com",
|
|
||||||
preferredUsername = "followerUser",
|
|
||||||
summary = "This user is follower user.",
|
|
||||||
inbox = "https://follower.example.com/inbox",
|
|
||||||
outbox = "https://follower.example.com/outbox",
|
|
||||||
url = "https://follower.example.com",
|
|
||||||
icon = Image(
|
|
||||||
type = emptyList(),
|
|
||||||
name = "https://follower.example.com/image",
|
|
||||||
mediaType = "image/png",
|
|
||||||
url = "https://follower.example.com/image"
|
|
||||||
),
|
|
||||||
publicKey = Key(
|
|
||||||
type = emptyList(),
|
|
||||||
name = "Public Key",
|
|
||||||
id = "https://follower.example.com#main-key",
|
|
||||||
owner = "https://follower.example.com",
|
|
||||||
publicKeyPem = "BEGIN PUBLIC KEY...END PUBLIC KEY",
|
|
||||||
),
|
|
||||||
followers = "",
|
|
||||||
following = ""
|
|
||||||
|
|
||||||
)
|
|
||||||
val apUserService = mock<APUserService> {
|
|
||||||
onBlocking { fetchPerson(anyString(), any()) } doReturn person
|
|
||||||
}
|
|
||||||
val userQueryService = mock<UserQueryService> {
|
|
||||||
onBlocking { findByUrl(eq("https://example.com")) } doReturn
|
|
||||||
userBuilder.of(
|
|
||||||
id = 1L,
|
|
||||||
name = "test",
|
|
||||||
domain = "example.com",
|
|
||||||
screenName = "testUser",
|
|
||||||
description = "This user is test user.",
|
|
||||||
inbox = "https://example.com/inbox",
|
|
||||||
outbox = "https://example.com/outbox",
|
|
||||||
url = "https://example.com",
|
|
||||||
publicKey = "",
|
|
||||||
password = "a",
|
|
||||||
privateKey = "a",
|
|
||||||
createdAt = Instant.now(),
|
|
||||||
keyId = "a"
|
|
||||||
)
|
|
||||||
onBlocking { findByUrl(eq("https://follower.example.com")) } doReturn
|
|
||||||
userBuilder.of(
|
|
||||||
id = 2L,
|
|
||||||
name = "follower",
|
|
||||||
domain = "follower.example.com",
|
|
||||||
screenName = "followerUser",
|
|
||||||
description = "This user is test follower user.",
|
|
||||||
inbox = "https://follower.example.com/inbox",
|
|
||||||
outbox = "https://follower.example.com/outbox",
|
|
||||||
url = "https://follower.example.com",
|
|
||||||
publicKey = "",
|
|
||||||
createdAt = Instant.now(),
|
|
||||||
keyId = "a"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val userService = mock<UserService> {
|
|
||||||
onBlocking { followRequest(any(), any()) } doReturn false
|
|
||||||
}
|
|
||||||
val activityPubFollowService =
|
|
||||||
APReceiveFollowJobServiceImpl(
|
|
||||||
apUserService,
|
|
||||||
userQueryService,
|
|
||||||
mock(),
|
|
||||||
userService,
|
|
||||||
objectMapper,
|
|
||||||
TestTransaction
|
|
||||||
)
|
|
||||||
activityPubFollowService.receiveFollowJob(
|
|
||||||
JobProps(
|
|
||||||
data = mapOf<String, Any>(
|
|
||||||
ReceiveFollowJob.actor.name to "https://follower.example.com",
|
|
||||||
ReceiveFollowJob.targetActor.name to "https://example.com",
|
|
||||||
//language=JSON
|
|
||||||
ReceiveFollowJob.follow.name to """{
|
|
||||||
"type": "Follow",
|
|
||||||
"name": "Follow",
|
|
||||||
"object": "https://example.com",
|
|
||||||
"actor": "https://follower.example.com",
|
|
||||||
"@context": null
|
|
||||||
}"""
|
|
||||||
),
|
|
||||||
json = Json
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,111 +0,0 @@
|
||||||
package dev.usbharu.hideout.activitypub.service.activity.like
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.exception.FailedToGetActivityPubResourceException
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.Like
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.Note
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.Person
|
|
||||||
import dev.usbharu.hideout.activitypub.service.objects.note.APNoteService
|
|
||||||
import dev.usbharu.hideout.activitypub.service.objects.user.APUserService
|
|
||||||
import dev.usbharu.hideout.core.query.PostQueryService
|
|
||||||
import dev.usbharu.hideout.core.service.reaction.ReactionService
|
|
||||||
import io.ktor.http.*
|
|
||||||
import kotlinx.coroutines.async
|
|
||||||
import kotlinx.coroutines.test.runTest
|
|
||||||
import org.junit.jupiter.api.Assertions.assertEquals
|
|
||||||
import org.junit.jupiter.api.Test
|
|
||||||
import org.mockito.kotlin.*
|
|
||||||
import utils.PostBuilder
|
|
||||||
import utils.TestTransaction
|
|
||||||
import utils.UserBuilder
|
|
||||||
|
|
||||||
|
|
||||||
class APLikeServiceImplTest {
|
|
||||||
@Test
|
|
||||||
fun `receiveLike 正常なLikeを処理できる`() = runTest {
|
|
||||||
val actor = "https://example.com/actor"
|
|
||||||
val note = "https://example.com/note"
|
|
||||||
val like = Like(
|
|
||||||
name = "Like", actor = actor, id = "htps://example.com", `object` = note, content = "aaa"
|
|
||||||
)
|
|
||||||
|
|
||||||
val user = UserBuilder.localUserOf()
|
|
||||||
val apUserService = mock<APUserService> {
|
|
||||||
onBlocking { fetchPersonWithEntity(eq(actor), anyOrNull()) } doReturn (Person(
|
|
||||||
name = "TestUser",
|
|
||||||
id = "https://example.com",
|
|
||||||
preferredUsername = "Test user",
|
|
||||||
summary = "test user",
|
|
||||||
inbox = "https://example.com/inbox",
|
|
||||||
outbox = "https://example.com/outbox",
|
|
||||||
url = "https://example.com/",
|
|
||||||
icon = null,
|
|
||||||
publicKey = null,
|
|
||||||
followers = null,
|
|
||||||
following = null
|
|
||||||
) to user)
|
|
||||||
}
|
|
||||||
val apNoteService = mock<APNoteService> {
|
|
||||||
on { fetchNoteAsync(eq(note), anyOrNull()) } doReturn async {
|
|
||||||
Note(
|
|
||||||
name = "Note",
|
|
||||||
id = "https://example.com/note",
|
|
||||||
attributedTo = "https://example.com/actor",
|
|
||||||
content = "Hello World",
|
|
||||||
published = "Date: Wed, 21 Oct 2015 07:28:00 GMT",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val post = PostBuilder.of()
|
|
||||||
val postQueryService = mock<PostQueryService> {
|
|
||||||
onBlocking { findByUrl(eq(note)) } doReturn post
|
|
||||||
}
|
|
||||||
val reactionService = mock<ReactionService>()
|
|
||||||
val apLikeServiceImpl = APLikeServiceImpl(
|
|
||||||
reactionService, apUserService, apNoteService, postQueryService, TestTransaction
|
|
||||||
)
|
|
||||||
|
|
||||||
val actual = apLikeServiceImpl.receiveLike(like)
|
|
||||||
|
|
||||||
verify(reactionService, times(1)).receiveReaction(eq("aaa"), eq("example.com"), eq(user.id), eq(post.id))
|
|
||||||
assertEquals(ActivityPubStringResponse(HttpStatusCode.OK, ""), actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `recieveLike Likeのobjectのurlが取得できないとき何もしない`() = runTest {
|
|
||||||
val actor = "https://example.com/actor"
|
|
||||||
val note = "https://example.com/note"
|
|
||||||
val like = Like(
|
|
||||||
name = "Like", actor = actor, id = "htps://example.com", `object` = note, content = "aaa"
|
|
||||||
)
|
|
||||||
|
|
||||||
val user = UserBuilder.localUserOf()
|
|
||||||
val apUserService = mock<APUserService> {
|
|
||||||
onBlocking { fetchPersonWithEntity(eq(actor), anyOrNull()) } doReturn (Person(
|
|
||||||
name = "TestUser",
|
|
||||||
id = "https://example.com",
|
|
||||||
preferredUsername = "Test user",
|
|
||||||
summary = "test user",
|
|
||||||
inbox = "https://example.com/inbox",
|
|
||||||
outbox = "https://example.com/outbox",
|
|
||||||
url = "https://example.com/",
|
|
||||||
icon = null,
|
|
||||||
publicKey = null,
|
|
||||||
followers = null,
|
|
||||||
following = null
|
|
||||||
) to user)
|
|
||||||
}
|
|
||||||
val apNoteService = mock<APNoteService> {
|
|
||||||
on { fetchNoteAsync(eq(note), anyOrNull()) } doThrow FailedToGetActivityPubResourceException()
|
|
||||||
}
|
|
||||||
|
|
||||||
val reactionService = mock<ReactionService>()
|
|
||||||
val apLikeServiceImpl = APLikeServiceImpl(
|
|
||||||
reactionService, apUserService, apNoteService, mock(), TestTransaction
|
|
||||||
)
|
|
||||||
|
|
||||||
val actual = apLikeServiceImpl.receiveLike(like)
|
|
||||||
|
|
||||||
verify(reactionService, times(0)).receiveReaction(anyOrNull(), anyOrNull(), anyOrNull(), anyOrNull())
|
|
||||||
assertEquals(ActivityPubStringResponse(HttpStatusCode.OK, ""), actual)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,128 +0,0 @@
|
||||||
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.activitypub.service.activity.like
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.Like
|
|
||||||
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.core.external.job.DeliverReactionJob
|
|
||||||
import dev.usbharu.hideout.core.external.job.DeliverRemoveReactionJob
|
|
||||||
import dev.usbharu.hideout.core.query.UserQueryService
|
|
||||||
import kjob.core.job.JobProps
|
|
||||||
import kotlinx.coroutines.test.runTest
|
|
||||||
import kotlinx.serialization.json.Json
|
|
||||||
import org.junit.jupiter.api.Test
|
|
||||||
import org.mockito.Mockito.mockStatic
|
|
||||||
import org.mockito.kotlin.*
|
|
||||||
import utils.JsonObjectMapper.objectMapper
|
|
||||||
import utils.UserBuilder
|
|
||||||
import java.net.URL
|
|
||||||
import java.time.Instant
|
|
||||||
|
|
||||||
class ApReactionJobServiceImplTest {
|
|
||||||
@Test
|
|
||||||
fun `reactionJob Likeが配送される`() = runTest {
|
|
||||||
|
|
||||||
val localUser = UserBuilder.localUserOf()
|
|
||||||
val remoteUser = UserBuilder.remoteUserOf()
|
|
||||||
|
|
||||||
val userQueryService = mock<UserQueryService> {
|
|
||||||
onBlocking { findByUrl(localUser.url) } doReturn localUser
|
|
||||||
}
|
|
||||||
val apRequestService = mock<APRequestService>()
|
|
||||||
val apReactionJobServiceImpl = ApReactionJobServiceImpl(
|
|
||||||
userQueryService = userQueryService,
|
|
||||||
apRequestService = apRequestService,
|
|
||||||
applicationConfig = ApplicationConfig(URL("https://example.com")),
|
|
||||||
objectMapper = objectMapper
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
val postUrl = "${remoteUser.url}/posts/1234"
|
|
||||||
|
|
||||||
apReactionJobServiceImpl.reactionJob(
|
|
||||||
JobProps(
|
|
||||||
data = mapOf(
|
|
||||||
DeliverReactionJob.inbox.name to remoteUser.inbox,
|
|
||||||
DeliverReactionJob.actor.name to localUser.url,
|
|
||||||
DeliverReactionJob.postUrl.name to postUrl,
|
|
||||||
DeliverReactionJob.id.name to "1234",
|
|
||||||
DeliverReactionJob.reaction.name to "❤",
|
|
||||||
|
|
||||||
),
|
|
||||||
json = Json
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
val body = Like(
|
|
||||||
name = "Like",
|
|
||||||
actor = localUser.url,
|
|
||||||
`object` = postUrl,
|
|
||||||
id = "https://example.com/like/note/1234",
|
|
||||||
content = "❤"
|
|
||||||
)
|
|
||||||
|
|
||||||
verify(apRequestService, times(1)).apPost(eq(remoteUser.inbox), eq(body), eq(localUser))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `removeReactionJob LikeのUndoが配送される`() = runTest {
|
|
||||||
|
|
||||||
val localUser = UserBuilder.localUserOf()
|
|
||||||
val remoteUser = UserBuilder.remoteUserOf()
|
|
||||||
|
|
||||||
val userQueryService = mock<UserQueryService> {
|
|
||||||
onBlocking { findByUrl(localUser.url) } doReturn localUser
|
|
||||||
}
|
|
||||||
val apRequestService = mock<APRequestService>()
|
|
||||||
val apReactionJobServiceImpl = ApReactionJobServiceImpl(
|
|
||||||
userQueryService = userQueryService,
|
|
||||||
apRequestService = apRequestService,
|
|
||||||
applicationConfig = ApplicationConfig(URL("https://example.com")),
|
|
||||||
objectMapper = objectMapper
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
val postUrl = "${remoteUser.url}/posts/1234"
|
|
||||||
val like = Like(
|
|
||||||
name = "Like",
|
|
||||||
actor = remoteUser.url,
|
|
||||||
`object` = postUrl,
|
|
||||||
id = "https://example.com/like/note/1234",
|
|
||||||
content = "❤"
|
|
||||||
)
|
|
||||||
|
|
||||||
val now = Instant.now()
|
|
||||||
|
|
||||||
val body = mockStatic(Instant::class.java).use {
|
|
||||||
|
|
||||||
it.`when`<Instant>(Instant::now).thenReturn(now)
|
|
||||||
|
|
||||||
apReactionJobServiceImpl.removeReactionJob(
|
|
||||||
JobProps(
|
|
||||||
data = mapOf(
|
|
||||||
DeliverRemoveReactionJob.inbox.name to remoteUser.inbox,
|
|
||||||
DeliverRemoveReactionJob.actor.name to localUser.url,
|
|
||||||
DeliverRemoveReactionJob.id.name to "1234",
|
|
||||||
DeliverRemoveReactionJob.like.name to objectMapper.writeValueAsString(like),
|
|
||||||
|
|
||||||
),
|
|
||||||
json = Json
|
|
||||||
)
|
|
||||||
)
|
|
||||||
Undo(
|
|
||||||
name = "Undo Reaction",
|
|
||||||
actor = localUser.url,
|
|
||||||
`object` = like,
|
|
||||||
id = "https://example.com/undo/note/1234",
|
|
||||||
published = now
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
verify(apRequestService, times(1)).apPost(eq(remoteUser.inbox), eq(body), eq(localUser))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,47 +0,0 @@
|
||||||
package dev.usbharu.hideout.activitypub.service.activity.undo
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.Follow
|
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.Undo
|
|
||||||
import dev.usbharu.hideout.core.query.UserQueryService
|
|
||||||
import io.ktor.http.*
|
|
||||||
import kotlinx.coroutines.test.runTest
|
|
||||||
import org.junit.jupiter.api.Assertions.assertEquals
|
|
||||||
import org.junit.jupiter.api.Test
|
|
||||||
import org.mockito.kotlin.doReturn
|
|
||||||
import org.mockito.kotlin.eq
|
|
||||||
import org.mockito.kotlin.mock
|
|
||||||
import utils.TestTransaction
|
|
||||||
import utils.UserBuilder
|
|
||||||
import java.time.Instant
|
|
||||||
|
|
||||||
class APUndoServiceImplTest {
|
|
||||||
@Test
|
|
||||||
fun `receiveUndo FollowのUndoを処理できる`() = runTest {
|
|
||||||
|
|
||||||
val userQueryService = mock<UserQueryService> {
|
|
||||||
onBlocking { findByUrl(eq("https://follower.example.com/actor")) } doReturn UserBuilder.remoteUserOf()
|
|
||||||
onBlocking { findByUrl(eq("https://example.com/actor")) } doReturn UserBuilder.localUserOf()
|
|
||||||
}
|
|
||||||
val apUndoServiceImpl = APUndoServiceImpl(
|
|
||||||
userService = mock(),
|
|
||||||
apUserService = mock(),
|
|
||||||
userQueryService = userQueryService,
|
|
||||||
transaction = TestTransaction
|
|
||||||
)
|
|
||||||
|
|
||||||
val undo = Undo(
|
|
||||||
name = "Undo",
|
|
||||||
actor = "https://follower.example.com/actor",
|
|
||||||
id = "https://follower.example.com/undo/follow",
|
|
||||||
`object` = Follow(
|
|
||||||
name = "Follow",
|
|
||||||
`object` = "https://example.com/actor",
|
|
||||||
actor = "https://follower.example.com/actor"
|
|
||||||
),
|
|
||||||
published = Instant.now()
|
|
||||||
)
|
|
||||||
val activityPubResponse = apUndoServiceImpl.receiveUndo(undo)
|
|
||||||
assertEquals(ActivityPubStringResponse(HttpStatusCode.OK, "Accept"), activityPubResponse)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -11,13 +11,7 @@ class APServiceImplTest {
|
||||||
@Test
|
@Test
|
||||||
fun `parseActivity 正常なActivityをパースできる`() {
|
fun `parseActivity 正常なActivityをパースできる`() {
|
||||||
val apServiceImpl = APServiceImpl(
|
val apServiceImpl = APServiceImpl(
|
||||||
apReceiveFollowService = mock(),
|
objectMapper = objectMapper, jobQueueParentService = mock()
|
||||||
apUndoService = mock(),
|
|
||||||
apAcceptService = mock(),
|
|
||||||
apCreateService = mock(),
|
|
||||||
apLikeService = mock(),
|
|
||||||
apReceiveDeleteService = mock(),
|
|
||||||
objectMapper = objectMapper
|
|
||||||
)
|
)
|
||||||
|
|
||||||
//language=JSON
|
//language=JSON
|
||||||
|
@ -29,13 +23,7 @@ class APServiceImplTest {
|
||||||
@Test
|
@Test
|
||||||
fun `parseActivity Typeが配列のActivityをパースできる`() {
|
fun `parseActivity Typeが配列のActivityをパースできる`() {
|
||||||
val apServiceImpl = APServiceImpl(
|
val apServiceImpl = APServiceImpl(
|
||||||
apReceiveFollowService = mock(),
|
objectMapper = objectMapper, jobQueueParentService = mock()
|
||||||
apUndoService = mock(),
|
|
||||||
apAcceptService = mock(),
|
|
||||||
apCreateService = mock(),
|
|
||||||
apLikeService = mock(),
|
|
||||||
apReceiveDeleteService = mock(),
|
|
||||||
objectMapper = objectMapper
|
|
||||||
)
|
)
|
||||||
|
|
||||||
//language=JSON
|
//language=JSON
|
||||||
|
@ -47,13 +35,7 @@ class APServiceImplTest {
|
||||||
@Test
|
@Test
|
||||||
fun `parseActivity Typeが配列で関係ない物が入っていてもパースできる`() {
|
fun `parseActivity Typeが配列で関係ない物が入っていてもパースできる`() {
|
||||||
val apServiceImpl = APServiceImpl(
|
val apServiceImpl = APServiceImpl(
|
||||||
apReceiveFollowService = mock(),
|
objectMapper = objectMapper, jobQueueParentService = mock()
|
||||||
apUndoService = mock(),
|
|
||||||
apAcceptService = mock(),
|
|
||||||
apCreateService = mock(),
|
|
||||||
apLikeService = mock(),
|
|
||||||
apReceiveDeleteService = mock(),
|
|
||||||
objectMapper = objectMapper
|
|
||||||
)
|
)
|
||||||
|
|
||||||
//language=JSON
|
//language=JSON
|
||||||
|
@ -65,13 +47,8 @@ class APServiceImplTest {
|
||||||
@Test
|
@Test
|
||||||
fun `parseActivity jsonとして解釈できない場合JsonParseExceptionがthrowされる`() {
|
fun `parseActivity jsonとして解釈できない場合JsonParseExceptionがthrowされる`() {
|
||||||
val apServiceImpl = APServiceImpl(
|
val apServiceImpl = APServiceImpl(
|
||||||
apReceiveFollowService = mock(),
|
|
||||||
apUndoService = mock(),
|
objectMapper = objectMapper, jobQueueParentService = mock()
|
||||||
apAcceptService = mock(),
|
|
||||||
apCreateService = mock(),
|
|
||||||
apLikeService = mock(),
|
|
||||||
apReceiveDeleteService = mock(),
|
|
||||||
objectMapper = objectMapper
|
|
||||||
)
|
)
|
||||||
|
|
||||||
//language=JSON
|
//language=JSON
|
||||||
|
@ -83,13 +60,8 @@ class APServiceImplTest {
|
||||||
@Test
|
@Test
|
||||||
fun `parseActivity 空の場合JsonParseExceptionがthrowされる`() {
|
fun `parseActivity 空の場合JsonParseExceptionがthrowされる`() {
|
||||||
val apServiceImpl = APServiceImpl(
|
val apServiceImpl = APServiceImpl(
|
||||||
apReceiveFollowService = mock(),
|
|
||||||
apUndoService = mock(),
|
objectMapper = objectMapper, jobQueueParentService = mock()
|
||||||
apAcceptService = mock(),
|
|
||||||
apCreateService = mock(),
|
|
||||||
apLikeService = mock(),
|
|
||||||
apReceiveDeleteService = mock(),
|
|
||||||
objectMapper = objectMapper
|
|
||||||
)
|
)
|
||||||
|
|
||||||
//language=JSON
|
//language=JSON
|
||||||
|
@ -101,13 +73,8 @@ class APServiceImplTest {
|
||||||
@Test
|
@Test
|
||||||
fun `parseActivity jsonにtypeプロパティがない場合JsonParseExceptionがthrowされる`() {
|
fun `parseActivity jsonにtypeプロパティがない場合JsonParseExceptionがthrowされる`() {
|
||||||
val apServiceImpl = APServiceImpl(
|
val apServiceImpl = APServiceImpl(
|
||||||
apReceiveFollowService = mock(),
|
|
||||||
apUndoService = mock(),
|
objectMapper = objectMapper, jobQueueParentService = mock()
|
||||||
apAcceptService = mock(),
|
|
||||||
apCreateService = mock(),
|
|
||||||
apLikeService = mock(),
|
|
||||||
apReceiveDeleteService = mock(),
|
|
||||||
objectMapper = objectMapper
|
|
||||||
)
|
)
|
||||||
|
|
||||||
//language=JSON
|
//language=JSON
|
||||||
|
@ -119,13 +86,8 @@ class APServiceImplTest {
|
||||||
@Test
|
@Test
|
||||||
fun `parseActivity typeが配列でないときtypeが未定義の場合IllegalArgumentExceptionがthrowされる`() {
|
fun `parseActivity typeが配列でないときtypeが未定義の場合IllegalArgumentExceptionがthrowされる`() {
|
||||||
val apServiceImpl = APServiceImpl(
|
val apServiceImpl = APServiceImpl(
|
||||||
apReceiveFollowService = mock(),
|
|
||||||
apUndoService = mock(),
|
objectMapper = objectMapper, jobQueueParentService = mock()
|
||||||
apAcceptService = mock(),
|
|
||||||
apCreateService = mock(),
|
|
||||||
apLikeService = mock(),
|
|
||||||
apReceiveDeleteService = mock(),
|
|
||||||
objectMapper = objectMapper
|
|
||||||
)
|
)
|
||||||
|
|
||||||
//language=JSON
|
//language=JSON
|
||||||
|
@ -137,13 +99,8 @@ class APServiceImplTest {
|
||||||
@Test
|
@Test
|
||||||
fun `parseActivity typeが配列のとき定義済みのtypeを見つけられなかった場合IllegalArgumentExceptionがthrowされる`() {
|
fun `parseActivity typeが配列のとき定義済みのtypeを見つけられなかった場合IllegalArgumentExceptionがthrowされる`() {
|
||||||
val apServiceImpl = APServiceImpl(
|
val apServiceImpl = APServiceImpl(
|
||||||
apReceiveFollowService = mock(),
|
|
||||||
apUndoService = mock(),
|
objectMapper = objectMapper, jobQueueParentService = mock()
|
||||||
apAcceptService = mock(),
|
|
||||||
apCreateService = mock(),
|
|
||||||
apLikeService = mock(),
|
|
||||||
apReceiveDeleteService = mock(),
|
|
||||||
objectMapper = objectMapper
|
|
||||||
)
|
)
|
||||||
|
|
||||||
//language=JSON
|
//language=JSON
|
||||||
|
@ -155,13 +112,8 @@ class APServiceImplTest {
|
||||||
@Test
|
@Test
|
||||||
fun `parseActivity typeが空の場合IllegalArgumentExceptionがthrowされる`() {
|
fun `parseActivity typeが空の場合IllegalArgumentExceptionがthrowされる`() {
|
||||||
val apServiceImpl = APServiceImpl(
|
val apServiceImpl = APServiceImpl(
|
||||||
apReceiveFollowService = mock(),
|
|
||||||
apUndoService = mock(),
|
objectMapper = objectMapper, jobQueueParentService = mock()
|
||||||
apAcceptService = mock(),
|
|
||||||
apCreateService = mock(),
|
|
||||||
apLikeService = mock(),
|
|
||||||
apReceiveDeleteService = mock(),
|
|
||||||
objectMapper = objectMapper
|
|
||||||
)
|
)
|
||||||
|
|
||||||
//language=JSON
|
//language=JSON
|
||||||
|
@ -173,13 +125,8 @@ class APServiceImplTest {
|
||||||
@Test
|
@Test
|
||||||
fun `parseActivity typeに指定されている文字の判定がcase-insensitiveで行われる`() {
|
fun `parseActivity typeに指定されている文字の判定がcase-insensitiveで行われる`() {
|
||||||
val apServiceImpl = APServiceImpl(
|
val apServiceImpl = APServiceImpl(
|
||||||
apReceiveFollowService = mock(),
|
|
||||||
apUndoService = mock(),
|
objectMapper = objectMapper, jobQueueParentService = mock()
|
||||||
apAcceptService = mock(),
|
|
||||||
apCreateService = mock(),
|
|
||||||
apLikeService = mock(),
|
|
||||||
apReceiveDeleteService = mock(),
|
|
||||||
objectMapper = objectMapper
|
|
||||||
)
|
)
|
||||||
|
|
||||||
//language=JSON
|
//language=JSON
|
||||||
|
@ -191,13 +138,8 @@ class APServiceImplTest {
|
||||||
@Test
|
@Test
|
||||||
fun `parseActivity typeが配列のとき指定されている文字の判定がcase-insensitiveで行われる`() {
|
fun `parseActivity typeが配列のとき指定されている文字の判定がcase-insensitiveで行われる`() {
|
||||||
val apServiceImpl = APServiceImpl(
|
val apServiceImpl = APServiceImpl(
|
||||||
apReceiveFollowService = mock(),
|
|
||||||
apUndoService = mock(),
|
objectMapper = objectMapper, jobQueueParentService = mock()
|
||||||
apAcceptService = mock(),
|
|
||||||
apCreateService = mock(),
|
|
||||||
apLikeService = mock(),
|
|
||||||
apReceiveDeleteService = mock(),
|
|
||||||
objectMapper = objectMapper
|
|
||||||
)
|
)
|
||||||
|
|
||||||
//language=JSON
|
//language=JSON
|
||||||
|
@ -209,13 +151,8 @@ class APServiceImplTest {
|
||||||
@Test
|
@Test
|
||||||
fun `parseActivity activityがarrayのときJsonParseExceptionがthrowされる`() {
|
fun `parseActivity activityがarrayのときJsonParseExceptionがthrowされる`() {
|
||||||
val apServiceImpl = APServiceImpl(
|
val apServiceImpl = APServiceImpl(
|
||||||
apReceiveFollowService = mock(),
|
|
||||||
apUndoService = mock(),
|
objectMapper = objectMapper, jobQueueParentService = mock()
|
||||||
apAcceptService = mock(),
|
|
||||||
apCreateService = mock(),
|
|
||||||
apLikeService = mock(),
|
|
||||||
apReceiveDeleteService = mock(),
|
|
||||||
objectMapper = objectMapper
|
|
||||||
)
|
)
|
||||||
|
|
||||||
//language=JSON
|
//language=JSON
|
||||||
|
@ -227,13 +164,8 @@ class APServiceImplTest {
|
||||||
@Test
|
@Test
|
||||||
fun `parseActivity activityがvalueのときJsonParseExceptionがthrowされる`() {
|
fun `parseActivity activityがvalueのときJsonParseExceptionがthrowされる`() {
|
||||||
val apServiceImpl = APServiceImpl(
|
val apServiceImpl = APServiceImpl(
|
||||||
apReceiveFollowService = mock(),
|
|
||||||
apUndoService = mock(),
|
objectMapper = objectMapper, jobQueueParentService = mock()
|
||||||
apAcceptService = mock(),
|
|
||||||
apCreateService = mock(),
|
|
||||||
apLikeService = mock(),
|
|
||||||
apReceiveDeleteService = mock(),
|
|
||||||
objectMapper = objectMapper
|
|
||||||
)
|
)
|
||||||
|
|
||||||
//language=JSON
|
//language=JSON
|
||||||
|
|
Loading…
Reference in New Issue