mirror of https://github.com/usbharu/Hideout.git
Merge pull request #205 from usbharu/feature/refactor-user-relationship
Feature/refactor user relationship
This commit is contained in:
commit
32cd7ad1db
|
@ -287,7 +287,7 @@ project.gradle.taskGraph.whenReady {
|
|||
kover {
|
||||
|
||||
excludeSourceSets {
|
||||
names("aot")
|
||||
names("aot", "e2eTest", "intTest")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,8 +21,9 @@ VALUES (9, 'test-user9', 'follower.example.com', 'Im test-user9.', 'THis account
|
|||
'https://follower.example.com/users/test-user9/following',
|
||||
'https://follower.example.com/users/test-user9/followers', null);
|
||||
|
||||
insert into USERS_FOLLOWERS (USER_ID, FOLLOWER_ID)
|
||||
VALUES (8, 9);
|
||||
insert into relationships (user_id, target_user_id, following, blocking, muting, follow_request,
|
||||
ignore_follow_request)
|
||||
VALUES (9, 8, true, false, false, false, false);
|
||||
|
||||
insert into POSTS (ID, USER_ID, OVERVIEW, TEXT, CREATED_AT, VISIBILITY, URL, REPLY_ID, REPOST_ID, SENSITIVE, AP_ID)
|
||||
VALUES (1239, 8, null, 'test post', 12345680, 2, 'https://example.com/users/test-user8/posts/1239', null, null, false,
|
||||
|
|
|
@ -21,8 +21,9 @@ VALUES (5, 'test-user5', 'follower.example.com', 'Im test user5.', 'THis account
|
|||
'https://follower.example.com/users/test-user5/following',
|
||||
'https://follower.example.com/users/test-user5/followers', null);
|
||||
|
||||
insert into USERS_FOLLOWERS (USER_ID, FOLLOWER_ID)
|
||||
VALUES (4, 5);
|
||||
insert into relationships (user_id, target_user_id, following, blocking, muting, follow_request,
|
||||
ignore_follow_request)
|
||||
VALUES (5, 4, true, false, false, false, false);
|
||||
|
||||
insert into POSTS (ID, "USER_ID", OVERVIEW, TEXT, "CREATED_AT", VISIBILITY, URL, "REPOST_ID", "REPLY_ID", SENSITIVE,
|
||||
AP_ID)
|
||||
|
|
|
@ -21,8 +21,9 @@ VALUES (7, 'test-user7', 'follower.example.com', 'Im test-user7.', 'THis account
|
|||
'https://follower.example.com/users/test-user7/following',
|
||||
'https://follower.example.com/users/test-user7/followers', null);
|
||||
|
||||
insert into USERS_FOLLOWERS (USER_ID, FOLLOWER_ID)
|
||||
VALUES (6, 7);
|
||||
insert into relationships (user_id, target_user_id, following, blocking, muting, follow_request,
|
||||
ignore_follow_request)
|
||||
VALUES (7, 6, true, false, false, false, false);
|
||||
|
||||
insert into POSTS (ID, "USER_ID", OVERVIEW, TEXT, "CREATED_AT", VISIBILITY, URL, "REPOST_ID", "REPLY_ID", SENSITIVE,
|
||||
AP_ID)
|
||||
|
|
|
@ -8,7 +8,6 @@ import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer
|
|||
|
||||
open class Accept @JsonCreator constructor(
|
||||
type: List<String> = emptyList(),
|
||||
override val name: String,
|
||||
@JsonDeserialize(using = ObjectDeserializer::class)
|
||||
@JsonProperty("object")
|
||||
val apObject: Object,
|
||||
|
@ -16,9 +15,7 @@ open class Accept @JsonCreator constructor(
|
|||
) : Object(
|
||||
type = add(type, "Accept")
|
||||
),
|
||||
HasActor,
|
||||
HasName {
|
||||
|
||||
HasActor {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
@ -26,7 +23,6 @@ open class Accept @JsonCreator constructor(
|
|||
|
||||
other as Accept
|
||||
|
||||
if (name != other.name) return false
|
||||
if (apObject != other.apObject) return false
|
||||
if (actor != other.actor) return false
|
||||
|
||||
|
@ -35,7 +31,6 @@ open class Accept @JsonCreator constructor(
|
|||
|
||||
override fun hashCode(): Int {
|
||||
var result = super.hashCode()
|
||||
result = 31 * result + name.hashCode()
|
||||
result = 31 * result + apObject.hashCode()
|
||||
result = 31 * result + actor.hashCode()
|
||||
return result
|
||||
|
@ -43,7 +38,6 @@ open class Accept @JsonCreator constructor(
|
|||
|
||||
override fun toString(): String {
|
||||
return "Accept(" +
|
||||
"name='$name', " +
|
||||
"apObject=$apObject, " +
|
||||
"actor='$actor'" +
|
||||
")" +
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
package dev.usbharu.hideout.activitypub.domain.model
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import dev.usbharu.hideout.activitypub.domain.model.objects.Object
|
||||
|
||||
open class Block(
|
||||
override val actor: String,
|
||||
override val id: String,
|
||||
@JsonProperty("object") val apObject: String
|
||||
) :
|
||||
Object(listOf("Block")), HasId, HasActor {
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
if (!super.equals(other)) return false
|
||||
|
||||
other as Block
|
||||
|
||||
if (actor != other.actor) return false
|
||||
if (id != other.id) return false
|
||||
if (apObject != other.apObject) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = super.hashCode()
|
||||
result = 31 * result + actor.hashCode()
|
||||
result = 31 * result + id.hashCode()
|
||||
result = 31 * result + apObject.hashCode()
|
||||
return result
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "Block(" +
|
||||
"actor='$actor', " +
|
||||
"id='$id', " +
|
||||
"apObject='$apObject'" +
|
||||
")" +
|
||||
" ${super.toString()}"
|
||||
}
|
||||
}
|
|
@ -7,7 +7,7 @@ import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer
|
|||
|
||||
open class Create(
|
||||
type: List<String> = emptyList(),
|
||||
override val name: String,
|
||||
val name: String? = null,
|
||||
@JsonDeserialize(using = ObjectDeserializer::class)
|
||||
@JsonProperty("object")
|
||||
val apObject: Object,
|
||||
|
@ -19,7 +19,6 @@ open class Create(
|
|||
type = add(type, "Create")
|
||||
),
|
||||
HasId,
|
||||
HasName,
|
||||
HasActor {
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
|
@ -41,7 +40,7 @@ open class Create(
|
|||
|
||||
override fun hashCode(): Int {
|
||||
var result = super.hashCode()
|
||||
result = 31 * result + name.hashCode()
|
||||
result = 31 * result + (name?.hashCode() ?: 0)
|
||||
result = 31 * result + apObject.hashCode()
|
||||
result = 31 * result + actor.hashCode()
|
||||
result = 31 * result + id.hashCode()
|
||||
|
@ -52,7 +51,7 @@ open class Create(
|
|||
|
||||
override fun toString(): String {
|
||||
return "Create(" +
|
||||
"name='$name', " +
|
||||
"name=$name, " +
|
||||
"apObject=$apObject, " +
|
||||
"actor='$actor', " +
|
||||
"id='$id', " +
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
package dev.usbharu.hideout.activitypub.domain.model
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
|
||||
import dev.usbharu.hideout.activitypub.domain.model.objects.Object
|
||||
import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer
|
||||
|
||||
open class Reject(
|
||||
override val actor: String,
|
||||
override val id: String,
|
||||
@JsonDeserialize(using = ObjectDeserializer::class) @JsonProperty("object") val apObject: Object
|
||||
) : Object(listOf("Reject")), HasId, HasActor {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
if (!super.equals(other)) return false
|
||||
|
||||
other as Reject
|
||||
|
||||
if (actor != other.actor) return false
|
||||
if (id != other.id) return false
|
||||
if (apObject != other.apObject) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = super.hashCode()
|
||||
result = 31 * result + actor.hashCode()
|
||||
result = 31 * result + id.hashCode()
|
||||
result = 31 * result + apObject.hashCode()
|
||||
return result
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "Reject(" +
|
||||
"actor='$actor', " +
|
||||
"id='$id', " +
|
||||
"apObject=$apObject" +
|
||||
")" +
|
||||
" ${super.toString()}"
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package dev.usbharu.hideout.activitypub.domain.model
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
|
||||
import dev.usbharu.hideout.activitypub.domain.model.objects.Object
|
||||
import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer
|
||||
|
@ -9,7 +10,7 @@ open class Undo(
|
|||
override val actor: String,
|
||||
override val id: String,
|
||||
@JsonDeserialize(using = ObjectDeserializer::class)
|
||||
@Suppress("VariableNaming") val `object`: Object,
|
||||
@JsonProperty("object") val apObject: Object,
|
||||
val published: String
|
||||
) : Object(add(type, "Undo")), HasId, HasActor {
|
||||
|
||||
|
@ -20,7 +21,7 @@ open class Undo(
|
|||
|
||||
other as Undo
|
||||
|
||||
if (`object` != other.`object`) return false
|
||||
if (apObject != other.apObject) return false
|
||||
if (published != other.published) return false
|
||||
if (actor != other.actor) return false
|
||||
if (id != other.id) return false
|
||||
|
@ -30,7 +31,7 @@ open class Undo(
|
|||
|
||||
override fun hashCode(): Int {
|
||||
var result = super.hashCode()
|
||||
result = 31 * result + `object`.hashCode()
|
||||
result = 31 * result + apObject.hashCode()
|
||||
result = 31 * result + published.hashCode()
|
||||
result = 31 * result + actor.hashCode()
|
||||
result = 31 * result + id.hashCode()
|
||||
|
@ -38,5 +39,5 @@ open class Undo(
|
|||
}
|
||||
|
||||
override fun toString(): String =
|
||||
"Undo(`object`=$`object`, published=$published, actor='$actor', id='$id') ${super.toString()}"
|
||||
"Undo(`object`=$apObject, published=$published, actor='$actor', id='$id') ${super.toString()}"
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ class ObjectDeserializer : JsonDeserializer<Object>() {
|
|||
ExtendedActivityVocabulary.Add -> TODO()
|
||||
ExtendedActivityVocabulary.Announce -> TODO()
|
||||
ExtendedActivityVocabulary.Arrive -> TODO()
|
||||
ExtendedActivityVocabulary.Block -> TODO()
|
||||
ExtendedActivityVocabulary.Block -> p.codec.treeToValue(treeNode, Block::class.java)
|
||||
ExtendedActivityVocabulary.Create -> p.codec.treeToValue(treeNode, Create::class.java)
|
||||
ExtendedActivityVocabulary.Delete -> p.codec.treeToValue(treeNode, Delete::class.java)
|
||||
ExtendedActivityVocabulary.Dislike -> TODO()
|
||||
|
@ -58,7 +58,7 @@ class ObjectDeserializer : JsonDeserializer<Object>() {
|
|||
ExtendedActivityVocabulary.Move -> TODO()
|
||||
ExtendedActivityVocabulary.Offer -> TODO()
|
||||
ExtendedActivityVocabulary.Question -> TODO()
|
||||
ExtendedActivityVocabulary.Reject -> TODO()
|
||||
ExtendedActivityVocabulary.Reject -> p.codec.treeToValue(treeNode, Reject::class.java)
|
||||
ExtendedActivityVocabulary.Read -> TODO()
|
||||
ExtendedActivityVocabulary.Remove -> TODO()
|
||||
ExtendedActivityVocabulary.TentativeReject -> TODO()
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
package dev.usbharu.hideout.activitypub.service.activity.accept
|
||||
|
||||
import dev.usbharu.hideout.activitypub.service.common.APRequestService
|
||||
import dev.usbharu.hideout.application.external.Transaction
|
||||
import dev.usbharu.hideout.core.external.job.DeliverAcceptJob
|
||||
import dev.usbharu.hideout.core.external.job.DeliverAcceptJobParam
|
||||
import dev.usbharu.hideout.core.query.UserQueryService
|
||||
import dev.usbharu.hideout.core.service.job.JobProcessor
|
||||
import org.springframework.stereotype.Service
|
||||
|
||||
@Service
|
||||
class APDeliverAcceptJobProcessor(
|
||||
private val apRequestService: APRequestService,
|
||||
private val userQueryService: UserQueryService,
|
||||
private val deliverAcceptJob: DeliverAcceptJob,
|
||||
private val transaction: Transaction
|
||||
) :
|
||||
JobProcessor<DeliverAcceptJobParam, DeliverAcceptJob> {
|
||||
override suspend fun process(param: DeliverAcceptJobParam): Unit = transaction.transaction {
|
||||
apRequestService.apPost(param.inbox, param.accept, userQueryService.findById(param.signer))
|
||||
}
|
||||
|
||||
override fun job(): DeliverAcceptJob = deliverAcceptJob
|
||||
}
|
|
@ -7,22 +7,20 @@ import dev.usbharu.hideout.activitypub.service.common.AbstractActivityPubProcess
|
|||
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
|
||||
import dev.usbharu.hideout.core.service.relationship.RelationshipService
|
||||
import org.springframework.stereotype.Service
|
||||
|
||||
@Service
|
||||
class ApAcceptProcessor(
|
||||
transaction: Transaction,
|
||||
private val userQueryService: UserQueryService,
|
||||
private val followerQueryService: FollowerQueryService,
|
||||
private val userService: UserService
|
||||
private val relationshipService: RelationshipService
|
||||
) :
|
||||
AbstractActivityPubProcessor<Accept>(transaction) {
|
||||
|
||||
override suspend fun internalProcess(activity: ActivityPubProcessContext<Accept>) {
|
||||
val value = activity.activity.apObject ?: throw IllegalActivityPubObjectException("object is null")
|
||||
val value = activity.activity.apObject
|
||||
|
||||
if (value.type.contains("Follow").not()) {
|
||||
logger.warn("FAILED Activity type isn't Follow.")
|
||||
|
@ -37,13 +35,8 @@ class ApAcceptProcessor(
|
|||
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}.")
|
||||
relationshipService.acceptFollowRequest(user.id, follower.id)
|
||||
logger.debug("SUCCESS Follow from ${user.url} to ${follower.url}.")
|
||||
}
|
||||
|
||||
override fun isSupported(activityType: ActivityType): Boolean = activityType == ActivityType.Accept
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
package dev.usbharu.hideout.activitypub.service.activity.accept
|
||||
|
||||
import dev.usbharu.hideout.activitypub.domain.model.Accept
|
||||
import dev.usbharu.hideout.activitypub.domain.model.Follow
|
||||
import dev.usbharu.hideout.core.domain.model.user.User
|
||||
import dev.usbharu.hideout.core.external.job.DeliverAcceptJob
|
||||
import dev.usbharu.hideout.core.external.job.DeliverAcceptJobParam
|
||||
import dev.usbharu.hideout.core.service.job.JobQueueParentService
|
||||
import org.springframework.stereotype.Service
|
||||
|
||||
interface ApSendAcceptService {
|
||||
suspend fun sendAcceptFollow(user: User, target: User)
|
||||
}
|
||||
|
||||
@Service
|
||||
class ApSendAcceptServiceImpl(
|
||||
private val jobQueueParentService: JobQueueParentService,
|
||||
private val deliverAcceptJob: DeliverAcceptJob
|
||||
) : ApSendAcceptService {
|
||||
override suspend fun sendAcceptFollow(user: User, target: User) {
|
||||
val deliverAcceptJobParam = DeliverAcceptJobParam(
|
||||
Accept(
|
||||
apObject = Follow(
|
||||
apObject = user.url,
|
||||
actor = target.url
|
||||
),
|
||||
actor = user.url
|
||||
),
|
||||
target.inbox,
|
||||
user.id
|
||||
)
|
||||
|
||||
jobQueueParentService.scheduleTypeSafe(deliverAcceptJob, deliverAcceptJobParam)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package dev.usbharu.hideout.activitypub.service.activity.block
|
||||
|
||||
import dev.usbharu.hideout.activitypub.service.common.APRequestService
|
||||
import dev.usbharu.hideout.application.external.Transaction
|
||||
import dev.usbharu.hideout.core.domain.model.user.UserRepository
|
||||
import dev.usbharu.hideout.core.external.job.DeliverBlockJob
|
||||
import dev.usbharu.hideout.core.external.job.DeliverBlockJobParam
|
||||
import dev.usbharu.hideout.core.service.job.JobProcessor
|
||||
import org.springframework.stereotype.Service
|
||||
|
||||
/**
|
||||
* ブロックアクティビティ配送を処理します
|
||||
*/
|
||||
@Service
|
||||
class APDeliverBlockJobProcessor(
|
||||
private val apRequestService: APRequestService,
|
||||
private val userRepository: UserRepository,
|
||||
private val transaction: Transaction,
|
||||
private val deliverBlockJob: DeliverBlockJob
|
||||
) : JobProcessor<DeliverBlockJobParam, DeliverBlockJob> {
|
||||
override suspend fun process(param: DeliverBlockJobParam): Unit = transaction.transaction {
|
||||
val signer = userRepository.findById(param.signer)
|
||||
apRequestService.apPost(
|
||||
param.inbox,
|
||||
param.reject,
|
||||
signer
|
||||
)
|
||||
apRequestService.apPost(
|
||||
param.inbox,
|
||||
param.block,
|
||||
signer
|
||||
)
|
||||
}
|
||||
|
||||
override fun job(): DeliverBlockJob = deliverBlockJob
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package dev.usbharu.hideout.activitypub.service.activity.block
|
||||
|
||||
import dev.usbharu.hideout.activitypub.domain.model.Block
|
||||
import dev.usbharu.hideout.activitypub.domain.model.Follow
|
||||
import dev.usbharu.hideout.activitypub.domain.model.Reject
|
||||
import dev.usbharu.hideout.application.config.ApplicationConfig
|
||||
import dev.usbharu.hideout.core.domain.model.user.User
|
||||
import dev.usbharu.hideout.core.external.job.DeliverBlockJob
|
||||
import dev.usbharu.hideout.core.external.job.DeliverBlockJobParam
|
||||
import dev.usbharu.hideout.core.service.job.JobQueueParentService
|
||||
import org.springframework.stereotype.Service
|
||||
|
||||
interface APSendBlockService {
|
||||
suspend fun sendBlock(user: User, target: User)
|
||||
}
|
||||
|
||||
@Service
|
||||
class ApSendBlockServiceImpl(
|
||||
private val applicationConfig: ApplicationConfig,
|
||||
private val jobQueueParentService: JobQueueParentService,
|
||||
private val deliverBlockJob: DeliverBlockJob
|
||||
) : APSendBlockService {
|
||||
override suspend fun sendBlock(user: User, target: User) {
|
||||
val blockJobParam = DeliverBlockJobParam(
|
||||
user.id,
|
||||
Block(
|
||||
user.url,
|
||||
"${applicationConfig.url}/block/${user.id}/${target.id}",
|
||||
target.url
|
||||
),
|
||||
Reject(
|
||||
user.url,
|
||||
"${applicationConfig.url}/reject/${user.id}/${target.id}",
|
||||
Follow(
|
||||
apObject = user.url,
|
||||
actor = target.url
|
||||
)
|
||||
),
|
||||
target.inbox
|
||||
)
|
||||
jobQueueParentService.scheduleTypeSafe(deliverBlockJob, blockJobParam)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package dev.usbharu.hideout.activitypub.service.activity.block
|
||||
|
||||
import dev.usbharu.hideout.activitypub.domain.model.Block
|
||||
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.UserQueryService
|
||||
import dev.usbharu.hideout.core.service.relationship.RelationshipService
|
||||
import org.springframework.stereotype.Service
|
||||
|
||||
/**
|
||||
* ブロックアクティビティを処理します
|
||||
*/
|
||||
@Service
|
||||
class BlockActivityPubProcessor(
|
||||
private val userQueryService: UserQueryService,
|
||||
private val relationshipService: RelationshipService,
|
||||
transaction: Transaction
|
||||
) :
|
||||
AbstractActivityPubProcessor<Block>(transaction) {
|
||||
override suspend fun internalProcess(activity: ActivityPubProcessContext<Block>) {
|
||||
val user = userQueryService.findByUrl(activity.activity.actor)
|
||||
val target = userQueryService.findByUrl(activity.activity.apObject)
|
||||
relationshipService.block(user.id, target.id)
|
||||
}
|
||||
|
||||
override fun isSupported(activityType: ActivityType): Boolean = activityType == ActivityType.Block
|
||||
|
||||
override fun type(): Class<Block> = Block::class.java
|
||||
}
|
|
@ -2,16 +2,14 @@ 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 dev.usbharu.hideout.core.service.relationship.RelationshipService
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.stereotype.Service
|
||||
|
||||
|
@ -21,35 +19,21 @@ class APReceiveFollowJobProcessor(
|
|||
private val userQueryService: UserQueryService,
|
||||
private val apUserService: APUserService,
|
||||
private val objectMapper: ObjectMapper,
|
||||
private val apRequestService: APRequestService,
|
||||
private val userService: UserService
|
||||
private val relationshipService: RelationshipService
|
||||
) :
|
||||
JobProcessor<ReceiveFollowJobParam, ReceiveFollowJob> {
|
||||
override suspend fun process(param: ReceiveFollowJobParam) = transaction.transaction {
|
||||
val person = apUserService.fetchPerson(param.actor, param.targetActor)
|
||||
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
|
||||
|
||||
apRequestService.apPost(
|
||||
url = urlString,
|
||||
body = Accept(
|
||||
name = "Follow",
|
||||
apObject = follow,
|
||||
actor = param.targetActor
|
||||
),
|
||||
signer = signer
|
||||
)
|
||||
|
||||
val targetEntity = userQueryService.findByUrl(param.targetActor)
|
||||
val followActorEntity =
|
||||
userQueryService.findByUrl(follow.actor)
|
||||
|
||||
userService.followRequest(targetEntity.id, followActorEntity.id)
|
||||
relationshipService.followRequest(followActorEntity.id, targetEntity.id)
|
||||
|
||||
logger.info("SUCCESS Follow from: {} to: {}", param.targetActor, param.actor)
|
||||
}
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ class ApRemoveReactionJobProcessor(
|
|||
param.inbox,
|
||||
Undo(
|
||||
actor = param.actor,
|
||||
`object` = like,
|
||||
apObject = like,
|
||||
id = "${applicationConfig.url}/undo/like/${param.id}",
|
||||
published = Instant.now().toString()
|
||||
),
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
package dev.usbharu.hideout.activitypub.service.activity.reject
|
||||
|
||||
import dev.usbharu.hideout.activitypub.service.common.APRequestService
|
||||
import dev.usbharu.hideout.application.external.Transaction
|
||||
import dev.usbharu.hideout.core.external.job.DeliverRejectJob
|
||||
import dev.usbharu.hideout.core.external.job.DeliverRejectJobParam
|
||||
import dev.usbharu.hideout.core.query.UserQueryService
|
||||
import dev.usbharu.hideout.core.service.job.JobProcessor
|
||||
import org.springframework.stereotype.Component
|
||||
|
||||
@Component
|
||||
class APDeliverRejectJobProcessor(
|
||||
private val apRequestService: APRequestService,
|
||||
private val userQueryService: UserQueryService,
|
||||
private val deliverRejectJob: DeliverRejectJob,
|
||||
private val transaction: Transaction
|
||||
) :
|
||||
JobProcessor<DeliverRejectJobParam, DeliverRejectJob> {
|
||||
override suspend fun process(param: DeliverRejectJobParam): Unit = transaction.transaction {
|
||||
apRequestService.apPost(param.inbox, param.reject, userQueryService.findById(param.signer))
|
||||
}
|
||||
|
||||
override fun job(): DeliverRejectJob = deliverRejectJob
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
package dev.usbharu.hideout.activitypub.service.activity.reject
|
||||
|
||||
import dev.usbharu.hideout.activitypub.domain.model.Follow
|
||||
import dev.usbharu.hideout.activitypub.domain.model.Reject
|
||||
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.UserQueryService
|
||||
import dev.usbharu.hideout.core.service.relationship.RelationshipService
|
||||
import org.springframework.stereotype.Service
|
||||
|
||||
@Service
|
||||
class ApRejectProcessor(
|
||||
private val relationshipService: RelationshipService,
|
||||
private val userQueryService: UserQueryService,
|
||||
transaction: Transaction
|
||||
) :
|
||||
AbstractActivityPubProcessor<Reject>(transaction) {
|
||||
override suspend fun internalProcess(activity: ActivityPubProcessContext<Reject>) {
|
||||
val activityType = activity.activity.apObject.type.firstOrNull { it == "Follow" }
|
||||
|
||||
if (activityType == null) {
|
||||
logger.warn("FAILED Process Reject Activity type: {}", activity.activity.apObject.type)
|
||||
return
|
||||
}
|
||||
when (activityType) {
|
||||
"Follow" -> {
|
||||
val user = userQueryService.findByUrl(activity.activity.actor)
|
||||
|
||||
activity.activity.apObject as Follow
|
||||
|
||||
val actor = activity.activity.apObject.actor
|
||||
|
||||
val target = userQueryService.findByUrl(actor)
|
||||
|
||||
logger.debug("REJECT Follow user {} target {}", user.url, target.url)
|
||||
|
||||
relationshipService.rejectFollowRequest(user.id, target.id)
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
override fun isSupported(activityType: ActivityType): Boolean = activityType == ActivityType.Reject
|
||||
|
||||
override fun type(): Class<Reject> = Reject::class.java
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package dev.usbharu.hideout.activitypub.service.activity.reject
|
||||
|
||||
import dev.usbharu.hideout.core.domain.model.user.User
|
||||
|
||||
interface ApSendRejectService {
|
||||
suspend fun sendRejectFollow(user: User, target: User)
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package dev.usbharu.hideout.activitypub.service.activity.reject
|
||||
|
||||
import dev.usbharu.hideout.activitypub.domain.model.Follow
|
||||
import dev.usbharu.hideout.activitypub.domain.model.Reject
|
||||
import dev.usbharu.hideout.application.config.ApplicationConfig
|
||||
import dev.usbharu.hideout.core.domain.model.user.User
|
||||
import dev.usbharu.hideout.core.external.job.DeliverRejectJob
|
||||
import dev.usbharu.hideout.core.external.job.DeliverRejectJobParam
|
||||
import dev.usbharu.hideout.core.service.job.JobQueueParentService
|
||||
import org.springframework.stereotype.Service
|
||||
|
||||
@Service
|
||||
class ApSendRejectServiceImpl(
|
||||
private val applicationConfig: ApplicationConfig,
|
||||
private val jobQueueParentService: JobQueueParentService,
|
||||
private val deliverRejectJob: DeliverRejectJob
|
||||
) : ApSendRejectService {
|
||||
override suspend fun sendRejectFollow(user: User, target: User) {
|
||||
val deliverRejectJobParam = DeliverRejectJobParam(
|
||||
Reject(
|
||||
user.url,
|
||||
"${applicationConfig.url}/reject/${user.id}/${target.id}",
|
||||
Follow(apObject = user.url, actor = target.url)
|
||||
),
|
||||
target.inbox,
|
||||
user.id
|
||||
)
|
||||
|
||||
jobQueueParentService.scheduleTypeSafe(deliverRejectJob, deliverRejectJobParam)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package dev.usbharu.hideout.activitypub.service.activity.undo
|
||||
|
||||
import dev.usbharu.hideout.activitypub.service.common.APRequestService
|
||||
import dev.usbharu.hideout.application.external.Transaction
|
||||
import dev.usbharu.hideout.core.external.job.DeliverUndoJob
|
||||
import dev.usbharu.hideout.core.external.job.DeliverUndoJobParam
|
||||
import dev.usbharu.hideout.core.query.UserQueryService
|
||||
import dev.usbharu.hideout.core.service.job.JobProcessor
|
||||
import org.springframework.stereotype.Service
|
||||
|
||||
@Service
|
||||
class APDeliverUndoJobProcessor(
|
||||
private val deliverUndoJob: DeliverUndoJob,
|
||||
private val apRequestService: APRequestService,
|
||||
private val userQueryService: UserQueryService,
|
||||
private val transaction: Transaction
|
||||
) : JobProcessor<DeliverUndoJobParam, DeliverUndoJob> {
|
||||
override suspend fun process(param: DeliverUndoJobParam): Unit = transaction.transaction {
|
||||
apRequestService.apPost(param.inbox, param.undo, userQueryService.findById(param.signer))
|
||||
}
|
||||
|
||||
override fun job(): DeliverUndoJob = deliverUndoJob
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package dev.usbharu.hideout.activitypub.service.activity.undo
|
||||
|
||||
import dev.usbharu.hideout.core.domain.model.user.User
|
||||
|
||||
interface APSendUndoService {
|
||||
suspend fun sendUndoFollow(user: User, target: User)
|
||||
suspend fun sendUndoBlock(user: User, target: User)
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package dev.usbharu.hideout.activitypub.service.activity.undo
|
||||
|
||||
import dev.usbharu.hideout.activitypub.domain.model.Block
|
||||
import dev.usbharu.hideout.activitypub.domain.model.Follow
|
||||
import dev.usbharu.hideout.activitypub.domain.model.Undo
|
||||
import dev.usbharu.hideout.application.config.ApplicationConfig
|
||||
import dev.usbharu.hideout.core.domain.model.user.User
|
||||
import dev.usbharu.hideout.core.external.job.DeliverUndoJob
|
||||
import dev.usbharu.hideout.core.external.job.DeliverUndoJobParam
|
||||
import dev.usbharu.hideout.core.service.job.JobQueueParentService
|
||||
import org.springframework.stereotype.Service
|
||||
import java.time.Instant
|
||||
|
||||
@Service
|
||||
class APSendUndoServiceImpl(
|
||||
private val jobQueueParentService: JobQueueParentService,
|
||||
private val deliverUndoJob: DeliverUndoJob,
|
||||
private val applicationConfig: ApplicationConfig
|
||||
) : APSendUndoService {
|
||||
override suspend fun sendUndoFollow(user: User, target: User) {
|
||||
val deliverUndoJobParam = DeliverUndoJobParam(
|
||||
Undo(
|
||||
actor = user.url,
|
||||
id = "${applicationConfig.url}/undo/follow/${user.id}/${target.url}",
|
||||
apObject = Follow(
|
||||
apObject = user.url,
|
||||
actor = target.url
|
||||
),
|
||||
published = Instant.now().toString()
|
||||
),
|
||||
target.inbox,
|
||||
user.id
|
||||
)
|
||||
|
||||
jobQueueParentService.scheduleTypeSafe(deliverUndoJob, deliverUndoJobParam)
|
||||
}
|
||||
|
||||
override suspend fun sendUndoBlock(user: User, target: User) {
|
||||
val deliverUndoJobParam = DeliverUndoJobParam(
|
||||
Undo(
|
||||
actor = user.url,
|
||||
id = "${applicationConfig.url}/undo/block/${user.id}/${target.url}",
|
||||
apObject = Block(
|
||||
apObject = user.url,
|
||||
actor = target.url,
|
||||
id = "${applicationConfig.url}/block/${user.id}/${target.id}"
|
||||
),
|
||||
published = Instant.now().toString()
|
||||
),
|
||||
target.inbox,
|
||||
user.id
|
||||
)
|
||||
|
||||
jobQueueParentService.scheduleTypeSafe(deliverUndoJob, deliverUndoJobParam)
|
||||
}
|
||||
}
|
|
@ -1,14 +1,17 @@
|
|||
package dev.usbharu.hideout.activitypub.service.activity.undo
|
||||
|
||||
import dev.usbharu.hideout.activitypub.domain.model.Accept
|
||||
import dev.usbharu.hideout.activitypub.domain.model.Block
|
||||
import dev.usbharu.hideout.activitypub.domain.model.Follow
|
||||
import dev.usbharu.hideout.activitypub.domain.model.Undo
|
||||
import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectValue
|
||||
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.application.external.Transaction
|
||||
import dev.usbharu.hideout.core.query.UserQueryService
|
||||
import dev.usbharu.hideout.core.service.user.UserService
|
||||
import dev.usbharu.hideout.core.service.relationship.RelationshipService
|
||||
import org.springframework.stereotype.Service
|
||||
|
||||
@Service
|
||||
|
@ -16,34 +19,57 @@ class APUndoProcessor(
|
|||
transaction: Transaction,
|
||||
private val apUserService: APUserService,
|
||||
private val userQueryService: UserQueryService,
|
||||
private val userService: UserService
|
||||
private val relationshipService: RelationshipService
|
||||
) :
|
||||
AbstractActivityPubProcessor<Undo>(transaction) {
|
||||
override suspend fun internalProcess(activity: ActivityPubProcessContext<Undo>) {
|
||||
val undo = activity.activity
|
||||
if (undo.actor == null) {
|
||||
return
|
||||
}
|
||||
|
||||
val type =
|
||||
undo.`object`.type.orEmpty()
|
||||
undo.apObject.type
|
||||
.firstOrNull { it == "Block" || it == "Follow" || it == "Like" || it == "Announce" || it == "Accept" }
|
||||
?: return
|
||||
|
||||
when (type) {
|
||||
"Follow" -> {
|
||||
val follow = undo.`object` as Follow
|
||||
val follow = undo.apObject as Follow
|
||||
|
||||
if (follow.apObject == null) {
|
||||
return
|
||||
}
|
||||
apUserService.fetchPerson(undo.actor, follow.apObject)
|
||||
val follower = userQueryService.findByUrl(undo.actor)
|
||||
val target = userQueryService.findByUrl(follow.apObject)
|
||||
userService.unfollow(target.id, follower.id)
|
||||
|
||||
relationshipService.unfollow(follower.id, target.id)
|
||||
return
|
||||
}
|
||||
|
||||
"Block" -> {
|
||||
val block = undo.apObject as Block
|
||||
|
||||
val blocker = apUserService.fetchPersonWithEntity(undo.actor, block.apObject).second
|
||||
val target = userQueryService.findByUrl(block.apObject)
|
||||
|
||||
relationshipService.unblock(blocker.id, target.id)
|
||||
return
|
||||
}
|
||||
|
||||
"Accept" -> {
|
||||
val accept = undo.apObject as Accept
|
||||
|
||||
val acceptObject = if (accept.apObject is ObjectValue) {
|
||||
accept.apObject.`object`
|
||||
} else if (accept.apObject is Follow) {
|
||||
accept.apObject.apObject
|
||||
} else {
|
||||
logger.warn("FAILED Unsupported type. Undo Accept {}", accept.apObject.type)
|
||||
return
|
||||
}
|
||||
|
||||
val accepter = apUserService.fetchPersonWithEntity(undo.actor, acceptObject).second
|
||||
val target = userQueryService.findByUrl(acceptObject)
|
||||
|
||||
relationshipService.rejectFollowRequest(accepter.id, target.id)
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
TODO()
|
||||
|
|
|
@ -7,9 +7,7 @@ import dev.usbharu.hideout.activitypub.domain.model.objects.Object
|
|||
import dev.usbharu.hideout.application.external.Transaction
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.stereotype.Service
|
||||
|
||||
@Service
|
||||
abstract class AbstractActivityPubProcessor<T : Object>(
|
||||
private val transaction: Transaction,
|
||||
private val allowUnauthorized: Boolean = false
|
||||
|
|
|
@ -106,7 +106,7 @@ class InboxJobProcessor(
|
|||
|
||||
if (activityPubProcessor == null) {
|
||||
logger.warn("ActivityType {} is not support.", param.type)
|
||||
throw IllegalStateException("ActivityPubProcessor not found.")
|
||||
throw IllegalStateException("ActivityPubProcessor not found. type: ${param.type}")
|
||||
}
|
||||
|
||||
val value = objectMapper.treeToValue(jsonNode, activityPubProcessor.type())
|
||||
|
|
|
@ -197,6 +197,9 @@ class SecurityConfig {
|
|||
authorize(GET, "/api/v1/accounts/*", permitAll)
|
||||
authorize(GET, "/api/v1/accounts/*/statuses", permitAll)
|
||||
authorize(POST, "/api/v1/accounts/*/follow", hasAnyScope("write", "write:follows"))
|
||||
authorize(POST, "/api/v1/accounts/*/unfollow", hasAnyScope("write", "write:follows"))
|
||||
authorize(POST, "/api/v1/accounts/*/block", hasAnyScope("write", "write:blocks"))
|
||||
authorize(POST, "/api/v1/accounts/*/unblock", hasAnyScope("write", "write:blocks"))
|
||||
|
||||
authorize(POST, "/api/v1/media", hasAnyScope("write", "write:media"))
|
||||
authorize(POST, "/api/v1/statuses", hasAnyScope("write", "write:statuses"))
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
package dev.usbharu.hideout.core.domain.model.relationship
|
||||
|
||||
/**
|
||||
* ユーザーとの関係を表します
|
||||
*
|
||||
* @property userId ユーザー
|
||||
* @property targetUserId 相手ユーザー
|
||||
* @property following フォローしているか
|
||||
* @property blocking ブロックしているか
|
||||
* @property muting ミュートしているか
|
||||
* @property followRequest フォローリクエストを送っているか
|
||||
* @property ignoreFollowRequestFromTarget フォローリクエストを無視しているか
|
||||
*/
|
||||
data class Relationship(
|
||||
val userId: Long,
|
||||
val targetUserId: Long,
|
||||
val following: Boolean,
|
||||
val blocking: Boolean,
|
||||
val muting: Boolean,
|
||||
val followRequest: Boolean,
|
||||
val ignoreFollowRequestFromTarget: Boolean
|
||||
)
|
|
@ -0,0 +1,31 @@
|
|||
package dev.usbharu.hideout.core.domain.model.relationship
|
||||
|
||||
/**
|
||||
* [Relationship]の永続化
|
||||
*
|
||||
*/
|
||||
interface RelationshipRepository {
|
||||
/**
|
||||
* 永続化します
|
||||
*
|
||||
* @param relationship 永続化する[Relationship]
|
||||
* @return 永続化された[Relationship]
|
||||
*/
|
||||
suspend fun save(relationship: Relationship): Relationship
|
||||
|
||||
/**
|
||||
* 永続化されたものを削除します
|
||||
*
|
||||
* @param relationship 削除する[Relationship]
|
||||
*/
|
||||
suspend fun delete(relationship: Relationship)
|
||||
|
||||
/**
|
||||
* userIdとtargetUserIdで[Relationship]を取得します
|
||||
*
|
||||
* @param userId 取得するユーザーID
|
||||
* @param targetUserId 対象ユーザーID
|
||||
* @return 取得された[Relationship] 存在しない場合nullが返ります
|
||||
*/
|
||||
suspend fun findByUserIdAndTargetUserId(userId: Long, targetUserId: Long): Relationship?
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
package dev.usbharu.hideout.core.domain.model.relationship
|
||||
|
||||
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Users
|
||||
import org.jetbrains.exposed.dao.id.LongIdTable
|
||||
import org.jetbrains.exposed.sql.*
|
||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||
import org.springframework.stereotype.Service
|
||||
|
||||
@Service
|
||||
class RelationshipRepositoryImpl : RelationshipRepository {
|
||||
override suspend fun save(relationship: Relationship): Relationship {
|
||||
val singleOrNull =
|
||||
Relationships
|
||||
.select {
|
||||
(Relationships.userId eq relationship.userId)
|
||||
.and(Relationships.targetUserId eq relationship.targetUserId)
|
||||
}
|
||||
.singleOrNull()
|
||||
|
||||
if (singleOrNull == null) {
|
||||
Relationships.insert {
|
||||
it[userId] = relationship.userId
|
||||
it[targetUserId] = relationship.targetUserId
|
||||
it[following] = relationship.following
|
||||
it[blocking] = relationship.blocking
|
||||
it[muting] = relationship.muting
|
||||
it[followRequest] = relationship.followRequest
|
||||
it[ignoreFollowRequestFromTarget] = relationship.ignoreFollowRequestFromTarget
|
||||
}
|
||||
} else {
|
||||
Relationships
|
||||
.update({
|
||||
(Relationships.userId eq relationship.userId)
|
||||
.and(Relationships.targetUserId eq relationship.targetUserId)
|
||||
}) {
|
||||
it[following] = relationship.following
|
||||
it[blocking] = relationship.blocking
|
||||
it[muting] = relationship.muting
|
||||
it[followRequest] = relationship.followRequest
|
||||
it[ignoreFollowRequestFromTarget] = relationship.ignoreFollowRequestFromTarget
|
||||
}
|
||||
}
|
||||
return relationship
|
||||
}
|
||||
|
||||
override suspend fun delete(relationship: Relationship) {
|
||||
Relationships.deleteWhere {
|
||||
(Relationships.userId eq relationship.userId)
|
||||
.and(Relationships.targetUserId eq relationship.targetUserId)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun findByUserIdAndTargetUserId(userId: Long, targetUserId: Long): Relationship? {
|
||||
return Relationships.select {
|
||||
(Relationships.userId eq userId)
|
||||
.and(Relationships.targetUserId eq targetUserId)
|
||||
}.singleOrNull()
|
||||
?.toRelationships()
|
||||
}
|
||||
}
|
||||
|
||||
fun ResultRow.toRelationships(): Relationship = Relationship(
|
||||
userId = this[Relationships.userId],
|
||||
targetUserId = this[Relationships.targetUserId],
|
||||
following = this[Relationships.following],
|
||||
blocking = this[Relationships.blocking],
|
||||
muting = this[Relationships.muting],
|
||||
followRequest = this[Relationships.followRequest],
|
||||
ignoreFollowRequestFromTarget = this[Relationships.ignoreFollowRequestFromTarget]
|
||||
)
|
||||
|
||||
object Relationships : LongIdTable("relationships") {
|
||||
val userId = long("user_id").references(Users.id)
|
||||
val targetUserId = long("target_user_id").references(Users.id)
|
||||
val following = bool("following")
|
||||
val blocking = bool("blocking")
|
||||
val muting = bool("muting")
|
||||
val followRequest = bool("follow_request")
|
||||
val ignoreFollowRequestFromTarget = bool("ignore_follow_request")
|
||||
|
||||
init {
|
||||
uniqueIndex(userId, targetUserId)
|
||||
}
|
||||
}
|
|
@ -10,9 +10,5 @@ interface UserRepository {
|
|||
|
||||
suspend fun delete(id: Long)
|
||||
|
||||
suspend fun deleteFollowRequest(id: Long, follower: Long)
|
||||
|
||||
suspend fun findFollowRequestsById(id: Long, follower: Long): Boolean
|
||||
|
||||
suspend fun nextId(): Long
|
||||
}
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
package dev.usbharu.hideout.core.external.job
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.fasterxml.jackson.module.kotlin.readValue
|
||||
import dev.usbharu.hideout.activitypub.domain.model.Accept
|
||||
import kjob.core.dsl.ScheduleContext
|
||||
import kjob.core.job.JobProps
|
||||
import org.springframework.stereotype.Component
|
||||
|
||||
data class DeliverAcceptJobParam(
|
||||
val accept: Accept,
|
||||
val inbox: String,
|
||||
val signer: Long
|
||||
)
|
||||
|
||||
@Component
|
||||
class DeliverAcceptJob(private val objectMapper: ObjectMapper) :
|
||||
HideoutJob<DeliverAcceptJobParam, DeliverAcceptJob>("DeliverAcceptJob") {
|
||||
|
||||
val accept = string("accept")
|
||||
val inbox = string("inbox")
|
||||
val signer = long("signer")
|
||||
|
||||
override fun convert(value: DeliverAcceptJobParam): ScheduleContext<DeliverAcceptJob>.(DeliverAcceptJob) -> Unit = {
|
||||
props[accept] = objectMapper.writeValueAsString(value.accept)
|
||||
props[inbox] = value.inbox
|
||||
props[signer] = value.signer
|
||||
}
|
||||
|
||||
override fun convert(props: JobProps<DeliverAcceptJob>): DeliverAcceptJobParam {
|
||||
return DeliverAcceptJobParam(
|
||||
objectMapper.readValue(props[accept]),
|
||||
props[inbox],
|
||||
props[signer]
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
package dev.usbharu.hideout.core.external.job
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.fasterxml.jackson.module.kotlin.readValue
|
||||
import dev.usbharu.hideout.activitypub.domain.model.Block
|
||||
import dev.usbharu.hideout.activitypub.domain.model.Reject
|
||||
import kjob.core.dsl.ScheduleContext
|
||||
import kjob.core.job.JobProps
|
||||
import org.springframework.beans.factory.annotation.Qualifier
|
||||
import org.springframework.stereotype.Component
|
||||
|
||||
/**
|
||||
* ブロックアクティビティ配送のジョブパラメーター
|
||||
*
|
||||
* @property signer ブロック操作を行ったユーザーid
|
||||
* @property block 配送するブロックアクティビティ
|
||||
* @property reject 配送するフォロー解除アクティビティ
|
||||
* @property inbox 配送先url
|
||||
*/
|
||||
data class DeliverBlockJobParam(
|
||||
val signer: Long,
|
||||
val block: Block,
|
||||
val reject: Reject,
|
||||
val inbox: String
|
||||
)
|
||||
|
||||
/**
|
||||
* ブロックアクティビティ配送のジョブ
|
||||
*/
|
||||
@Component
|
||||
class DeliverBlockJob(@Qualifier("activitypub") private val objectMapper: ObjectMapper) :
|
||||
HideoutJob<DeliverBlockJobParam, DeliverBlockJob>("DeliverBlockJob") {
|
||||
|
||||
val block = string("block")
|
||||
val reject = string("reject")
|
||||
val inbox = string("inbox")
|
||||
val signer = long("signer")
|
||||
|
||||
override fun convert(value: DeliverBlockJobParam): ScheduleContext<DeliverBlockJob>.(DeliverBlockJob) -> Unit = {
|
||||
props[block] = objectMapper.writeValueAsString(value.block)
|
||||
props[reject] = objectMapper.writeValueAsString(value.reject)
|
||||
props[inbox] = value.inbox
|
||||
props[signer] = value.signer
|
||||
}
|
||||
|
||||
override fun convert(props: JobProps<DeliverBlockJob>): DeliverBlockJobParam = DeliverBlockJobParam(
|
||||
signer = props[signer],
|
||||
block = objectMapper.readValue(props[block]),
|
||||
reject = objectMapper.readValue(props[reject]),
|
||||
inbox = props[inbox]
|
||||
)
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package dev.usbharu.hideout.core.external.job
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.fasterxml.jackson.module.kotlin.readValue
|
||||
import dev.usbharu.hideout.activitypub.domain.model.Reject
|
||||
import kjob.core.dsl.ScheduleContext
|
||||
import kjob.core.job.JobProps
|
||||
import org.springframework.beans.factory.annotation.Qualifier
|
||||
import org.springframework.stereotype.Component
|
||||
|
||||
data class DeliverRejectJobParam(
|
||||
val reject: Reject,
|
||||
val inbox: String,
|
||||
val signer: Long
|
||||
)
|
||||
|
||||
@Component
|
||||
class DeliverRejectJob(@Qualifier("activitypub") private val objectMapper: ObjectMapper) :
|
||||
HideoutJob<DeliverRejectJobParam, DeliverRejectJob>("DeliverRejectJob") {
|
||||
val reject = string("reject")
|
||||
val inbox = string("inbox")
|
||||
val signer = long("signer")
|
||||
|
||||
override fun convert(value: DeliverRejectJobParam): ScheduleContext<DeliverRejectJob>.(DeliverRejectJob) -> Unit =
|
||||
{
|
||||
props[reject] = objectMapper.writeValueAsString(value.reject)
|
||||
props[inbox] = value.inbox
|
||||
props[signer] = value.signer
|
||||
}
|
||||
|
||||
override fun convert(props: JobProps<DeliverRejectJob>): DeliverRejectJobParam = DeliverRejectJobParam(
|
||||
objectMapper.readValue<Reject>(props[reject]),
|
||||
props[inbox],
|
||||
props[signer]
|
||||
)
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package dev.usbharu.hideout.core.external.job
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.fasterxml.jackson.module.kotlin.readValue
|
||||
import dev.usbharu.hideout.activitypub.domain.model.Undo
|
||||
import kjob.core.dsl.ScheduleContext
|
||||
import kjob.core.job.JobProps
|
||||
import org.springframework.beans.factory.annotation.Qualifier
|
||||
import org.springframework.stereotype.Component
|
||||
|
||||
data class DeliverUndoJobParam(
|
||||
val undo: Undo,
|
||||
val inbox: String,
|
||||
val signer: Long
|
||||
)
|
||||
|
||||
@Component
|
||||
class DeliverUndoJob(@Qualifier("activitypub") private val objectMapper: ObjectMapper) :
|
||||
HideoutJob<DeliverUndoJobParam, DeliverUndoJob>("DeliverUndoJob") {
|
||||
|
||||
val undo = string("undo")
|
||||
val inbox = string("inbox")
|
||||
val signer = long("signer")
|
||||
|
||||
override fun convert(value: DeliverUndoJobParam): ScheduleContext<DeliverUndoJob>.(DeliverUndoJob) -> Unit = {
|
||||
props[undo] = objectMapper.writeValueAsString(value.undo)
|
||||
props[inbox] = value.inbox
|
||||
props[signer] = value.signer
|
||||
}
|
||||
|
||||
override fun convert(props: JobProps<DeliverUndoJob>): DeliverUndoJobParam {
|
||||
return DeliverUndoJobParam(
|
||||
objectMapper.readValue(props[undo]),
|
||||
props[inbox],
|
||||
props[signer]
|
||||
)
|
||||
}
|
||||
}
|
|
@ -7,7 +7,7 @@ import kjob.core.dsl.ScheduleContext
|
|||
import kjob.core.job.JobProps
|
||||
import org.springframework.stereotype.Component
|
||||
|
||||
abstract class HideoutJob<out T, out R : HideoutJob<T, R>>(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
|
||||
|
|
|
@ -1,242 +1,24 @@
|
|||
package dev.usbharu.hideout.core.infrastructure.exposedquery
|
||||
|
||||
import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository
|
||||
import dev.usbharu.hideout.core.domain.model.user.User
|
||||
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Users
|
||||
import dev.usbharu.hideout.core.infrastructure.exposedrepository.UsersFollowers
|
||||
import dev.usbharu.hideout.core.query.FollowerQueryService
|
||||
import org.jetbrains.exposed.sql.*
|
||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||
import dev.usbharu.hideout.core.query.RelationshipQueryService
|
||||
import dev.usbharu.hideout.core.query.UserQueryService
|
||||
import org.springframework.stereotype.Repository
|
||||
import java.time.Instant
|
||||
|
||||
@Repository
|
||||
class FollowerQueryServiceImpl(private val userBuilder: User.UserBuilder) : FollowerQueryService {
|
||||
class FollowerQueryServiceImpl(
|
||||
private val relationshipQueryService: RelationshipQueryService,
|
||||
private val userQueryService: UserQueryService,
|
||||
private val relationshipRepository: RelationshipRepository
|
||||
) : FollowerQueryService {
|
||||
override suspend fun findFollowersById(id: Long): List<User> {
|
||||
val followers = Users.alias("FOLLOWERS")
|
||||
return Users.innerJoin(
|
||||
otherTable = UsersFollowers,
|
||||
onColumn = { Users.id },
|
||||
otherColumn = { userId }
|
||||
return userQueryService.findByIds(
|
||||
relationshipQueryService.findByTargetIdAndFollowing(id, true).map { it.userId }
|
||||
)
|
||||
.innerJoin(
|
||||
otherTable = followers,
|
||||
onColumn = { UsersFollowers.followerId },
|
||||
otherColumn = { followers[Users.id] }
|
||||
)
|
||||
.slice(
|
||||
followers[Users.id],
|
||||
followers[Users.name],
|
||||
followers[Users.domain],
|
||||
followers[Users.screenName],
|
||||
followers[Users.description],
|
||||
followers[Users.password],
|
||||
followers[Users.inbox],
|
||||
followers[Users.outbox],
|
||||
followers[Users.url],
|
||||
followers[Users.publicKey],
|
||||
followers[Users.privateKey],
|
||||
followers[Users.createdAt],
|
||||
followers[Users.keyId],
|
||||
followers[Users.following],
|
||||
followers[Users.followers],
|
||||
followers[Users.instance]
|
||||
)
|
||||
.select { Users.id eq id }
|
||||
.map {
|
||||
userBuilder.of(
|
||||
id = it[followers[Users.id]],
|
||||
name = it[followers[Users.name]],
|
||||
domain = it[followers[Users.domain]],
|
||||
screenName = it[followers[Users.screenName]],
|
||||
description = it[followers[Users.description]],
|
||||
password = it[followers[Users.password]],
|
||||
inbox = it[followers[Users.inbox]],
|
||||
outbox = it[followers[Users.outbox]],
|
||||
url = it[followers[Users.url]],
|
||||
publicKey = it[followers[Users.publicKey]],
|
||||
privateKey = it[followers[Users.privateKey]],
|
||||
createdAt = Instant.ofEpochMilli(it[followers[Users.createdAt]]),
|
||||
keyId = it[followers[Users.keyId]],
|
||||
followers = it[followers[Users.followers]],
|
||||
following = it[followers[Users.following]],
|
||||
instance = it[followers[Users.instance]]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun findFollowersByNameAndDomain(name: String, domain: String): List<User> {
|
||||
val followers = Users.alias("FOLLOWERS")
|
||||
return Users.innerJoin(
|
||||
otherTable = UsersFollowers,
|
||||
onColumn = { id },
|
||||
otherColumn = { userId }
|
||||
)
|
||||
.innerJoin(
|
||||
otherTable = followers,
|
||||
onColumn = { UsersFollowers.followerId },
|
||||
otherColumn = { followers[Users.id] }
|
||||
)
|
||||
.slice(
|
||||
followers[Users.id],
|
||||
followers[Users.name],
|
||||
followers[Users.domain],
|
||||
followers[Users.screenName],
|
||||
followers[Users.description],
|
||||
followers[Users.password],
|
||||
followers[Users.inbox],
|
||||
followers[Users.outbox],
|
||||
followers[Users.url],
|
||||
followers[Users.publicKey],
|
||||
followers[Users.privateKey],
|
||||
followers[Users.createdAt],
|
||||
followers[Users.keyId],
|
||||
followers[Users.following],
|
||||
followers[Users.followers],
|
||||
followers[Users.instance]
|
||||
)
|
||||
.select { Users.name eq name and (Users.domain eq domain) }
|
||||
.map {
|
||||
userBuilder.of(
|
||||
id = it[followers[Users.id]],
|
||||
name = it[followers[Users.name]],
|
||||
domain = it[followers[Users.domain]],
|
||||
screenName = it[followers[Users.screenName]],
|
||||
description = it[followers[Users.description]],
|
||||
password = it[followers[Users.password]],
|
||||
inbox = it[followers[Users.inbox]],
|
||||
outbox = it[followers[Users.outbox]],
|
||||
url = it[followers[Users.url]],
|
||||
publicKey = it[followers[Users.publicKey]],
|
||||
privateKey = it[followers[Users.privateKey]],
|
||||
createdAt = Instant.ofEpochMilli(it[followers[Users.createdAt]]),
|
||||
keyId = it[followers[Users.keyId]],
|
||||
followers = it[followers[Users.followers]],
|
||||
following = it[followers[Users.following]],
|
||||
instance = it[followers[Users.instance]]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun findFollowingById(id: Long): List<User> {
|
||||
val followers = Users.alias("FOLLOWERS")
|
||||
return Users.innerJoin(
|
||||
otherTable = UsersFollowers,
|
||||
onColumn = { Users.id },
|
||||
otherColumn = { userId }
|
||||
)
|
||||
.innerJoin(
|
||||
otherTable = followers,
|
||||
onColumn = { UsersFollowers.followerId },
|
||||
otherColumn = { followers[Users.id] }
|
||||
)
|
||||
.slice(
|
||||
followers[Users.id],
|
||||
followers[Users.name],
|
||||
followers[Users.domain],
|
||||
followers[Users.screenName],
|
||||
followers[Users.description],
|
||||
followers[Users.password],
|
||||
followers[Users.inbox],
|
||||
followers[Users.outbox],
|
||||
followers[Users.url],
|
||||
followers[Users.publicKey],
|
||||
followers[Users.privateKey],
|
||||
followers[Users.createdAt],
|
||||
followers[Users.keyId],
|
||||
followers[Users.following],
|
||||
followers[Users.followers],
|
||||
followers[Users.instance]
|
||||
)
|
||||
.select { followers[Users.id] eq id }
|
||||
.map {
|
||||
userBuilder.of(
|
||||
id = it[followers[Users.id]],
|
||||
name = it[followers[Users.name]],
|
||||
domain = it[followers[Users.domain]],
|
||||
screenName = it[followers[Users.screenName]],
|
||||
description = it[followers[Users.description]],
|
||||
password = it[followers[Users.password]],
|
||||
inbox = it[followers[Users.inbox]],
|
||||
outbox = it[followers[Users.outbox]],
|
||||
url = it[followers[Users.url]],
|
||||
publicKey = it[followers[Users.publicKey]],
|
||||
privateKey = it[followers[Users.privateKey]],
|
||||
createdAt = Instant.ofEpochMilli(it[followers[Users.createdAt]]),
|
||||
keyId = it[followers[Users.keyId]],
|
||||
followers = it[followers[Users.followers]],
|
||||
following = it[followers[Users.following]],
|
||||
instance = it[followers[Users.instance]]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun findFollowingByNameAndDomain(name: String, domain: String): List<User> {
|
||||
val followers = Users.alias("FOLLOWERS")
|
||||
return Users.innerJoin(
|
||||
otherTable = UsersFollowers,
|
||||
onColumn = { id },
|
||||
otherColumn = { userId }
|
||||
)
|
||||
.innerJoin(
|
||||
otherTable = followers,
|
||||
onColumn = { UsersFollowers.followerId },
|
||||
otherColumn = { followers[Users.id] }
|
||||
)
|
||||
.slice(
|
||||
followers[Users.id],
|
||||
followers[Users.name],
|
||||
followers[Users.domain],
|
||||
followers[Users.screenName],
|
||||
followers[Users.description],
|
||||
followers[Users.password],
|
||||
followers[Users.inbox],
|
||||
followers[Users.outbox],
|
||||
followers[Users.url],
|
||||
followers[Users.publicKey],
|
||||
followers[Users.privateKey],
|
||||
followers[Users.createdAt],
|
||||
followers[Users.keyId],
|
||||
followers[Users.following],
|
||||
followers[Users.followers],
|
||||
followers[Users.instance]
|
||||
)
|
||||
.select { followers[Users.name] eq name and (followers[Users.domain] eq domain) }
|
||||
.map {
|
||||
userBuilder.of(
|
||||
id = it[followers[Users.id]],
|
||||
name = it[followers[Users.name]],
|
||||
domain = it[followers[Users.domain]],
|
||||
screenName = it[followers[Users.screenName]],
|
||||
description = it[followers[Users.description]],
|
||||
password = it[followers[Users.password]],
|
||||
inbox = it[followers[Users.inbox]],
|
||||
outbox = it[followers[Users.outbox]],
|
||||
url = it[followers[Users.url]],
|
||||
publicKey = it[followers[Users.publicKey]],
|
||||
privateKey = it[followers[Users.privateKey]],
|
||||
createdAt = Instant.ofEpochMilli(it[followers[Users.createdAt]]),
|
||||
keyId = it[followers[Users.keyId]],
|
||||
followers = it[followers[Users.followers]],
|
||||
following = it[followers[Users.following]],
|
||||
instance = it[followers[Users.instance]]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun appendFollower(user: Long, follower: Long) {
|
||||
UsersFollowers.insert {
|
||||
it[userId] = user
|
||||
it[followerId] = follower
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun removeFollower(user: Long, follower: Long) {
|
||||
UsersFollowers.deleteWhere { userId eq user and (followerId eq follower) }
|
||||
}
|
||||
|
||||
override suspend fun alreadyFollow(userId: Long, followerId: Long): Boolean {
|
||||
return UsersFollowers.select { UsersFollowers.userId eq userId or (UsersFollowers.followerId eq followerId) }
|
||||
.empty()
|
||||
.not()
|
||||
}
|
||||
override suspend fun alreadyFollow(userId: Long, followerId: Long): Boolean =
|
||||
relationshipRepository.findByUserIdAndTargetUserId(followerId, userId)?.following ?: false
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
package dev.usbharu.hideout.core.infrastructure.exposedquery
|
||||
|
||||
import dev.usbharu.hideout.core.domain.model.relationship.Relationship
|
||||
import dev.usbharu.hideout.core.domain.model.relationship.Relationships
|
||||
import dev.usbharu.hideout.core.domain.model.relationship.toRelationships
|
||||
import dev.usbharu.hideout.core.query.RelationshipQueryService
|
||||
import org.jetbrains.exposed.sql.and
|
||||
import org.jetbrains.exposed.sql.select
|
||||
import org.springframework.stereotype.Service
|
||||
|
||||
@Service
|
||||
class RelationshipQueryServiceImpl : RelationshipQueryService {
|
||||
override suspend fun findByTargetIdAndFollowing(targetId: Long, following: Boolean): List<Relationship> =
|
||||
Relationships.select { Relationships.targetUserId eq targetId and (Relationships.following eq following) }
|
||||
.map { it.toRelationships() }
|
||||
}
|
|
@ -4,7 +4,6 @@ import dev.usbharu.hideout.application.infrastructure.exposed.ResultRowMapper
|
|||
import dev.usbharu.hideout.application.service.id.IdGenerateService
|
||||
import dev.usbharu.hideout.core.domain.model.user.User
|
||||
import dev.usbharu.hideout.core.domain.model.user.UserRepository
|
||||
import org.jetbrains.exposed.dao.id.LongIdTable
|
||||
import org.jetbrains.exposed.sql.*
|
||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||
import org.springframework.stereotype.Repository
|
||||
|
@ -62,15 +61,6 @@ class UserRepositoryImpl(
|
|||
override suspend fun findById(id: Long): User? =
|
||||
Users.select { Users.id eq id }.singleOrNull()?.let(userResultRowMapper::map)
|
||||
|
||||
override suspend fun deleteFollowRequest(id: Long, follower: Long) {
|
||||
FollowRequests.deleteWhere { userId.eq(id) and followerId.eq(follower) }
|
||||
}
|
||||
|
||||
override suspend fun findFollowRequestsById(id: Long, follower: Long): Boolean {
|
||||
return FollowRequests.select { (FollowRequests.userId eq id) and (FollowRequests.followerId eq follower) }
|
||||
.singleOrNull() != null
|
||||
}
|
||||
|
||||
override suspend fun delete(id: Long) {
|
||||
Users.deleteWhere { Users.id.eq(id) }
|
||||
}
|
||||
|
@ -108,21 +98,3 @@ object Users : Table("users") {
|
|||
uniqueIndex(name, domain)
|
||||
}
|
||||
}
|
||||
|
||||
object UsersFollowers : LongIdTable("users_followers") {
|
||||
val userId: Column<Long> = long("user_id").references(Users.id).index()
|
||||
val followerId: Column<Long> = long("follower_id").references(Users.id)
|
||||
|
||||
init {
|
||||
uniqueIndex(userId, followerId)
|
||||
}
|
||||
}
|
||||
|
||||
object FollowRequests : LongIdTable("follow_requests") {
|
||||
val userId: Column<Long> = long("user_id").references(Users.id)
|
||||
val followerId: Column<Long> = long("follower_id").references(Users.id)
|
||||
|
||||
init {
|
||||
uniqueIndex(userId, followerId)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,10 +36,14 @@ class KJobJobQueueWorkerService(private val jobQueueProcessorList: List<JobProce
|
|||
for (jobProcessor in jobQueueProcessorList) {
|
||||
kjob.register(jobProcessor.job()) {
|
||||
execute {
|
||||
@Suppress("TooGenericExceptionCaught")
|
||||
try {
|
||||
MDC.put("x-job-id", this.jobId)
|
||||
val param = it.convertUnsafe(props)
|
||||
jobProcessor.process(param)
|
||||
} catch (e: Exception) {
|
||||
logger.warn("FAILED Execute Job. job name: {} job id: {}", it.name, this.jobId, e)
|
||||
throw e
|
||||
} finally {
|
||||
MDC.remove("x-job-id")
|
||||
}
|
||||
|
|
|
@ -2,12 +2,8 @@ package dev.usbharu.hideout.core.query
|
|||
|
||||
import dev.usbharu.hideout.core.domain.model.user.User
|
||||
|
||||
@Deprecated("Use RelationshipQueryService")
|
||||
interface FollowerQueryService {
|
||||
suspend fun findFollowersById(id: Long): List<User>
|
||||
suspend fun findFollowersByNameAndDomain(name: String, domain: String): List<User>
|
||||
suspend fun findFollowingById(id: Long): List<User>
|
||||
suspend fun findFollowingByNameAndDomain(name: String, domain: String): List<User>
|
||||
suspend fun appendFollower(user: Long, follower: Long)
|
||||
suspend fun removeFollower(user: Long, follower: Long)
|
||||
suspend fun alreadyFollow(userId: Long, followerId: Long): Boolean
|
||||
}
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
package dev.usbharu.hideout.core.query
|
||||
|
||||
import dev.usbharu.hideout.core.domain.model.relationship.Relationship
|
||||
|
||||
interface RelationshipQueryService {
|
||||
|
||||
suspend fun findByTargetIdAndFollowing(targetId: Long, following: Boolean): List<Relationship>
|
||||
}
|
|
@ -36,6 +36,7 @@ class LocalFileSystemMediaDataStore(
|
|||
savePath.createDirectories()
|
||||
}
|
||||
|
||||
@Suppress("NestedBlockDepth")
|
||||
override suspend fun save(dataMediaSave: MediaSave): SavedMedia {
|
||||
val fileSavePath = buildSavePath(savePath, dataMediaSave.name)
|
||||
val thumbnailSavePath = buildSavePath(savePath, "thumbnail-" + dataMediaSave.name)
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
package dev.usbharu.hideout.core.service.relationship
|
||||
|
||||
interface RelationshipService {
|
||||
suspend fun followRequest(userId: Long, targetId: Long)
|
||||
suspend fun block(userId: Long, targetId: Long)
|
||||
|
||||
/**
|
||||
* フォローリクエストを承認します
|
||||
* [userId]が[targetId]からのフォローリクエストを承認します
|
||||
*
|
||||
* @param userId 承認操作をするユーザー
|
||||
* @param targetId 承認するフォローリクエストを送ってきたユーザー
|
||||
* @param force 強制的にAcceptアクティビティを発行する
|
||||
*/
|
||||
suspend fun acceptFollowRequest(userId: Long, targetId: Long, force: Boolean = false)
|
||||
suspend fun rejectFollowRequest(userId: Long, targetId: Long)
|
||||
suspend fun ignoreFollowRequest(userId: Long, targetId: Long)
|
||||
suspend fun unfollow(userId: Long, targetId: Long)
|
||||
suspend fun unblock(userId: Long, targetId: Long)
|
||||
suspend fun mute(userId: Long, targetId: Long)
|
||||
suspend fun unmute(userId: Long, targetId: Long)
|
||||
}
|
|
@ -0,0 +1,305 @@
|
|||
package dev.usbharu.hideout.core.service.relationship
|
||||
|
||||
import dev.usbharu.hideout.activitypub.service.activity.accept.ApSendAcceptService
|
||||
import dev.usbharu.hideout.activitypub.service.activity.block.APSendBlockService
|
||||
import dev.usbharu.hideout.activitypub.service.activity.follow.APSendFollowService
|
||||
import dev.usbharu.hideout.activitypub.service.activity.reject.ApSendRejectService
|
||||
import dev.usbharu.hideout.activitypub.service.activity.undo.APSendUndoService
|
||||
import dev.usbharu.hideout.application.config.ApplicationConfig
|
||||
import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException
|
||||
import dev.usbharu.hideout.core.domain.model.relationship.Relationship
|
||||
import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository
|
||||
import dev.usbharu.hideout.core.domain.model.user.User
|
||||
import dev.usbharu.hideout.core.query.UserQueryService
|
||||
import dev.usbharu.hideout.core.service.follow.SendFollowDto
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.stereotype.Service
|
||||
|
||||
@Service
|
||||
class RelationshipServiceImpl(
|
||||
private val applicationConfig: ApplicationConfig,
|
||||
private val userQueryService: UserQueryService,
|
||||
private val relationshipRepository: RelationshipRepository,
|
||||
private val apSendFollowService: APSendFollowService,
|
||||
private val apSendBlockService: APSendBlockService,
|
||||
private val apSendAcceptService: ApSendAcceptService,
|
||||
private val apSendRejectService: ApSendRejectService,
|
||||
private val apSendUndoService: APSendUndoService
|
||||
) : RelationshipService {
|
||||
override suspend fun followRequest(userId: Long, targetId: Long) {
|
||||
logger.info("START Follow Request userId: {} targetId: {}", userId, targetId)
|
||||
|
||||
val relationship =
|
||||
relationshipRepository.findByUserIdAndTargetUserId(userId, targetId)?.copy(followRequest = true)
|
||||
?: Relationship(
|
||||
userId = userId,
|
||||
targetUserId = targetId,
|
||||
following = false,
|
||||
blocking = false,
|
||||
muting = false,
|
||||
followRequest = true,
|
||||
ignoreFollowRequestFromTarget = false
|
||||
)
|
||||
|
||||
val inverseRelationship = relationshipRepository.findByUserIdAndTargetUserId(targetId, userId) ?: Relationship(
|
||||
userId = targetId,
|
||||
targetUserId = userId,
|
||||
following = false,
|
||||
blocking = false,
|
||||
muting = false,
|
||||
followRequest = false,
|
||||
ignoreFollowRequestFromTarget = false
|
||||
)
|
||||
|
||||
if (inverseRelationship.blocking) {
|
||||
logger.debug("FAILED Blocked by target. userId: {} targetId: {}", userId, targetId)
|
||||
return
|
||||
}
|
||||
|
||||
if (relationship.blocking) {
|
||||
logger.debug("FAILED Blocking user. userId: {} targetId: {}", userId, targetId)
|
||||
return
|
||||
}
|
||||
if (inverseRelationship.ignoreFollowRequestFromTarget) {
|
||||
logger.debug("SUCCESS Ignore Follow Request. userId: {} targetId: {}", userId, targetId)
|
||||
return
|
||||
}
|
||||
|
||||
if (relationship.following) {
|
||||
logger.debug("SUCCESS User already follow. userId: {} targetId: {}", userId, targetId)
|
||||
acceptFollowRequest(targetId, userId, true)
|
||||
return
|
||||
}
|
||||
|
||||
relationshipRepository.save(relationship)
|
||||
|
||||
val remoteUser = isRemoteUser(targetId)
|
||||
|
||||
if (remoteUser != null) {
|
||||
val user = userQueryService.findById(userId)
|
||||
apSendFollowService.sendFollow(SendFollowDto(user, remoteUser))
|
||||
} else {
|
||||
// TODO: フォロー許可制ユーザーを実装したら消す
|
||||
acceptFollowRequest(targetId, userId)
|
||||
}
|
||||
|
||||
logger.info("SUCCESS Follow Request userId: {} targetId: {}", userId, targetId)
|
||||
}
|
||||
|
||||
override suspend fun block(userId: Long, targetId: Long) {
|
||||
val relationship = relationshipRepository.findByUserIdAndTargetUserId(userId, targetId)
|
||||
?.copy(blocking = true, followRequest = false, following = false) ?: Relationship(
|
||||
userId = userId,
|
||||
targetUserId = targetId,
|
||||
following = false,
|
||||
blocking = true,
|
||||
muting = false,
|
||||
followRequest = false,
|
||||
ignoreFollowRequestFromTarget = false
|
||||
)
|
||||
|
||||
val inverseRelationship = relationshipRepository.findByUserIdAndTargetUserId(targetId, userId)
|
||||
?.copy(followRequest = false, following = false)
|
||||
|
||||
relationshipRepository.save(relationship)
|
||||
if (inverseRelationship != null) {
|
||||
relationshipRepository.save(inverseRelationship)
|
||||
}
|
||||
|
||||
val remoteUser = isRemoteUser(targetId)
|
||||
|
||||
if (remoteUser != null) {
|
||||
val user = userQueryService.findById(userId)
|
||||
apSendBlockService.sendBlock(user, remoteUser)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun acceptFollowRequest(userId: Long, targetId: Long, force: Boolean) {
|
||||
logger.info("START Accept follow request userId: {} targetId: {}", userId, targetId)
|
||||
|
||||
val relationship = relationshipRepository.findByUserIdAndTargetUserId(targetId, userId)
|
||||
|
||||
val inverseRelationship = relationshipRepository.findByUserIdAndTargetUserId(userId, targetId) ?: Relationship(
|
||||
userId = targetId,
|
||||
targetUserId = userId,
|
||||
following = false,
|
||||
blocking = false,
|
||||
muting = false,
|
||||
followRequest = false,
|
||||
ignoreFollowRequestFromTarget = false
|
||||
)
|
||||
|
||||
if (relationship == null) {
|
||||
logger.warn("FAILED Follow Request Not Found. (Relationship) userId: {} targetId: {}", userId, targetId)
|
||||
return
|
||||
}
|
||||
|
||||
if (relationship.followRequest.not() && force.not()) {
|
||||
logger.warn("FAILED Follow Request Not Found. (Follow Request) userId: {} targetId: {}", userId, targetId)
|
||||
return
|
||||
}
|
||||
|
||||
if (relationship.blocking) {
|
||||
logger.warn("FAILED Blocking user userId: {} targetId: {}", userId, targetId)
|
||||
throw IllegalStateException(
|
||||
"Cannot accept a follow request from a blocked user. userId: $userId targetId: $targetId"
|
||||
)
|
||||
}
|
||||
|
||||
if (inverseRelationship.blocking) {
|
||||
logger.warn("FAILED BLocked by user userId: {} targetId: {}", userId, targetId)
|
||||
throw IllegalStateException(
|
||||
"Cannot accept a follow request from a blocking user. userId: $userId targetId: $targetId"
|
||||
)
|
||||
}
|
||||
|
||||
val copy = relationship.copy(followRequest = false, following = true, blocking = false)
|
||||
|
||||
relationshipRepository.save(copy)
|
||||
|
||||
val remoteUser = isRemoteUser(targetId)
|
||||
|
||||
if (remoteUser != null) {
|
||||
val user = userQueryService.findById(userId)
|
||||
apSendAcceptService.sendAcceptFollow(user, remoteUser)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun rejectFollowRequest(userId: Long, targetId: Long) {
|
||||
val relationship = relationshipRepository.findByUserIdAndTargetUserId(targetId, userId)
|
||||
|
||||
if (relationship == null) {
|
||||
logger.warn("FAILED Follow Request Not Found. (Relationship) userId: {} targetId: {}", userId, targetId)
|
||||
return
|
||||
}
|
||||
|
||||
if (relationship.followRequest.not() && relationship.following.not()) {
|
||||
logger.warn("FAILED Follow Request Not Found. (Follow Request) userId: {} targetId: {}", userId, targetId)
|
||||
return
|
||||
}
|
||||
|
||||
val copy = relationship.copy(followRequest = false, following = false)
|
||||
|
||||
relationshipRepository.save(copy)
|
||||
|
||||
val remoteUser = isRemoteUser(targetId)
|
||||
|
||||
if (remoteUser != null) {
|
||||
val user = userQueryService.findById(userId)
|
||||
apSendRejectService.sendRejectFollow(user, remoteUser)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun ignoreFollowRequest(userId: Long, targetId: Long) {
|
||||
val relationship = relationshipRepository.findByUserIdAndTargetUserId(userId, targetId)
|
||||
?.copy(ignoreFollowRequestFromTarget = true)
|
||||
?: Relationship(
|
||||
userId = userId,
|
||||
targetUserId = targetId,
|
||||
following = false,
|
||||
blocking = false,
|
||||
muting = false,
|
||||
followRequest = false,
|
||||
ignoreFollowRequestFromTarget = true
|
||||
)
|
||||
|
||||
relationshipRepository.save(relationship)
|
||||
}
|
||||
|
||||
override suspend fun unfollow(userId: Long, targetId: Long) {
|
||||
val relationship = relationshipRepository.findByUserIdAndTargetUserId(userId, targetId)
|
||||
|
||||
if (relationship == null) {
|
||||
logger.warn("FAILED Unfollow. (Relationship) userId: {} targetId: {}", userId, targetId)
|
||||
return
|
||||
}
|
||||
|
||||
if (relationship.following.not()) {
|
||||
logger.warn("SUCCESS User already unfollow. userId: {} targetId: {}", userId, targetId)
|
||||
return
|
||||
}
|
||||
|
||||
val copy = relationship.copy(following = false)
|
||||
|
||||
relationshipRepository.save(copy)
|
||||
|
||||
val remoteUser = isRemoteUser(targetId)
|
||||
|
||||
if (remoteUser != null) {
|
||||
val user = userQueryService.findById(userId)
|
||||
apSendUndoService.sendUndoFollow(user, remoteUser)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun unblock(userId: Long, targetId: Long) {
|
||||
val relationship = relationshipRepository.findByUserIdAndTargetUserId(userId, targetId)
|
||||
|
||||
if (relationship == null) {
|
||||
logger.warn("FAILED Unblock. (Relationship) userId: {} targetId: {}", userId, targetId)
|
||||
return
|
||||
}
|
||||
|
||||
if (relationship.blocking.not()) {
|
||||
logger.warn("SUCCESS User is not blocking. userId: {] targetId: {}", userId, targetId)
|
||||
return
|
||||
}
|
||||
|
||||
val copy = relationship.copy(blocking = false)
|
||||
relationshipRepository.save(copy)
|
||||
|
||||
val remoteUser = isRemoteUser(targetId)
|
||||
if (remoteUser != null) {
|
||||
val user = userQueryService.findById(userId)
|
||||
apSendUndoService.sendUndoBlock(user, remoteUser)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun mute(userId: Long, targetId: Long) {
|
||||
val relationship = relationshipRepository.findByUserIdAndTargetUserId(userId, targetId)?.copy(muting = true)
|
||||
?: Relationship(
|
||||
userId = userId,
|
||||
targetUserId = targetId,
|
||||
following = false,
|
||||
blocking = false,
|
||||
muting = true,
|
||||
followRequest = false,
|
||||
ignoreFollowRequestFromTarget = false
|
||||
)
|
||||
|
||||
relationshipRepository.save(relationship)
|
||||
}
|
||||
|
||||
override suspend fun unmute(userId: Long, targetId: Long) {
|
||||
val relationship = relationshipRepository.findByUserIdAndTargetUserId(userId, targetId)?.copy(muting = false)
|
||||
|
||||
if (relationship == null) {
|
||||
logger.warn("FAILED Mute. (Relationship) userId: {} targetId: {}", userId, targetId)
|
||||
return
|
||||
}
|
||||
|
||||
relationshipRepository.save(relationship)
|
||||
}
|
||||
|
||||
private suspend fun isRemoteUser(userId: Long): User? {
|
||||
logger.trace("isRemoteUser({})", userId)
|
||||
val user = try {
|
||||
userQueryService.findById(userId)
|
||||
} catch (e: FailedToGetResourcesException) {
|
||||
logger.warn("User not found.", e)
|
||||
throw IllegalStateException("User not found.", e)
|
||||
}
|
||||
|
||||
logger.trace("user info {}", user)
|
||||
|
||||
if (user.domain == applicationConfig.url.host) {
|
||||
logger.trace("user: {} is local user", userId)
|
||||
return null
|
||||
}
|
||||
logger.trace("user: {} is remote user", userId)
|
||||
return user
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val logger = LoggerFactory.getLogger(RelationshipServiceImpl::class.java)
|
||||
}
|
||||
}
|
|
@ -11,23 +11,4 @@ interface UserService {
|
|||
suspend fun createLocalUser(user: UserCreateDto): User
|
||||
|
||||
suspend fun createRemoteUser(user: RemoteUserCreateDto): User
|
||||
|
||||
/**
|
||||
* フォローリクエストを送信する
|
||||
*
|
||||
* @param id
|
||||
* @param followerId
|
||||
* @return リクエストが成功したか
|
||||
*/
|
||||
suspend fun followRequest(id: Long, followerId: Long): Boolean
|
||||
|
||||
/**
|
||||
* フォローする
|
||||
*
|
||||
* @param id
|
||||
* @param followerId
|
||||
*/
|
||||
suspend fun follow(id: Long, followerId: Long)
|
||||
|
||||
suspend fun unfollow(id: Long, followerId: Long): Boolean
|
||||
}
|
||||
|
|
|
@ -1,13 +1,9 @@
|
|||
package dev.usbharu.hideout.core.service.user
|
||||
|
||||
import dev.usbharu.hideout.activitypub.service.activity.follow.APSendFollowService
|
||||
import dev.usbharu.hideout.application.config.ApplicationConfig
|
||||
import dev.usbharu.hideout.core.domain.exception.UserNotFoundException
|
||||
import dev.usbharu.hideout.core.domain.model.user.User
|
||||
import dev.usbharu.hideout.core.domain.model.user.UserRepository
|
||||
import dev.usbharu.hideout.core.query.FollowerQueryService
|
||||
import dev.usbharu.hideout.core.query.UserQueryService
|
||||
import dev.usbharu.hideout.core.service.follow.SendFollowDto
|
||||
import dev.usbharu.hideout.core.service.instance.InstanceService
|
||||
import org.jetbrains.exposed.exceptions.ExposedSQLException
|
||||
import org.slf4j.LoggerFactory
|
||||
|
@ -19,9 +15,7 @@ import java.time.Instant
|
|||
class UserServiceImpl(
|
||||
private val userRepository: UserRepository,
|
||||
private val userAuthService: UserAuthService,
|
||||
private val apSendFollowService: APSendFollowService,
|
||||
private val userQueryService: UserQueryService,
|
||||
private val followerQueryService: FollowerQueryService,
|
||||
private val userBuilder: User.UserBuilder,
|
||||
private val applicationConfig: ApplicationConfig,
|
||||
private val instanceService: InstanceService
|
||||
|
@ -96,38 +90,6 @@ class UserServiceImpl(
|
|||
}
|
||||
}
|
||||
|
||||
// TODO APのフォロー処理を作る
|
||||
override suspend fun followRequest(id: Long, followerId: Long): Boolean {
|
||||
val user = userRepository.findById(id) ?: throw UserNotFoundException("$id was not found.")
|
||||
val follower = userRepository.findById(followerId) ?: throw UserNotFoundException("$followerId was not found.")
|
||||
return if (user.domain == applicationConfig.url.host) {
|
||||
follow(id, followerId)
|
||||
true
|
||||
} else {
|
||||
if (userRepository.findFollowRequestsById(id, followerId)) {
|
||||
// do-nothing
|
||||
} else {
|
||||
apSendFollowService.sendFollow(SendFollowDto(follower, user))
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun follow(id: Long, followerId: Long) {
|
||||
logger.debug("START Follow id: {} → target: {}", followerId, id)
|
||||
followerQueryService.appendFollower(id, followerId)
|
||||
if (userRepository.findFollowRequestsById(id, followerId)) {
|
||||
logger.debug("Follow request is accepted! ")
|
||||
userRepository.deleteFollowRequest(id, followerId)
|
||||
}
|
||||
logger.debug("SUCCESS Follow id: {} → target: {}", followerId, id)
|
||||
}
|
||||
|
||||
override suspend fun unfollow(id: Long, followerId: Long): Boolean {
|
||||
followerQueryService.removeFollower(id, followerId)
|
||||
return false
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val logger = LoggerFactory.getLogger(UserServiceImpl::class.java)
|
||||
}
|
||||
|
|
|
@ -75,17 +75,17 @@ class MastodonAccountApiController(
|
|||
|
||||
val userid = principal.getClaim<String>("uid").toLong()
|
||||
val statusFlow = accountApiService.accountsStatuses(
|
||||
id.toLong(),
|
||||
maxId?.toLongOrNull(),
|
||||
sinceId?.toLongOrNull(),
|
||||
minId?.toLongOrNull(),
|
||||
limit,
|
||||
onlyMedia,
|
||||
excludeReplies,
|
||||
excludeReblogs,
|
||||
pinned,
|
||||
tagged,
|
||||
userid
|
||||
userid = id.toLong(),
|
||||
maxId = maxId?.toLongOrNull(),
|
||||
sinceId = sinceId?.toLongOrNull(),
|
||||
minId = minId?.toLongOrNull(),
|
||||
limit = limit,
|
||||
onlyMedia = onlyMedia,
|
||||
excludeReplies = excludeReplies,
|
||||
excludeReblogs = excludeReblogs,
|
||||
pinned = pinned,
|
||||
tagged = tagged,
|
||||
loginUser = userid
|
||||
).asFlow()
|
||||
ResponseEntity.ok(statusFlow)
|
||||
}
|
||||
|
@ -103,4 +103,44 @@ class MastodonAccountApiController(
|
|||
.asFlow()
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun apiV1AccountsIdBlockPost(id: String): ResponseEntity<Relationship> {
|
||||
val principal = SecurityContextHolder.getContext().getAuthentication().principal as Jwt
|
||||
|
||||
val userid = principal.getClaim<String>("uid").toLong()
|
||||
|
||||
val block = accountApiService.block(userid, id.toLong())
|
||||
|
||||
return ResponseEntity.ok(block)
|
||||
}
|
||||
|
||||
override suspend fun apiV1AccountsIdUnblockPost(id: String): ResponseEntity<Relationship> {
|
||||
val principal = SecurityContextHolder.getContext().getAuthentication().principal as Jwt
|
||||
|
||||
val userid = principal.getClaim<String>("uid").toLong()
|
||||
|
||||
val unblock = accountApiService.unblock(userid, id.toLong())
|
||||
|
||||
return ResponseEntity.ok(unblock)
|
||||
}
|
||||
|
||||
override suspend fun apiV1AccountsIdUnfollowPost(id: String): ResponseEntity<Relationship> {
|
||||
val principal = SecurityContextHolder.getContext().getAuthentication().principal as Jwt
|
||||
|
||||
val userid = principal.getClaim<String>("uid").toLong()
|
||||
|
||||
val unfollow = accountApiService.unfollow(userid, id.toLong())
|
||||
|
||||
return ResponseEntity.ok(unfollow)
|
||||
}
|
||||
|
||||
override suspend fun apiV1AccountsIdRemoveFromFollowersPost(id: String): ResponseEntity<Relationship> {
|
||||
val principal = SecurityContextHolder.getContext().getAuthentication().principal as Jwt
|
||||
|
||||
val userid = principal.getClaim<String>("uid").toLong()
|
||||
|
||||
val removeFromFollowers = accountApiService.removeFromFollowers(userid, id.toLong())
|
||||
|
||||
return ResponseEntity.ok(removeFromFollowers)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package dev.usbharu.hideout.mastodon.service.account
|
||||
|
||||
import dev.usbharu.hideout.application.external.Transaction
|
||||
import dev.usbharu.hideout.core.domain.model.user.UserRepository
|
||||
import dev.usbharu.hideout.core.query.FollowerQueryService
|
||||
import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository
|
||||
import dev.usbharu.hideout.core.service.relationship.RelationshipService
|
||||
import dev.usbharu.hideout.core.service.user.UserCreateDto
|
||||
import dev.usbharu.hideout.core.service.user.UserService
|
||||
import dev.usbharu.hideout.domain.mastodon.model.generated.*
|
||||
|
@ -13,6 +13,7 @@ import kotlin.math.min
|
|||
|
||||
@Service
|
||||
interface AccountApiService {
|
||||
@Suppress("LongParameterList")
|
||||
suspend fun accountsStatuses(
|
||||
userid: Long,
|
||||
maxId: Long?,
|
||||
|
@ -29,9 +30,21 @@ interface AccountApiService {
|
|||
|
||||
suspend fun verifyCredentials(userid: Long): CredentialAccount
|
||||
suspend fun registerAccount(userCreateDto: UserCreateDto): Unit
|
||||
suspend fun follow(userid: Long, followeeId: Long): Relationship
|
||||
suspend fun follow(loginUser: Long, followTargetUserId: Long): Relationship
|
||||
suspend fun account(id: Long): Account
|
||||
suspend fun relationships(userid: Long, id: List<Long>, withSuspended: Boolean): List<Relationship>
|
||||
|
||||
/**
|
||||
* ブロック操作を行う
|
||||
*
|
||||
* @param userid ブロック操作を行ったユーザーid
|
||||
* @param target ブロック対象のユーザーid
|
||||
* @return ブロック後のブロック対象ユーザーとの[Relationship]
|
||||
*/
|
||||
suspend fun block(userid: Long, target: Long): Relationship
|
||||
suspend fun unblock(userid: Long, target: Long): Relationship
|
||||
suspend fun unfollow(userid: Long, target: Long): Relationship
|
||||
suspend fun removeFromFollowers(userid: Long, target: Long): Relationship
|
||||
}
|
||||
|
||||
@Service
|
||||
|
@ -39,9 +52,9 @@ class AccountApiServiceImpl(
|
|||
private val accountService: AccountService,
|
||||
private val transaction: Transaction,
|
||||
private val userService: UserService,
|
||||
private val followerQueryService: FollowerQueryService,
|
||||
private val userRepository: UserRepository,
|
||||
private val statusQueryService: StatusQueryService
|
||||
private val statusQueryService: StatusQueryService,
|
||||
private val relationshipService: RelationshipService,
|
||||
private val relationshipRepository: RelationshipRepository
|
||||
) :
|
||||
AccountApiService {
|
||||
override suspend fun accountsStatuses(
|
||||
|
@ -61,23 +74,23 @@ class AccountApiServiceImpl(
|
|||
false
|
||||
} else {
|
||||
transaction.transaction {
|
||||
followerQueryService.alreadyFollow(userid, loginUser)
|
||||
isFollowing(loginUser, userid)
|
||||
}
|
||||
}
|
||||
|
||||
return transaction.transaction {
|
||||
statusQueryService.accountsStatus(
|
||||
userid,
|
||||
maxId,
|
||||
sinceId,
|
||||
minId,
|
||||
limit,
|
||||
onlyMedia,
|
||||
excludeReplies,
|
||||
excludeReblogs,
|
||||
pinned,
|
||||
tagged,
|
||||
canViewFollowers
|
||||
accountId = userid,
|
||||
maxId = maxId,
|
||||
sinceId = sinceId,
|
||||
minId = minId,
|
||||
limit = limit,
|
||||
onlyMedia = onlyMedia,
|
||||
excludeReplies = excludeReplies,
|
||||
excludeReblogs = excludeReblogs,
|
||||
pinned = pinned,
|
||||
tagged = tagged,
|
||||
includeFollowers = canViewFollowers
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -91,34 +104,10 @@ class AccountApiServiceImpl(
|
|||
userService.createLocalUser(UserCreateDto(userCreateDto.name, userCreateDto.name, "", userCreateDto.password))
|
||||
}
|
||||
|
||||
override suspend fun follow(userid: Long, followeeId: Long): Relationship = transaction.transaction {
|
||||
val alreadyFollow = followerQueryService.alreadyFollow(followeeId, userid)
|
||||
override suspend fun follow(loginUser: Long, followTargetUserId: Long): Relationship = transaction.transaction {
|
||||
relationshipService.followRequest(loginUser, followTargetUserId)
|
||||
|
||||
val followRequest = if (alreadyFollow) {
|
||||
true
|
||||
} else {
|
||||
userService.followRequest(followeeId, userid)
|
||||
}
|
||||
|
||||
val alreadyFollow1 = followerQueryService.alreadyFollow(userid, followeeId)
|
||||
|
||||
val followRequestsById = userRepository.findFollowRequestsById(followeeId, userid)
|
||||
|
||||
return@transaction Relationship(
|
||||
followeeId.toString(),
|
||||
followRequest,
|
||||
true,
|
||||
false,
|
||||
alreadyFollow1,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
followRequestsById,
|
||||
false,
|
||||
false,
|
||||
""
|
||||
)
|
||||
return@transaction fetchRelationship(loginUser, followTargetUserId)
|
||||
}
|
||||
|
||||
override suspend fun account(id: Long): Account = transaction.transaction {
|
||||
|
@ -136,30 +125,34 @@ class AccountApiServiceImpl(
|
|||
val subList = id.subList(0, min(id.size, 20))
|
||||
|
||||
return@transaction subList.map {
|
||||
val alreadyFollow = followerQueryService.alreadyFollow(userid, it)
|
||||
|
||||
val followed = followerQueryService.alreadyFollow(it, userid)
|
||||
|
||||
val requested = userRepository.findFollowRequestsById(it, userid)
|
||||
|
||||
Relationship(
|
||||
id = it.toString(),
|
||||
following = alreadyFollow,
|
||||
showingReblogs = true,
|
||||
notifying = false,
|
||||
followedBy = followed,
|
||||
blocking = false,
|
||||
blockedBy = false,
|
||||
muting = false,
|
||||
mutingNotifications = false,
|
||||
requested = requested,
|
||||
domainBlocking = false,
|
||||
endorsed = false,
|
||||
note = ""
|
||||
)
|
||||
fetchRelationship(userid, it)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun block(userid: Long, target: Long): Relationship = transaction.transaction {
|
||||
relationshipService.block(userid, target)
|
||||
|
||||
fetchRelationship(userid, target)
|
||||
}
|
||||
|
||||
override suspend fun unblock(userid: Long, target: Long): Relationship = transaction.transaction {
|
||||
relationshipService.unblock(userid, target)
|
||||
|
||||
return@transaction fetchRelationship(userid, target)
|
||||
}
|
||||
|
||||
override suspend fun unfollow(userid: Long, target: Long): Relationship = transaction.transaction {
|
||||
relationshipService.unfollow(userid, target)
|
||||
|
||||
return@transaction fetchRelationship(userid, target)
|
||||
}
|
||||
|
||||
override suspend fun removeFromFollowers(userid: Long, target: Long): Relationship = transaction.transaction {
|
||||
relationshipService.rejectFollowRequest(userid, target)
|
||||
|
||||
return@transaction fetchRelationship(userid, target)
|
||||
}
|
||||
|
||||
private fun from(account: Account): CredentialAccount {
|
||||
return CredentialAccount(
|
||||
id = account.id,
|
||||
|
@ -198,6 +191,49 @@ class AccountApiServiceImpl(
|
|||
)
|
||||
}
|
||||
|
||||
private suspend fun fetchRelationship(userid: Long, targetId: Long): Relationship {
|
||||
val relationship = relationshipRepository.findByUserIdAndTargetUserId(userid, targetId)
|
||||
?: dev.usbharu.hideout.core.domain.model.relationship.Relationship(
|
||||
userId = userid,
|
||||
targetUserId = targetId,
|
||||
following = false,
|
||||
blocking = false,
|
||||
muting = false,
|
||||
followRequest = false,
|
||||
ignoreFollowRequestFromTarget = false
|
||||
)
|
||||
|
||||
val inverseRelationship = relationshipRepository.findByUserIdAndTargetUserId(targetId, userid)
|
||||
?: dev.usbharu.hideout.core.domain.model.relationship.Relationship(
|
||||
userId = targetId,
|
||||
targetUserId = userid,
|
||||
following = false,
|
||||
blocking = false,
|
||||
muting = false,
|
||||
followRequest = false,
|
||||
ignoreFollowRequestFromTarget = false
|
||||
)
|
||||
|
||||
return Relationship(
|
||||
id = targetId.toString(),
|
||||
following = relationship.following,
|
||||
showingReblogs = true,
|
||||
notifying = false,
|
||||
followedBy = inverseRelationship.following,
|
||||
blocking = relationship.blocking,
|
||||
blockedBy = inverseRelationship.blocking,
|
||||
muting = relationship.muting,
|
||||
mutingNotifications = relationship.muting,
|
||||
requested = relationship.followRequest,
|
||||
domainBlocking = false,
|
||||
endorsed = false,
|
||||
note = ""
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun isFollowing(userid: Long, target: Long): Boolean =
|
||||
relationshipRepository.findByUserIdAndTargetUserId(userid, target)?.following ?: false
|
||||
|
||||
companion object {
|
||||
private val logger = LoggerFactory.getLogger(AccountApiServiceImpl::class.java)
|
||||
}
|
||||
|
|
|
@ -55,12 +55,12 @@ class AppApiServiceImpl(
|
|||
registeredClientRepository.save(registeredClient)
|
||||
|
||||
Application(
|
||||
appsRequest.clientName,
|
||||
"invalid-vapid-key",
|
||||
appsRequest.website,
|
||||
id,
|
||||
clientSecret,
|
||||
appsRequest.redirectUris
|
||||
name = appsRequest.clientName,
|
||||
vapidKey = "invalid-vapid-key",
|
||||
website = appsRequest.website,
|
||||
clientId = id,
|
||||
clientSecret = clientSecret,
|
||||
redirectUri = appsRequest.redirectUris
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,14 +34,7 @@ create table if not exists users
|
|||
unique ("name", "domain"),
|
||||
constraint fk_users_instance__id foreign key ("instance") references instance (id) on delete restrict on update restrict
|
||||
);
|
||||
create table if not exists follow_requests
|
||||
(
|
||||
id bigserial primary key,
|
||||
user_id bigint not null,
|
||||
follower_id bigint not null,
|
||||
constraint fk_follow_requests_user_id__id foreign key (user_id) references users (id) on delete restrict on update restrict,
|
||||
constraint fk_follow_requests_follower_id__id foreign key (follower_id) references users (id) on delete restrict on update restrict
|
||||
);
|
||||
|
||||
create table if not exists media
|
||||
(
|
||||
id bigint primary key,
|
||||
|
@ -119,14 +112,7 @@ create table if not exists timelines
|
|||
is_pure_repost boolean not null,
|
||||
media_ids varchar(255) not null
|
||||
);
|
||||
create table if not exists users_followers
|
||||
(
|
||||
id bigserial primary key,
|
||||
user_id bigint not null,
|
||||
follower_id bigint not null,
|
||||
constraint fk_users_followers_user_id__id foreign key (user_id) references users (id) on delete restrict on update restrict,
|
||||
constraint fk_users_followers_follower_id__id foreign key (follower_id) references users (id) on delete restrict on update restrict
|
||||
);
|
||||
|
||||
create table if not exists application_authorization
|
||||
(
|
||||
id varchar(255) primary key,
|
||||
|
@ -186,4 +172,19 @@ create table if not exists registered_client
|
|||
scopes varchar(1000) not null,
|
||||
client_settings varchar(2000) not null,
|
||||
token_settings varchar(2000) not null
|
||||
);
|
||||
|
||||
create table if not exists relationships
|
||||
(
|
||||
id bigserial primary key,
|
||||
user_id bigint not null,
|
||||
target_user_id bigint not null,
|
||||
following boolean not null,
|
||||
blocking boolean not null,
|
||||
muting boolean not null,
|
||||
follow_request boolean not null,
|
||||
ignore_follow_request boolean not null,
|
||||
constraint fk_relationships_user_id__id foreign key (user_id) references users (id) on delete restrict on update restrict,
|
||||
constraint fk_relationships_target_user_id__id foreign key (target_user_id) references users (id) on delete restrict on update restrict,
|
||||
unique (user_id, target_user_id)
|
||||
)
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
<configuration>
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level [%X{x-request-id}] %logger{36} - %msg%n</pattern>
|
||||
<pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level [%X{x-request-id}] [%X{x-job-id}] %logger{36} -
|
||||
%msg%n
|
||||
</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
<root level="DEBUG">
|
||||
|
|
|
@ -287,6 +287,89 @@ paths:
|
|||
schema:
|
||||
$ref: "#/components/schemas/Relationship"
|
||||
|
||||
/api/v1/accounts/{id}/block:
|
||||
post:
|
||||
tags:
|
||||
- account
|
||||
security:
|
||||
- OAuth2:
|
||||
- "write:blocks"
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
200:
|
||||
description: 成功
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Relationship"
|
||||
|
||||
/api/v1/accounts/{id}/unfollow:
|
||||
post:
|
||||
tags:
|
||||
- account
|
||||
security:
|
||||
- OAuth2:
|
||||
- "write:follows"
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
200:
|
||||
description: 成功
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Relationship"
|
||||
|
||||
/api/v1/accounts/{id}/unblock:
|
||||
post:
|
||||
tags:
|
||||
- account
|
||||
security:
|
||||
- OAuth2:
|
||||
- "write:blocks"
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
200:
|
||||
description: 成功
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Relationship"
|
||||
/api/v1/accounts/{id}/remove_from_followers:
|
||||
post:
|
||||
tags:
|
||||
- account
|
||||
security:
|
||||
- OAuth2:
|
||||
- "write:follows"
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
200:
|
||||
description: 成功
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Relationship"
|
||||
|
||||
/api/v1/accounts/{id}/statuses:
|
||||
get:
|
||||
tags:
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
package dev.usbharu.hideout.activitypub.domain.model
|
||||
|
||||
import com.fasterxml.jackson.module.kotlin.readValue
|
||||
import dev.usbharu.hideout.application.config.ActivityPubConfig
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.intellij.lang.annotations.Language
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.springframework.boot.test.json.BasicJsonTester
|
||||
|
||||
class BlockTest {
|
||||
@Test
|
||||
fun blockDeserializeTest() {
|
||||
@Language("JSON") val json = """{
|
||||
"@context" : [ "https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1", {
|
||||
"manuallyApprovesFollowers" : "as:manuallyApprovesFollowers",
|
||||
"sensitive" : "as:sensitive",
|
||||
"Hashtag" : "as:Hashtag",
|
||||
"quoteUrl" : "as:quoteUrl",
|
||||
"toot" : "http://joinmastodon.org/ns#",
|
||||
"Emoji" : "toot:Emoji",
|
||||
"featured" : "toot:featured",
|
||||
"discoverable" : "toot:discoverable",
|
||||
"schema" : "http://schema.org#",
|
||||
"PropertyValue" : "schema:PropertyValue",
|
||||
"value" : "schema:value",
|
||||
"misskey" : "https://misskey-hub.net/ns#",
|
||||
"_misskey_content" : "misskey:_misskey_content",
|
||||
"_misskey_quote" : "misskey:_misskey_quote",
|
||||
"_misskey_reaction" : "misskey:_misskey_reaction",
|
||||
"_misskey_votes" : "misskey:_misskey_votes",
|
||||
"_misskey_summary" : "misskey:_misskey_summary",
|
||||
"isCat" : "misskey:isCat",
|
||||
"vcard" : "http://www.w3.org/2006/vcard/ns#"
|
||||
} ],
|
||||
"type" : "Block",
|
||||
"id" : "https://misskey.usbharu.dev/blocks/9myf6e40vm",
|
||||
"actor" : "https://misskey.usbharu.dev/users/97ws8y3rj6",
|
||||
"object" : "https://test-hideout.usbharu.dev/users/test-user2"
|
||||
}
|
||||
"""
|
||||
|
||||
val objectMapper = ActivityPubConfig().objectMapper()
|
||||
|
||||
val block = objectMapper.readValue<Block>(json)
|
||||
|
||||
val expected = Block(
|
||||
"https://misskey.usbharu.dev/users/97ws8y3rj6",
|
||||
"https://misskey.usbharu.dev/blocks/9myf6e40vm",
|
||||
"https://test-hideout.usbharu.dev/users/test-user2"
|
||||
).apply { context = listOf("https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1") }
|
||||
assertThat(block).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun blockSerializeTest() {
|
||||
val basicJsonTester = BasicJsonTester(javaClass)
|
||||
|
||||
val block = Block(
|
||||
"https://misskey.usbharu.dev/users/97ws8y3rj6",
|
||||
"https://misskey.usbharu.dev/blocks/9myf6e40vm",
|
||||
"https://test-hideout.usbharu.dev/users/test-user2"
|
||||
).apply { context = listOf("https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1") }
|
||||
|
||||
val objectMapper = ActivityPubConfig().objectMapper()
|
||||
|
||||
val writeValueAsString = objectMapper.writeValueAsString(block)
|
||||
|
||||
val from = basicJsonTester.from(writeValueAsString)
|
||||
assertThat(from).extractingJsonPathStringValue("$.actor")
|
||||
.isEqualTo("https://misskey.usbharu.dev/users/97ws8y3rj6")
|
||||
assertThat(from).extractingJsonPathStringValue("$.id")
|
||||
.isEqualTo("https://misskey.usbharu.dev/blocks/9myf6e40vm")
|
||||
assertThat(from).extractingJsonPathStringValue("$.object")
|
||||
.isEqualTo("https://test-hideout.usbharu.dev/users/test-user2")
|
||||
assertThat(from).extractingJsonPathStringValue("$.type").isEqualTo("Block")
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
package dev.usbharu.hideout.activitypub.domain.model
|
||||
|
||||
import com.fasterxml.jackson.module.kotlin.readValue
|
||||
import dev.usbharu.hideout.application.config.ActivityPubConfig
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.intellij.lang.annotations.Language
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.springframework.boot.test.json.BasicJsonTester
|
||||
|
||||
class RejectTest {
|
||||
@Test
|
||||
fun rejectDeserializeTest() {
|
||||
@Language("JSON") val json = """{
|
||||
"@context" : [ "https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1", {
|
||||
"manuallyApprovesFollowers" : "as:manuallyApprovesFollowers",
|
||||
"sensitive" : "as:sensitive",
|
||||
"Hashtag" : "as:Hashtag",
|
||||
"quoteUrl" : "as:quoteUrl",
|
||||
"toot" : "http://joinmastodon.org/ns#",
|
||||
"Emoji" : "toot:Emoji",
|
||||
"featured" : "toot:featured",
|
||||
"discoverable" : "toot:discoverable",
|
||||
"schema" : "http://schema.org#",
|
||||
"PropertyValue" : "schema:PropertyValue",
|
||||
"value" : "schema:value",
|
||||
"misskey" : "https://misskey-hub.net/ns#",
|
||||
"_misskey_content" : "misskey:_misskey_content",
|
||||
"_misskey_quote" : "misskey:_misskey_quote",
|
||||
"_misskey_reaction" : "misskey:_misskey_reaction",
|
||||
"_misskey_votes" : "misskey:_misskey_votes",
|
||||
"_misskey_summary" : "misskey:_misskey_summary",
|
||||
"isCat" : "misskey:isCat",
|
||||
"vcard" : "http://www.w3.org/2006/vcard/ns#"
|
||||
} ],
|
||||
"type" : "Reject",
|
||||
"actor" : "https://misskey.usbharu.dev/users/97ws8y3rj6",
|
||||
"object" : {
|
||||
"id" : "https://misskey.usbharu.dev/follows/9mxh6mawru/97ws8y3rj6",
|
||||
"type" : "Follow",
|
||||
"actor" : "https://test-hideout.usbharu.dev/users/test-user2",
|
||||
"object" : "https://misskey.usbharu.dev/users/97ws8y3rj6"
|
||||
},
|
||||
"id" : "https://misskey.usbharu.dev/06407419-5aeb-4e2d-8885-aa54b03decf0"
|
||||
}
|
||||
"""
|
||||
|
||||
val objectMapper = ActivityPubConfig().objectMapper()
|
||||
|
||||
val reject = objectMapper.readValue<Reject>(json)
|
||||
|
||||
val expected = Reject(
|
||||
"https://misskey.usbharu.dev/users/97ws8y3rj6",
|
||||
"https://misskey.usbharu.dev/06407419-5aeb-4e2d-8885-aa54b03decf0",
|
||||
Follow(
|
||||
apObject = "https://misskey.usbharu.dev/users/97ws8y3rj6",
|
||||
actor = "https://test-hideout.usbharu.dev/users/test-user2"
|
||||
)
|
||||
).apply { context = listOf("https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1") }
|
||||
|
||||
assertThat(reject).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun rejectSerializeTest() {
|
||||
val basicJsonTester = BasicJsonTester(javaClass)
|
||||
|
||||
val reject = Reject(
|
||||
"https://misskey.usbharu.dev/users/97ws8y3rj6",
|
||||
"https://misskey.usbharu.dev/06407419-5aeb-4e2d-8885-aa54b03decf0",
|
||||
Follow(
|
||||
apObject = "https://misskey.usbharu.dev/users/97ws8y3rj6",
|
||||
actor = "https://test-hideout.usbharu.dev/users/test-user2"
|
||||
)
|
||||
).apply { context = listOf("https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1") }
|
||||
|
||||
val objectMapper = ActivityPubConfig().objectMapper()
|
||||
|
||||
val writeValueAsString = objectMapper.writeValueAsString(reject)
|
||||
|
||||
val from = basicJsonTester.from(writeValueAsString)
|
||||
|
||||
assertThat(from).extractingJsonPathStringValue("$.actor")
|
||||
.isEqualTo("https://misskey.usbharu.dev/users/97ws8y3rj6")
|
||||
assertThat(from).extractingJsonPathStringValue("$.id")
|
||||
.isEqualTo("https://misskey.usbharu.dev/06407419-5aeb-4e2d-8885-aa54b03decf0")
|
||||
assertThat(from).extractingJsonPathStringValue("$.type").isEqualTo("Reject")
|
||||
assertThat(from).extractingJsonPathStringValue("$.object.actor")
|
||||
.isEqualTo("https://test-hideout.usbharu.dev/users/test-user2")
|
||||
assertThat(from).extractingJsonPathStringValue("$.object.object")
|
||||
.isEqualTo("https://misskey.usbharu.dev/users/97ws8y3rj6")
|
||||
assertThat(from).extractingJsonPathStringValue("$.object.type").isEqualTo("Follow")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
package dev.usbharu.hideout.activitypub.service.activity.accept
|
||||
|
||||
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.core.external.job.DeliverAcceptJob
|
||||
import dev.usbharu.hideout.core.external.job.DeliverAcceptJobParam
|
||||
import dev.usbharu.hideout.core.query.UserQueryService
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.extension.ExtendWith
|
||||
import org.mockito.InjectMocks
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Spy
|
||||
import org.mockito.junit.jupiter.MockitoExtension
|
||||
import org.mockito.kotlin.*
|
||||
import utils.TestTransaction
|
||||
import utils.UserBuilder
|
||||
|
||||
@ExtendWith(MockitoExtension::class)
|
||||
class APDeliverAcceptJobProcessorTest {
|
||||
|
||||
@Mock
|
||||
private lateinit var apRequestService: APRequestService
|
||||
|
||||
@Mock
|
||||
private lateinit var userQueryService: UserQueryService
|
||||
|
||||
@Mock
|
||||
private lateinit var deliverAcceptJob: DeliverAcceptJob
|
||||
|
||||
@Spy
|
||||
private val transaction = TestTransaction
|
||||
|
||||
@InjectMocks
|
||||
private lateinit var apDeliverAcceptJobProcessor: APDeliverAcceptJobProcessor
|
||||
|
||||
@Test
|
||||
fun `process apPostが発行される`() = runTest {
|
||||
val user = UserBuilder.localUserOf()
|
||||
|
||||
whenever(userQueryService.findById(eq(1))).doReturn(user)
|
||||
|
||||
val accept = Accept(
|
||||
apObject = Follow(
|
||||
apObject = "https://example.com",
|
||||
actor = "https://remote.example.com"
|
||||
),
|
||||
actor = "https://example.com"
|
||||
)
|
||||
val param = DeliverAcceptJobParam(
|
||||
accept = accept,
|
||||
"https://remote.example.com",
|
||||
1
|
||||
)
|
||||
|
||||
apDeliverAcceptJobProcessor.process(param)
|
||||
|
||||
verify(apRequestService, times(1)).apPost(eq("https://remote.example.com"), eq(accept), eq(user))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `job DeliverAcceptJobが返ってくる`() {
|
||||
val actual = apDeliverAcceptJobProcessor.job()
|
||||
|
||||
assertThat(actual).isEqualTo(deliverAcceptJob)
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
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.activitypub.service.common.ActivityPubProcessContext
|
||||
import dev.usbharu.hideout.activitypub.service.common.ActivityType
|
||||
import dev.usbharu.hideout.application.config.ActivityPubConfig
|
||||
import dev.usbharu.hideout.core.query.UserQueryService
|
||||
import dev.usbharu.hideout.core.service.relationship.RelationshipService
|
||||
import dev.usbharu.httpsignature.common.HttpHeaders
|
||||
import dev.usbharu.httpsignature.common.HttpMethod
|
||||
import dev.usbharu.httpsignature.common.HttpRequest
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.DynamicTest
|
||||
import org.junit.jupiter.api.DynamicTest.dynamicTest
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestFactory
|
||||
import org.junit.jupiter.api.assertThrows
|
||||
import org.junit.jupiter.api.extension.ExtendWith
|
||||
import org.mockito.InjectMocks
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Spy
|
||||
import org.mockito.junit.jupiter.MockitoExtension
|
||||
import org.mockito.kotlin.*
|
||||
import utils.TestTransaction
|
||||
import utils.UserBuilder
|
||||
import java.net.URL
|
||||
|
||||
|
||||
@ExtendWith(MockitoExtension::class)
|
||||
class ApAcceptProcessorTest {
|
||||
|
||||
@Mock
|
||||
private lateinit var userQueryService: UserQueryService
|
||||
|
||||
@Mock
|
||||
private lateinit var relationshipService: RelationshipService
|
||||
|
||||
@Spy
|
||||
private val transaction = TestTransaction
|
||||
|
||||
@InjectMocks
|
||||
private lateinit var apAcceptProcessor: ApAcceptProcessor
|
||||
|
||||
@Test
|
||||
fun `internalProcess objectがFollowの場合フォローを承認する`() = runTest {
|
||||
|
||||
val json = """"""
|
||||
val objectMapper = ActivityPubConfig().objectMapper()
|
||||
val jsonNode = objectMapper.readTree(json)
|
||||
|
||||
val accept = Accept(
|
||||
apObject = Follow(
|
||||
apObject = "https://example.com",
|
||||
actor = "https://remote.example.com"
|
||||
),
|
||||
actor = "https://example.com"
|
||||
)
|
||||
val activity = ActivityPubProcessContext<Accept>(
|
||||
accept, jsonNode, HttpRequest(
|
||||
URL("https://example.com"),
|
||||
HttpHeaders(emptyMap()), HttpMethod.POST
|
||||
), null, true
|
||||
)
|
||||
|
||||
val user = UserBuilder.localUserOf()
|
||||
whenever(userQueryService.findByUrl(eq("https://example.com"))).doReturn(user)
|
||||
val remoteUser = UserBuilder.remoteUserOf()
|
||||
whenever(userQueryService.findByUrl(eq("https://remote.example.com"))).doReturn(remoteUser)
|
||||
|
||||
apAcceptProcessor.internalProcess(activity)
|
||||
|
||||
verify(relationshipService, times(1)).acceptFollowRequest(eq(user.id), eq(remoteUser.id), eq(false))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `internalProcess objectがFollow以外の場合IllegalActivityPubObjecExceptionが発生する`() = runTest {
|
||||
val json = """"""
|
||||
val objectMapper = ActivityPubConfig().objectMapper()
|
||||
val jsonNode = objectMapper.readTree(json)
|
||||
|
||||
val accept = Accept(
|
||||
apObject = Like(
|
||||
apObject = "https://example.com",
|
||||
actor = "https://remote.example.com",
|
||||
content = "",
|
||||
id = ""
|
||||
),
|
||||
actor = "https://example.com"
|
||||
)
|
||||
val activity = ActivityPubProcessContext<Accept>(
|
||||
accept, jsonNode, HttpRequest(
|
||||
URL("https://example.com"),
|
||||
HttpHeaders(emptyMap()), HttpMethod.POST
|
||||
), null, true
|
||||
)
|
||||
|
||||
assertThrows<IllegalActivityPubObjectException> {
|
||||
apAcceptProcessor.internalProcess(activity)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `isSupproted Acceptにはtrue`() {
|
||||
val actual = apAcceptProcessor.isSupported(ActivityType.Accept)
|
||||
assertThat(actual).isTrue()
|
||||
}
|
||||
|
||||
@TestFactory
|
||||
fun `isSupported Accept以外にはfalse`(): List<DynamicTest> {
|
||||
return ActivityType
|
||||
.values()
|
||||
.filterNot { it == ActivityType.Accept }
|
||||
.map {
|
||||
dynamicTest("isSupported $it にはfalse") {
|
||||
|
||||
val actual = apAcceptProcessor.isSupported(it)
|
||||
assertThat(actual).isFalse()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `type Acceptのclassjavaが返ってくる`() {
|
||||
assertThat(apAcceptProcessor.type()).isEqualTo(Accept::class.java)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
package dev.usbharu.hideout.activitypub.service.activity.accept
|
||||
|
||||
import dev.usbharu.hideout.activitypub.domain.model.Accept
|
||||
import dev.usbharu.hideout.activitypub.domain.model.Follow
|
||||
import dev.usbharu.hideout.core.external.job.DeliverAcceptJob
|
||||
import dev.usbharu.hideout.core.external.job.DeliverAcceptJobParam
|
||||
import dev.usbharu.hideout.core.service.job.JobQueueParentService
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.extension.ExtendWith
|
||||
import org.mockito.InjectMocks
|
||||
import org.mockito.Mock
|
||||
import org.mockito.junit.jupiter.MockitoExtension
|
||||
import org.mockito.kotlin.eq
|
||||
import org.mockito.kotlin.times
|
||||
import org.mockito.kotlin.verify
|
||||
import utils.UserBuilder
|
||||
|
||||
@ExtendWith(MockitoExtension::class)
|
||||
class ApSendAcceptServiceImplTest {
|
||||
|
||||
@Mock
|
||||
private lateinit var jobQueueParentService: JobQueueParentService
|
||||
|
||||
@Mock
|
||||
private lateinit var deliverAcceptJob: DeliverAcceptJob
|
||||
|
||||
@InjectMocks
|
||||
private lateinit var apSendAcceptServiceImpl: ApSendAcceptServiceImpl
|
||||
|
||||
@Test
|
||||
fun `sendAccept DeliverAcceptJobが発行される`() = runTest {
|
||||
val user = UserBuilder.localUserOf()
|
||||
val remoteUser = UserBuilder.remoteUserOf()
|
||||
|
||||
apSendAcceptServiceImpl.sendAcceptFollow(user, remoteUser)
|
||||
|
||||
val deliverAcceptJobParam = DeliverAcceptJobParam(
|
||||
Accept(apObject = Follow(apObject = user.url, actor = remoteUser.url), actor = user.url),
|
||||
remoteUser.inbox,
|
||||
user.id
|
||||
)
|
||||
verify(jobQueueParentService, times(1)).scheduleTypeSafe(eq(deliverAcceptJob), eq(deliverAcceptJobParam))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
package dev.usbharu.hideout.activitypub.service.activity.block
|
||||
|
||||
import dev.usbharu.hideout.activitypub.domain.model.Block
|
||||
import dev.usbharu.hideout.activitypub.domain.model.Follow
|
||||
import dev.usbharu.hideout.activitypub.domain.model.Reject
|
||||
import dev.usbharu.hideout.activitypub.service.common.APRequestService
|
||||
import dev.usbharu.hideout.core.domain.model.user.UserRepository
|
||||
import dev.usbharu.hideout.core.external.job.DeliverBlockJob
|
||||
import dev.usbharu.hideout.core.external.job.DeliverBlockJobParam
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.extension.ExtendWith
|
||||
import org.mockito.InjectMocks
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Spy
|
||||
import org.mockito.junit.jupiter.MockitoExtension
|
||||
import org.mockito.kotlin.*
|
||||
import utils.TestTransaction
|
||||
import utils.UserBuilder
|
||||
|
||||
@ExtendWith(MockitoExtension::class)
|
||||
class APDeliverBlockJobProcessorTest {
|
||||
|
||||
@Mock
|
||||
private lateinit var apRequestService: APRequestService
|
||||
|
||||
@Mock
|
||||
private lateinit var userRepository: UserRepository
|
||||
|
||||
@Spy
|
||||
private val transaction = TestTransaction
|
||||
|
||||
@Mock
|
||||
private lateinit var deliverBlockJob: DeliverBlockJob
|
||||
|
||||
@InjectMocks
|
||||
private lateinit var apDeliverBlockJobProcessor: APDeliverBlockJobProcessor
|
||||
|
||||
@Test
|
||||
fun `process rejectとblockがapPostされる`() = runTest {
|
||||
val user = UserBuilder.localUserOf()
|
||||
whenever(userRepository.findById(eq(user.id))).doReturn(user)
|
||||
|
||||
|
||||
val block = Block(
|
||||
actor = user.url,
|
||||
"https://example.com/block",
|
||||
apObject = "https://remote.example.com"
|
||||
)
|
||||
val reject = Reject(
|
||||
actor = user.url,
|
||||
"https://example.com/reject/follow",
|
||||
apObject = Follow(
|
||||
apObject = user.url,
|
||||
actor = "https://remote.example.com"
|
||||
)
|
||||
)
|
||||
val param = DeliverBlockJobParam(
|
||||
user.id,
|
||||
block,
|
||||
reject,
|
||||
"https://remote.example.com"
|
||||
)
|
||||
|
||||
|
||||
apDeliverBlockJobProcessor.process(param)
|
||||
|
||||
verify(apRequestService, times(1)).apPost(eq("https://remote.example.com"), eq(block), eq(user))
|
||||
verify(apRequestService, times(1)).apPost(eq("https://remote.example.com"), eq(reject), eq(user))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `job deliverBlockJobが返ってくる`() {
|
||||
val actual = apDeliverBlockJobProcessor.job()
|
||||
assertThat(actual).isEqualTo(deliverBlockJob)
|
||||
}
|
||||
}
|
|
@ -10,7 +10,6 @@ class ContextSerializerTest {
|
|||
@Test
|
||||
fun serialize() {
|
||||
val accept = Accept(
|
||||
name = "aaa",
|
||||
actor = "bbb",
|
||||
apObject = Follow(
|
||||
apObject = "ddd",
|
||||
|
|
|
@ -0,0 +1,786 @@
|
|||
package dev.usbharu.hideout.core.service.relationship
|
||||
|
||||
import dev.usbharu.hideout.activitypub.service.activity.accept.ApSendAcceptService
|
||||
import dev.usbharu.hideout.activitypub.service.activity.block.APSendBlockService
|
||||
import dev.usbharu.hideout.activitypub.service.activity.follow.APSendFollowService
|
||||
import dev.usbharu.hideout.activitypub.service.activity.reject.ApSendRejectService
|
||||
import dev.usbharu.hideout.activitypub.service.activity.undo.APSendUndoService
|
||||
import dev.usbharu.hideout.application.config.ApplicationConfig
|
||||
import dev.usbharu.hideout.core.domain.model.relationship.Relationship
|
||||
import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository
|
||||
import dev.usbharu.hideout.core.query.UserQueryService
|
||||
import dev.usbharu.hideout.core.service.follow.SendFollowDto
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.assertThrows
|
||||
import org.junit.jupiter.api.extension.ExtendWith
|
||||
import org.mockito.InjectMocks
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Spy
|
||||
import org.mockito.junit.jupiter.MockitoExtension
|
||||
import org.mockito.kotlin.*
|
||||
import utils.UserBuilder
|
||||
import java.net.URL
|
||||
|
||||
@ExtendWith(MockitoExtension::class)
|
||||
class RelationshipServiceImplTest {
|
||||
|
||||
@Spy
|
||||
private val applicationConfig = ApplicationConfig(URL("https://example.com"))
|
||||
|
||||
@Mock
|
||||
private lateinit var userQueryService: UserQueryService
|
||||
|
||||
@Mock
|
||||
private lateinit var relationshipRepository: RelationshipRepository
|
||||
|
||||
@Mock
|
||||
private lateinit var apSendFollowService: APSendFollowService
|
||||
|
||||
@Mock
|
||||
private lateinit var apSendBlockService: APSendBlockService
|
||||
|
||||
@Mock
|
||||
private lateinit var apSendAcceptService: ApSendAcceptService
|
||||
|
||||
@Mock
|
||||
private lateinit var apSendRejectService: ApSendRejectService
|
||||
|
||||
@Mock
|
||||
private lateinit var apSendUndoService: APSendUndoService
|
||||
|
||||
@InjectMocks
|
||||
private lateinit var relationshipServiceImpl: RelationshipServiceImpl
|
||||
|
||||
@Test
|
||||
fun `followRequest ローカルの場合followRequestフラグがtrueで永続化される`() = runTest {
|
||||
whenever(userQueryService.findById(eq(5678))).doReturn(UserBuilder.localUserOf(domain = "example.com"))
|
||||
|
||||
relationshipServiceImpl.followRequest(1234, 5678)
|
||||
|
||||
verify(relationshipRepository, times(1)).save(
|
||||
eq(
|
||||
Relationship(
|
||||
userId = 1234,
|
||||
targetUserId = 5678,
|
||||
following = false,
|
||||
blocking = false,
|
||||
muting = false,
|
||||
followRequest = true,
|
||||
ignoreFollowRequestFromTarget = false
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `followRequest リモートの場合Followアクティビティが配送される`() = runTest {
|
||||
val localUser = UserBuilder.localUserOf(domain = "example.com")
|
||||
whenever(userQueryService.findById(eq(1234))).doReturn(localUser)
|
||||
val remoteUser = UserBuilder.remoteUserOf(domain = "remote.example.com")
|
||||
whenever(userQueryService.findById(eq(5678))).doReturn(remoteUser)
|
||||
|
||||
relationshipServiceImpl.followRequest(1234, 5678)
|
||||
|
||||
verify(relationshipRepository, times(1)).save(
|
||||
eq(
|
||||
Relationship(
|
||||
userId = 1234,
|
||||
targetUserId = 5678,
|
||||
following = false,
|
||||
blocking = false,
|
||||
muting = false,
|
||||
followRequest = true,
|
||||
ignoreFollowRequestFromTarget = false
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
verify(apSendFollowService, times(1)).sendFollow(eq(SendFollowDto(localUser, remoteUser)))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `followRequest ブロックされている場合フォローリクエスト出来ない`() = runTest {
|
||||
whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn(null)
|
||||
whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(5678), eq(1234))).doReturn(
|
||||
Relationship(
|
||||
userId = 5678,
|
||||
targetUserId = 1234,
|
||||
following = false,
|
||||
blocking = true,
|
||||
muting = false,
|
||||
followRequest = false,
|
||||
ignoreFollowRequestFromTarget = false
|
||||
)
|
||||
)
|
||||
|
||||
relationshipServiceImpl.followRequest(1234, 5678)
|
||||
|
||||
verify(relationshipRepository, never()).save(any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `followRequest ブロックしている場合フォローリクエスト出来ない`() = runTest {
|
||||
whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn(
|
||||
Relationship(
|
||||
userId = 1234,
|
||||
targetUserId = 5678,
|
||||
following = false,
|
||||
blocking = true,
|
||||
muting = false,
|
||||
followRequest = false,
|
||||
ignoreFollowRequestFromTarget = false
|
||||
)
|
||||
)
|
||||
|
||||
relationshipServiceImpl.followRequest(1234, 5678)
|
||||
|
||||
verify(relationshipRepository, never()).save(any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `followRequest 既にフォローしている場合は念の為フォロー承認を自動で行う`() = runTest {
|
||||
val remoteUser = UserBuilder.remoteUserOf(domain = "remote.example.com")
|
||||
whenever(userQueryService.findById(eq(1234))).doReturn(remoteUser)
|
||||
val localUser = UserBuilder.localUserOf(domain = "example.com")
|
||||
whenever(userQueryService.findById(eq(5678))).doReturn(localUser)
|
||||
whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn(
|
||||
Relationship(
|
||||
userId = 1234,
|
||||
targetUserId = 5678,
|
||||
following = true,
|
||||
blocking = false,
|
||||
muting = false,
|
||||
followRequest = false,
|
||||
ignoreFollowRequestFromTarget = false
|
||||
)
|
||||
)
|
||||
|
||||
relationshipServiceImpl.followRequest(1234, 5678)
|
||||
|
||||
verify(relationshipRepository, times(1)).save(
|
||||
eq(
|
||||
Relationship(
|
||||
userId = 1234,
|
||||
targetUserId = 5678,
|
||||
following = true,
|
||||
blocking = false,
|
||||
muting = false,
|
||||
followRequest = false,
|
||||
ignoreFollowRequestFromTarget = false
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
verify(apSendAcceptService, times(1)).sendAcceptFollow(eq(localUser), eq(remoteUser))
|
||||
verify(apSendFollowService, never()).sendFollow(any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `followRequest フォローリクエスト無視の場合は無視する`() = runTest {
|
||||
whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn(
|
||||
Relationship(
|
||||
userId = 1234,
|
||||
targetUserId = 5678,
|
||||
following = false,
|
||||
blocking = false,
|
||||
muting = false,
|
||||
followRequest = false,
|
||||
ignoreFollowRequestFromTarget = false
|
||||
)
|
||||
)
|
||||
|
||||
whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(5678), eq(1234))).doReturn(
|
||||
Relationship(
|
||||
userId = 5678,
|
||||
targetUserId = 1234,
|
||||
following = false,
|
||||
blocking = false,
|
||||
muting = false,
|
||||
followRequest = false,
|
||||
ignoreFollowRequestFromTarget = true
|
||||
)
|
||||
)
|
||||
|
||||
relationshipServiceImpl.followRequest(1234, 5678)
|
||||
|
||||
verify(relationshipRepository, never()).save(any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `block ローカルユーザーの場合永続化される`() = runTest {
|
||||
whenever(userQueryService.findById(eq(5678))).doReturn(UserBuilder.localUserOf(domain = "example.com"))
|
||||
|
||||
relationshipServiceImpl.block(1234, 5678)
|
||||
|
||||
verify(relationshipRepository, times(1)).save(
|
||||
eq(
|
||||
Relationship(
|
||||
userId = 1234,
|
||||
targetUserId = 5678,
|
||||
following = false,
|
||||
blocking = true,
|
||||
muting = false,
|
||||
followRequest = false,
|
||||
ignoreFollowRequestFromTarget = false
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `block リモートユーザーの場合永続化されて配送される`() = runTest {
|
||||
val localUser = UserBuilder.localUserOf(domain = "example.com")
|
||||
whenever(userQueryService.findById(eq(1234))).doReturn(localUser)
|
||||
val remoteUser = UserBuilder.remoteUserOf(domain = "remote.example.com")
|
||||
whenever(userQueryService.findById(eq(5678))).doReturn(remoteUser)
|
||||
|
||||
relationshipServiceImpl.block(1234, 5678)
|
||||
|
||||
verify(relationshipRepository, times(1)).save(
|
||||
eq(
|
||||
Relationship(
|
||||
userId = 1234,
|
||||
targetUserId = 5678,
|
||||
following = false,
|
||||
blocking = true,
|
||||
muting = false,
|
||||
followRequest = false,
|
||||
ignoreFollowRequestFromTarget = false
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
verify(apSendBlockService, times(1)).sendBlock(eq(localUser), eq(remoteUser))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `acceptFollowRequest ローカルユーザーの場合永続化される`() = runTest {
|
||||
whenever(userQueryService.findById(eq(5678))).doReturn(UserBuilder.localUserOf(domain = "example.com"))
|
||||
|
||||
whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(5678), eq(1234))).doReturn(
|
||||
Relationship(
|
||||
userId = 5678,
|
||||
targetUserId = 1234,
|
||||
following = false,
|
||||
blocking = false,
|
||||
muting = false,
|
||||
followRequest = true,
|
||||
ignoreFollowRequestFromTarget = false
|
||||
)
|
||||
)
|
||||
|
||||
relationshipServiceImpl.acceptFollowRequest(1234, 5678, false)
|
||||
|
||||
verify(relationshipRepository, times(1)).save(
|
||||
Relationship(
|
||||
userId = 5678,
|
||||
targetUserId = 1234,
|
||||
following = true,
|
||||
blocking = false,
|
||||
muting = false,
|
||||
followRequest = false,
|
||||
ignoreFollowRequestFromTarget = false
|
||||
)
|
||||
)
|
||||
|
||||
verify(apSendAcceptService, never()).sendAcceptFollow(any(), any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `acceptFollowRequest リモートユーザーの場合永続化されて配送される`() = runTest {
|
||||
val localUser = UserBuilder.localUserOf(domain = "example.com")
|
||||
whenever(userQueryService.findById(eq(1234))).doReturn(localUser)
|
||||
val remoteUser = UserBuilder.remoteUserOf(domain = "remote.example.com")
|
||||
whenever(userQueryService.findById(eq(5678))).doReturn(remoteUser)
|
||||
|
||||
whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(5678), eq(1234))).doReturn(
|
||||
Relationship(
|
||||
userId = 5678,
|
||||
targetUserId = 1234,
|
||||
following = false,
|
||||
blocking = false,
|
||||
muting = false,
|
||||
followRequest = true,
|
||||
ignoreFollowRequestFromTarget = false
|
||||
)
|
||||
)
|
||||
|
||||
relationshipServiceImpl.acceptFollowRequest(1234, 5678, false)
|
||||
|
||||
verify(relationshipRepository, times(1)).save(
|
||||
eq(
|
||||
Relationship(
|
||||
userId = 5678,
|
||||
targetUserId = 1234,
|
||||
following = true,
|
||||
blocking = false,
|
||||
muting = false,
|
||||
followRequest = false,
|
||||
ignoreFollowRequestFromTarget = false
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
verify(apSendAcceptService, times(1)).sendAcceptFollow(eq(localUser), eq(remoteUser))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `acceptFollowRequest Relationshipが存在しないときは何もしない`() = runTest {
|
||||
relationshipServiceImpl.acceptFollowRequest(1234, 5678, false)
|
||||
|
||||
verify(apSendAcceptService, never()).sendAcceptFollow(any(), any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `acceptFollowRequest フォローリクエストが存在せずforceがfalseのとき何もしない`() = runTest {
|
||||
whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(5678), eq(1234))).doReturn(
|
||||
Relationship(
|
||||
5678, 1234, false, false, false, false, false
|
||||
)
|
||||
)
|
||||
|
||||
relationshipServiceImpl.acceptFollowRequest(1234, 5678, false)
|
||||
|
||||
verify(apSendAcceptService, never()).sendAcceptFollow(any(), any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `acceptFollowRequest フォローリクエストが存在せずforceがtrueのときフォローを承認する`() = runTest {
|
||||
whenever(userQueryService.findById(eq(5678))).doReturn(UserBuilder.remoteUserOf(domain = "remote.example.com"))
|
||||
|
||||
whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(5678), eq(1234))).doReturn(
|
||||
Relationship(
|
||||
5678, 1234, false, false, false, false, false
|
||||
)
|
||||
)
|
||||
|
||||
relationshipServiceImpl.acceptFollowRequest(1234, 5678, true)
|
||||
|
||||
verify(relationshipRepository, times(1)).save(
|
||||
eq(
|
||||
Relationship(
|
||||
userId = 5678,
|
||||
targetUserId = 1234,
|
||||
following = true,
|
||||
blocking = false,
|
||||
muting = false,
|
||||
followRequest = false,
|
||||
ignoreFollowRequestFromTarget = false
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `acceptFollowRequest ブロックしている場合は何もしない`() = runTest {
|
||||
whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(5678), eq(1234))).doReturn(
|
||||
Relationship(
|
||||
5678, 1234, false, true, false, true, false
|
||||
)
|
||||
)
|
||||
|
||||
assertThrows<IllegalStateException> {
|
||||
relationshipServiceImpl.acceptFollowRequest(1234, 5678, false)
|
||||
}
|
||||
|
||||
verify(relationshipRepository, never()).save(any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `acceptFollowRequest ブロックされている場合は何もしない`() = runTest {
|
||||
whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn(
|
||||
Relationship(
|
||||
1234, 5678, false, false, false, true, false
|
||||
)
|
||||
)
|
||||
|
||||
whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(5678), eq(1234))).doReturn(
|
||||
Relationship(
|
||||
5678, 1234, false, true, false, true, false
|
||||
)
|
||||
)
|
||||
|
||||
assertThrows<IllegalStateException> {
|
||||
relationshipServiceImpl.acceptFollowRequest(1234, 5678, false)
|
||||
}
|
||||
|
||||
verify(relationshipRepository, never()).save(any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `rejectFollowRequest ローカルユーザーの場合永続化される`() = runTest {
|
||||
whenever(userQueryService.findById(eq(5678))).doReturn(UserBuilder.localUserOf(domain = "example.com"))
|
||||
|
||||
whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(5678), eq(1234))).doReturn(
|
||||
Relationship(
|
||||
userId = 5678,
|
||||
targetUserId = 1234,
|
||||
following = false,
|
||||
blocking = false,
|
||||
muting = false,
|
||||
followRequest = true,
|
||||
ignoreFollowRequestFromTarget = false
|
||||
)
|
||||
)
|
||||
|
||||
relationshipServiceImpl.rejectFollowRequest(1234, 5678)
|
||||
|
||||
verify(relationshipRepository, times(1)).save(
|
||||
eq(
|
||||
Relationship(
|
||||
userId = 5678,
|
||||
targetUserId = 1234,
|
||||
following = false,
|
||||
blocking = false,
|
||||
muting = false,
|
||||
followRequest = false,
|
||||
ignoreFollowRequestFromTarget = false
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
verify(apSendRejectService, never()).sendRejectFollow(any(), any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `rejectFollowRequest リモートユーザーの場合永続化されて配送される`() = runTest {
|
||||
val localUser = UserBuilder.localUserOf(domain = "example.com")
|
||||
whenever(userQueryService.findById(eq(1234))).doReturn(localUser)
|
||||
|
||||
val remoteUser = UserBuilder.remoteUserOf(domain = "remote.example.com")
|
||||
whenever(userQueryService.findById(eq(5678))).doReturn(remoteUser)
|
||||
|
||||
whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(5678), eq(1234))).doReturn(
|
||||
Relationship(
|
||||
userId = 5678,
|
||||
targetUserId = 1234,
|
||||
following = false,
|
||||
blocking = false,
|
||||
muting = false,
|
||||
followRequest = true,
|
||||
ignoreFollowRequestFromTarget = false
|
||||
)
|
||||
)
|
||||
|
||||
relationshipServiceImpl.rejectFollowRequest(1234, 5678)
|
||||
|
||||
verify(relationshipRepository, times(1)).save(
|
||||
eq(
|
||||
Relationship(
|
||||
userId = 5678,
|
||||
targetUserId = 1234,
|
||||
following = false,
|
||||
blocking = false,
|
||||
muting = false,
|
||||
followRequest = false,
|
||||
ignoreFollowRequestFromTarget = false
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
verify(apSendRejectService, times(1)).sendRejectFollow(eq(localUser), eq(remoteUser))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `rejectFollowRequest Relationshipが存在しないとき何もしない`() = runTest {
|
||||
|
||||
relationshipServiceImpl.rejectFollowRequest(1234, 5678)
|
||||
|
||||
verify(relationshipRepository, never()).save(any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `rejectFollowRequest フォローリクエストが存在しない場合何もしない`() = runTest {
|
||||
whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(5678), eq(1234))).doReturn(
|
||||
Relationship(
|
||||
userId = 5678,
|
||||
targetUserId = 1234,
|
||||
following = false,
|
||||
blocking = false,
|
||||
muting = false,
|
||||
followRequest = false,
|
||||
ignoreFollowRequestFromTarget = false
|
||||
)
|
||||
)
|
||||
|
||||
relationshipServiceImpl.rejectFollowRequest(1234, 5678)
|
||||
|
||||
verify(relationshipRepository, never()).save(any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ignoreFollowRequest 永続化される`() = runTest {
|
||||
relationshipServiceImpl.ignoreFollowRequest(1234, 5678)
|
||||
|
||||
verify(relationshipRepository, times(1)).save(
|
||||
eq(
|
||||
Relationship(
|
||||
userId = 1234,
|
||||
targetUserId = 5678,
|
||||
following = false,
|
||||
blocking = false,
|
||||
muting = false,
|
||||
followRequest = false,
|
||||
ignoreFollowRequestFromTarget = true
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `unfollow ローカルユーザーの場合永続化される`() = runTest {
|
||||
whenever(userQueryService.findById(eq(5678))).doReturn(UserBuilder.localUserOf(domain = "example.com"))
|
||||
whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn(
|
||||
Relationship(
|
||||
userId = 1234,
|
||||
targetUserId = 5678,
|
||||
following = true,
|
||||
blocking = false,
|
||||
muting = false,
|
||||
followRequest = false,
|
||||
ignoreFollowRequestFromTarget = false
|
||||
)
|
||||
)
|
||||
|
||||
relationshipServiceImpl.unfollow(1234, 5678)
|
||||
|
||||
verify(relationshipRepository, times(1)).save(
|
||||
eq(
|
||||
Relationship(
|
||||
userId = 1234,
|
||||
targetUserId = 5678,
|
||||
following = false,
|
||||
blocking = false,
|
||||
muting = false,
|
||||
followRequest = false,
|
||||
ignoreFollowRequestFromTarget = false
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
verify(apSendUndoService, never()).sendUndoFollow(any(), any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `unfollow リモートユーザー場合永続化されて配送される`() = runTest {
|
||||
val localUser = UserBuilder.localUserOf(domain = "example.com")
|
||||
whenever(userQueryService.findById(eq(1234))).doReturn(localUser)
|
||||
|
||||
val remoteUser = UserBuilder.remoteUserOf(domain = "remote.example.com")
|
||||
whenever(userQueryService.findById(eq(5678))).doReturn(remoteUser)
|
||||
|
||||
whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn(
|
||||
Relationship(
|
||||
userId = 1234,
|
||||
targetUserId = 5678,
|
||||
following = true,
|
||||
blocking = false,
|
||||
muting = false,
|
||||
followRequest = false,
|
||||
ignoreFollowRequestFromTarget = false
|
||||
)
|
||||
)
|
||||
|
||||
relationshipServiceImpl.unfollow(1234, 5678)
|
||||
|
||||
verify(relationshipRepository, times(1)).save(
|
||||
eq(
|
||||
Relationship(
|
||||
userId = 1234,
|
||||
targetUserId = 5678,
|
||||
following = false,
|
||||
blocking = false,
|
||||
muting = false,
|
||||
followRequest = false,
|
||||
ignoreFollowRequestFromTarget = false
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
verify(apSendUndoService, times(1)).sendUndoFollow(eq(localUser), eq(remoteUser))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `unfollow Relationshipが存在しないときは何もしない`() = runTest {
|
||||
relationshipServiceImpl.unfollow(1234, 5678)
|
||||
|
||||
verify(relationshipRepository, never()).save(any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `unfollow フォローしていなかった場合は何もしない`() = runTest {
|
||||
whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn(
|
||||
Relationship(
|
||||
userId = 1234,
|
||||
targetUserId = 5678,
|
||||
following = false,
|
||||
blocking = false,
|
||||
muting = false,
|
||||
followRequest = false,
|
||||
ignoreFollowRequestFromTarget = false
|
||||
)
|
||||
)
|
||||
|
||||
relationshipServiceImpl.unfollow(1234, 5678)
|
||||
|
||||
verify(relationshipRepository, never()).save(any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `unblock ローカルユーザーの場合永続化される`() = runTest {
|
||||
whenever(userQueryService.findById(eq(5678))).doReturn(UserBuilder.localUserOf(domain = "example.com"))
|
||||
whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn(
|
||||
Relationship(
|
||||
userId = 1234,
|
||||
targetUserId = 5678,
|
||||
following = false,
|
||||
blocking = true,
|
||||
muting = false,
|
||||
followRequest = false,
|
||||
ignoreFollowRequestFromTarget = false
|
||||
)
|
||||
)
|
||||
|
||||
relationshipServiceImpl.unblock(1234, 5678)
|
||||
|
||||
verify(relationshipRepository, times(1)).save(
|
||||
eq(
|
||||
Relationship(
|
||||
userId = 1234,
|
||||
targetUserId = 5678,
|
||||
following = false,
|
||||
blocking = false,
|
||||
muting = false,
|
||||
followRequest = false,
|
||||
ignoreFollowRequestFromTarget = false
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
verify(apSendUndoService, never()).sendUndoBlock(any(), any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `unblock リモートユーザーの場合永続化されて配送される`() = runTest {
|
||||
val localUser = UserBuilder.localUserOf(domain = "example.com")
|
||||
whenever(userQueryService.findById(eq(1234))).doReturn(localUser)
|
||||
|
||||
val remoteUser = UserBuilder.remoteUserOf(domain = "remote.example.com")
|
||||
whenever(userQueryService.findById(eq(5678))).doReturn(remoteUser)
|
||||
|
||||
whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn(
|
||||
Relationship(
|
||||
userId = 1234,
|
||||
targetUserId = 5678,
|
||||
following = false,
|
||||
blocking = true,
|
||||
muting = false,
|
||||
followRequest = false,
|
||||
ignoreFollowRequestFromTarget = false
|
||||
)
|
||||
)
|
||||
|
||||
relationshipServiceImpl.unblock(1234, 5678)
|
||||
|
||||
verify(relationshipRepository, times(1)).save(
|
||||
eq(
|
||||
Relationship(
|
||||
1234,
|
||||
5678,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
verify(apSendUndoService, times(1)).sendUndoBlock(eq(localUser), eq(remoteUser))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `unblock Relationshipがない場合何もしない`() = runTest {
|
||||
relationshipServiceImpl.unblock(1234, 5678)
|
||||
|
||||
verify(relationshipRepository, never()).save(any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `unblock ブロックしていない場合は何もしない`() = runTest {
|
||||
whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn(
|
||||
Relationship(
|
||||
userId = 1234,
|
||||
targetUserId = 5678,
|
||||
following = false,
|
||||
blocking = false,
|
||||
muting = false,
|
||||
followRequest = false,
|
||||
ignoreFollowRequestFromTarget = false
|
||||
)
|
||||
)
|
||||
|
||||
relationshipServiceImpl.unblock(1234, 5678)
|
||||
|
||||
verify(relationshipRepository, never()).save(any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `mute ミュートが永続化される`() = runTest {
|
||||
relationshipServiceImpl.mute(1234, 5678)
|
||||
|
||||
verify(relationshipRepository, times(1)).save(
|
||||
eq(
|
||||
Relationship(
|
||||
userId = 1234,
|
||||
targetUserId = 5678,
|
||||
following = false,
|
||||
blocking = false,
|
||||
muting = true,
|
||||
followRequest = false,
|
||||
ignoreFollowRequestFromTarget = false
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `unmute 永続化される`() = runTest {
|
||||
|
||||
whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn(
|
||||
Relationship(
|
||||
userId = 1234,
|
||||
targetUserId = 5678,
|
||||
following = false,
|
||||
blocking = false,
|
||||
muting = true,
|
||||
followRequest = false,
|
||||
ignoreFollowRequestFromTarget = false
|
||||
)
|
||||
)
|
||||
|
||||
relationshipServiceImpl.unmute(1234, 5678)
|
||||
|
||||
verify(relationshipRepository, times(1)).save(
|
||||
eq(
|
||||
Relationship(
|
||||
userId = 1234,
|
||||
targetUserId = 5678,
|
||||
following = false,
|
||||
blocking = false,
|
||||
muting = false,
|
||||
followRequest = false,
|
||||
ignoreFollowRequestFromTarget = false
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `unmute Relationshipが存在しない場合は何もしない`() = runTest {
|
||||
relationshipServiceImpl.unmute(1234, 5678)
|
||||
|
||||
verify(relationshipRepository, never()).save(any())
|
||||
}
|
||||
}
|
|
@ -37,8 +37,6 @@ class UserServiceTest {
|
|||
userRepository,
|
||||
userAuthService,
|
||||
mock(),
|
||||
mock(),
|
||||
mock(),
|
||||
userBuilder,
|
||||
testApplicationConfig,
|
||||
mock()
|
||||
|
@ -68,7 +66,7 @@ class UserServiceTest {
|
|||
onBlocking { nextId() } doReturn 113345L
|
||||
}
|
||||
val userService =
|
||||
UserServiceImpl(userRepository, mock(), mock(), mock(), mock(), userBuilder, testApplicationConfig, mock())
|
||||
UserServiceImpl(userRepository, mock(), mock(), userBuilder, testApplicationConfig, mock())
|
||||
val user = RemoteUserCreateDto(
|
||||
name = "test",
|
||||
domain = "remote.example.com",
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
package dev.usbharu.hideout.mastodon.service.account
|
||||
|
||||
import dev.usbharu.hideout.application.external.Transaction
|
||||
import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository
|
||||
import dev.usbharu.hideout.core.domain.model.user.UserRepository
|
||||
import dev.usbharu.hideout.core.query.FollowerQueryService
|
||||
import dev.usbharu.hideout.core.service.relationship.RelationshipService
|
||||
import dev.usbharu.hideout.core.service.user.UserService
|
||||
import dev.usbharu.hideout.domain.mastodon.model.generated.Account
|
||||
import dev.usbharu.hideout.domain.mastodon.model.generated.Relationship
|
||||
|
@ -40,6 +42,12 @@ class AccountApiServiceImplTest {
|
|||
@Spy
|
||||
private val transaction: Transaction = TestTransaction
|
||||
|
||||
@Mock
|
||||
private lateinit var relationshipService: RelationshipService
|
||||
|
||||
@Mock
|
||||
private lateinit var relationshipRepository: RelationshipRepository
|
||||
|
||||
@InjectMocks
|
||||
private lateinit var accountApiServiceImpl: AccountApiServiceImpl
|
||||
|
||||
|
@ -157,9 +165,6 @@ class AccountApiServiceImplTest {
|
|||
)
|
||||
).doReturn(statusList)
|
||||
|
||||
whenever(followerQueryService.alreadyFollow(eq(userId), eq(loginUser))).doReturn(false)
|
||||
|
||||
|
||||
val accountsStatuses = accountApiServiceImpl.accountsStatuses(
|
||||
userid = userId,
|
||||
maxId = null,
|
||||
|
@ -197,7 +202,17 @@ class AccountApiServiceImplTest {
|
|||
)
|
||||
).doReturn(statusList)
|
||||
|
||||
whenever(followerQueryService.alreadyFollow(eq(userId), eq(loginUser))).doReturn(true)
|
||||
whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(loginUser), eq(userId))).doReturn(
|
||||
dev.usbharu.hideout.core.domain.model.relationship.Relationship(
|
||||
userId = loginUser,
|
||||
targetUserId = userId,
|
||||
following = true,
|
||||
blocking = false,
|
||||
muting = false,
|
||||
followRequest = false,
|
||||
ignoreFollowRequestFromTarget = false
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
val accountsStatuses = accountApiServiceImpl.accountsStatuses(
|
||||
|
@ -217,51 +232,34 @@ class AccountApiServiceImplTest {
|
|||
assertThat(accountsStatuses).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `follow 既にフォローしている場合は何もしない`() = runTest {
|
||||
val userId = 1234L
|
||||
val followeeId = 1L
|
||||
|
||||
whenever(followerQueryService.alreadyFollow(eq(followeeId), eq(userId))).doReturn(true)
|
||||
|
||||
whenever(followerQueryService.alreadyFollow(eq(userId), eq(followeeId))).doReturn(true)
|
||||
|
||||
whenever(userRepository.findFollowRequestsById(eq(followeeId), eq(userId))).doReturn(false)
|
||||
|
||||
val follow = accountApiServiceImpl.follow(userId, followeeId)
|
||||
|
||||
val expected = Relationship(
|
||||
id = followeeId.toString(),
|
||||
following = true,
|
||||
showingReblogs = true,
|
||||
notifying = false,
|
||||
followedBy = true,
|
||||
blocking = false,
|
||||
blockedBy = false,
|
||||
muting = false,
|
||||
mutingNotifications = false,
|
||||
requested = false,
|
||||
domainBlocking = false,
|
||||
endorsed = false,
|
||||
note = ""
|
||||
)
|
||||
assertThat(follow).isEqualTo(expected)
|
||||
|
||||
verify(userService, never()).followRequest(any(), any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `follow 未フォローの場合フォローリクエストが発生する`() = runTest {
|
||||
val userId = 1234L
|
||||
val followeeId = 1L
|
||||
|
||||
whenever(followerQueryService.alreadyFollow(eq(followeeId), eq(userId))).doReturn(false)
|
||||
whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(followeeId), eq(userId))).doReturn(
|
||||
dev.usbharu.hideout.core.domain.model.relationship.Relationship(
|
||||
userId = followeeId,
|
||||
targetUserId = userId,
|
||||
following = true,
|
||||
blocking = false,
|
||||
muting = false,
|
||||
followRequest = false,
|
||||
ignoreFollowRequestFromTarget = false
|
||||
)
|
||||
)
|
||||
whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(userId), eq(followeeId))).doReturn(
|
||||
dev.usbharu.hideout.core.domain.model.relationship.Relationship(
|
||||
userId = userId,
|
||||
targetUserId = followeeId,
|
||||
following = true,
|
||||
blocking = false,
|
||||
muting = false,
|
||||
followRequest = false,
|
||||
ignoreFollowRequestFromTarget = false
|
||||
)
|
||||
)
|
||||
|
||||
whenever(userService.followRequest(eq(followeeId), eq(userId))).doReturn(true)
|
||||
|
||||
whenever(followerQueryService.alreadyFollow(eq(userId), eq(followeeId))).doReturn(true)
|
||||
|
||||
whenever(userRepository.findFollowRequestsById(eq(followeeId), eq(userId))).doReturn(false)
|
||||
|
||||
val follow = accountApiServiceImpl.follow(userId, followeeId)
|
||||
|
||||
|
@ -282,14 +280,11 @@ class AccountApiServiceImplTest {
|
|||
)
|
||||
assertThat(follow).isEqualTo(expected)
|
||||
|
||||
verify(userService, times(1)).followRequest(eq(followeeId), eq(userId))
|
||||
verify(relationshipService, times(1)).followRequest(eq(userId), eq(followeeId))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `relationships idが長すぎたら省略する`() = runTest {
|
||||
whenever(followerQueryService.alreadyFollow(any(), any())).doReturn(true)
|
||||
|
||||
whenever(userRepository.findFollowRequestsById(any(), any())).doReturn(true)
|
||||
|
||||
val relationships = accountApiServiceImpl.relationships(
|
||||
userid = 1234L,
|
||||
|
@ -297,7 +292,7 @@ class AccountApiServiceImplTest {
|
|||
withSuspended = false
|
||||
)
|
||||
|
||||
assertThat(relationships).hasSizeLessThanOrEqualTo(20)
|
||||
assertThat(relationships).hasSize(20)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -310,14 +305,10 @@ class AccountApiServiceImplTest {
|
|||
|
||||
assertThat(relationships).hasSize(0)
|
||||
verify(followerQueryService, never()).alreadyFollow(any(), any())
|
||||
verify(userRepository, never()).findFollowRequestsById(any(), any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `relationships idに指定されたアカウントの関係を取得する`() = runTest {
|
||||
whenever(followerQueryService.alreadyFollow(any(), any())).doReturn(true)
|
||||
|
||||
whenever(userRepository.findFollowRequestsById(any(), any())).doReturn(true)
|
||||
|
||||
val relationships = accountApiServiceImpl.relationships(
|
||||
userid = 1234L,
|
||||
|
|
Loading…
Reference in New Issue