mirror of https://github.com/usbharu/Hideout.git
feat: フォローリクエストに対応。フォローした後無限ループする問題を修正
This commit is contained in:
parent
c1f7f838ca
commit
5b6e0a3657
|
@ -66,7 +66,7 @@ fun Application.parent() {
|
|||
HttpClient(CIO).config {
|
||||
install(Logging) {
|
||||
logger = Logger.DEFAULT
|
||||
level = LogLevel.ALL
|
||||
level = LogLevel.INFO
|
||||
}
|
||||
install(httpSignaturePlugin) {
|
||||
keyMap = KtorKeyMap(get())
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
package dev.usbharu.hideout.domain.model.hideout.entity
|
||||
|
||||
data class FollowRequest(val userId: Long, val followerId: Long)
|
|
@ -35,5 +35,9 @@ interface IUserRepository {
|
|||
suspend fun deleteFollower(id: Long, follower: Long)
|
||||
suspend fun findFollowersById(id: Long): List<User>
|
||||
|
||||
suspend fun addFollowRequest(id: Long, follower: Long)
|
||||
suspend fun deleteFollowRequest(id: Long, follower: Long)
|
||||
suspend fun findFollowRequestsById(id: Long, follower: Long): Boolean
|
||||
|
||||
suspend fun nextId(): Long
|
||||
}
|
||||
|
|
|
@ -20,6 +20,8 @@ class UserRepository(private val database: Database, private val idGenerateServi
|
|||
SchemaUtils.create(UsersFollowers)
|
||||
SchemaUtils.createMissingTablesAndColumns(Users)
|
||||
SchemaUtils.createMissingTablesAndColumns(UsersFollowers)
|
||||
SchemaUtils.create(FollowRequests)
|
||||
SchemaUtils.createMissingTablesAndColumns(FollowRequests)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -180,6 +182,28 @@ class UserRepository(private val database: Database, private val idGenerateServi
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun addFollowRequest(id: Long, follower: Long) {
|
||||
query {
|
||||
FollowRequests.insert {
|
||||
it[userId] = id
|
||||
it[followerId] = follower
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun deleteFollowRequest(id: Long, follower: Long) {
|
||||
query {
|
||||
FollowRequests.deleteWhere { userId.eq(id) and followerId.eq(follower) }
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun findFollowRequestsById(id: Long, follower: Long): Boolean {
|
||||
return query {
|
||||
FollowRequests.select { (FollowRequests.userId eq id) and (FollowRequests.followerId eq follower) }
|
||||
.singleOrNull() != null
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun delete(id: Long) {
|
||||
query {
|
||||
Users.deleteWhere { Users.id.eq(id) }
|
||||
|
@ -253,3 +277,12 @@ object UsersFollowers : LongIdTable("users_followers") {
|
|||
uniqueIndex(userId, followerId)
|
||||
}
|
||||
}
|
||||
|
||||
object FollowRequests : LongIdTable("follow_requests") {
|
||||
val userId = long("user_id").references(Users.id)
|
||||
val followerId = long("follower_id").references(Users.id)
|
||||
|
||||
init {
|
||||
uniqueIndex(userId, followerId)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,7 +71,7 @@ fun Route.users(userService: IUserService, userApiService: IUserApiService) {
|
|||
val userParameter = call.parameters["name"]
|
||||
?: throw ParameterNotExistException("Parameter(name='userName@domain') does not exist.")
|
||||
if (userParameter.toLongOrNull() != null) {
|
||||
if (userService.follow(userParameter.toLong(), userId)) {
|
||||
if (userService.followRequest(userParameter.toLong(), userId)) {
|
||||
return@post call.respond(HttpStatusCode.OK)
|
||||
} else {
|
||||
return@post call.respond(HttpStatusCode.Accepted)
|
||||
|
@ -79,7 +79,7 @@ fun Route.users(userService: IUserService, userApiService: IUserApiService) {
|
|||
}
|
||||
val acct = AcctUtil.parse(userParameter)
|
||||
val targetUser = userApiService.findByAcct(acct)
|
||||
if (userService.follow(targetUser.id, userId)) {
|
||||
if (userService.followRequest(targetUser.id, userId)) {
|
||||
return@post call.respond(HttpStatusCode.OK)
|
||||
} else {
|
||||
return@post call.respond(HttpStatusCode.Accepted)
|
||||
|
|
|
@ -49,6 +49,6 @@ class ActivityPubReceiveFollowServiceImpl(
|
|||
val users =
|
||||
userService.findByUrls(listOf(targetActor, follow.actor ?: throw IllegalArgumentException("actor is null")))
|
||||
|
||||
userService.follow(users.first { it.url == targetActor }.id, users.first { it.url == follow.actor }.id)
|
||||
userService.followRequest(users.first { it.url == targetActor }.id, users.first { it.url == follow.actor }.id)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ class ActivityPubServiceImpl(
|
|||
val logger: Logger = LoggerFactory.getLogger(this::class.java)
|
||||
override fun parseActivity(json: String): ActivityType {
|
||||
val readTree = configData.objectMapper.readTree(json)
|
||||
logger.debug("readTree: {}", readTree)
|
||||
logger.trace("readTree: {}", readTree)
|
||||
if (readTree.isObject.not()) {
|
||||
throw JsonParseException("Json is not object.")
|
||||
}
|
||||
|
@ -41,16 +41,9 @@ class ActivityPubServiceImpl(
|
|||
|
||||
@Suppress("CyclomaticComplexMethod", "NotImplementedDeclaration")
|
||||
override suspend fun processActivity(json: String, type: ActivityType): ActivityPubResponse {
|
||||
logger.debug("proccess activity: {}", type)
|
||||
return when (type) {
|
||||
ActivityType.Accept -> activityPubAcceptService.receiveAccept(configData.objectMapper.readValue(json))
|
||||
ActivityType.Add -> TODO()
|
||||
ActivityType.Announce -> TODO()
|
||||
ActivityType.Arrive -> TODO()
|
||||
ActivityType.Block -> TODO()
|
||||
ActivityType.Create -> TODO()
|
||||
ActivityType.Delete -> TODO()
|
||||
ActivityType.Dislike -> TODO()
|
||||
ActivityType.Flag -> TODO()
|
||||
ActivityType.Follow -> activityPubReceiveFollowService.receiveFollow(
|
||||
configData.objectMapper.readValue(
|
||||
json,
|
||||
|
@ -58,25 +51,11 @@ class ActivityPubServiceImpl(
|
|||
)
|
||||
)
|
||||
|
||||
ActivityType.Ignore -> TODO()
|
||||
ActivityType.Invite -> TODO()
|
||||
ActivityType.Join -> TODO()
|
||||
ActivityType.Leave -> TODO()
|
||||
ActivityType.Like -> TODO()
|
||||
ActivityType.Listen -> TODO()
|
||||
ActivityType.Move -> TODO()
|
||||
ActivityType.Offer -> TODO()
|
||||
ActivityType.Question -> TODO()
|
||||
ActivityType.Reject -> TODO()
|
||||
ActivityType.Read -> TODO()
|
||||
ActivityType.Remove -> TODO()
|
||||
ActivityType.TentativeReject -> TODO()
|
||||
ActivityType.TentativeAccept -> TODO()
|
||||
ActivityType.Travel -> TODO()
|
||||
ActivityType.Undo -> activityPubUndoService.receiveUndo(configData.objectMapper.readValue(json))
|
||||
ActivityType.Update -> TODO()
|
||||
ActivityType.View -> TODO()
|
||||
ActivityType.Other -> TODO()
|
||||
|
||||
else -> {
|
||||
throw IllegalArgumentException("$type is not supported.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -39,13 +39,21 @@ interface IUserService {
|
|||
suspend fun findFollowingByNameAndDomain(name: String, domain: String?): List<User>
|
||||
|
||||
/**
|
||||
* フォロワーを追加する
|
||||
* フォローリクエストを送信する
|
||||
*
|
||||
* @param id
|
||||
* @param follower
|
||||
* @param followerId
|
||||
* @return リクエストが成功したか
|
||||
*/
|
||||
suspend fun follow(id: Long, follower: Long): Boolean
|
||||
suspend fun followRequest(id: Long, followerId: Long): Boolean
|
||||
|
||||
suspend fun unfollow(id: Long, follower: Long): Boolean
|
||||
/**
|
||||
* フォローする
|
||||
*
|
||||
* @param id
|
||||
* @param followerId
|
||||
*/
|
||||
suspend fun follow(id: Long, followerId: Long)
|
||||
|
||||
suspend fun unfollow(id: Long, followerId: Long): Boolean
|
||||
}
|
||||
|
|
|
@ -111,23 +111,31 @@ class UserService(
|
|||
}
|
||||
|
||||
// TODO APのフォロー処理を作る
|
||||
override suspend fun follow(id: Long, followerId: Long): Boolean {
|
||||
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.")
|
||||
if (follower.domain != Config.configData.domain) {
|
||||
throw IllegalArgumentException("follower is not local user.")
|
||||
}
|
||||
return if (user.domain == Config.configData.domain) {
|
||||
userRepository.createFollower(id, followerId)
|
||||
follow(id, followerId)
|
||||
true
|
||||
} else {
|
||||
activityPubSendFollowService.sendFollow(SendFollowDto(follower, user))
|
||||
if (userRepository.findFollowRequestsById(id, followerId)) {
|
||||
// do-nothing
|
||||
} else {
|
||||
activityPubSendFollowService.sendFollow(SendFollowDto(follower, user))
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun unfollow(id: Long, follower: Long): Boolean {
|
||||
userRepository.deleteFollower(id, follower)
|
||||
override suspend fun follow(id: Long, followerId: Long) {
|
||||
userRepository.createFollower(id, followerId)
|
||||
if (userRepository.findFollowRequestsById(id, followerId)) {
|
||||
userRepository.deleteFollowRequest(id, followerId)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun unfollow(id: Long, followerId: Long): Boolean {
|
||||
userRepository.deleteFollower(id, followerId)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
<root level="trace">
|
||||
<root level="DEBUG">
|
||||
<appender-ref ref="STDOUT"/>
|
||||
</root>
|
||||
<logger name="org.eclipse.jetty" level="INFO"/>
|
||||
|
|
|
@ -432,7 +432,7 @@ class UsersTest {
|
|||
)
|
||||
}
|
||||
val userService = mock<IUserService> {
|
||||
onBlocking { follow(eq(1235), eq(1234)) } doReturn true
|
||||
onBlocking { followRequest(eq(1235), eq(1234)) } doReturn true
|
||||
}
|
||||
application {
|
||||
configureSerialization()
|
||||
|
@ -482,7 +482,7 @@ class UsersTest {
|
|||
)
|
||||
}
|
||||
val userService = mock<IUserService> {
|
||||
onBlocking { follow(eq(1235), eq(1234)) } doReturn false
|
||||
onBlocking { followRequest(eq(1235), eq(1234)) } doReturn false
|
||||
}
|
||||
application {
|
||||
configureSerialization()
|
||||
|
@ -532,7 +532,7 @@ class UsersTest {
|
|||
)
|
||||
}
|
||||
val userService = mock<IUserService> {
|
||||
onBlocking { follow(eq(1235), eq(1234)) } doReturn false
|
||||
onBlocking { followRequest(eq(1235), eq(1234)) } doReturn false
|
||||
}
|
||||
application {
|
||||
configureSerialization()
|
||||
|
|
|
@ -116,7 +116,7 @@ class ActivityPubReceiveFollowServiceImplTest {
|
|||
createdAt = Instant.now()
|
||||
)
|
||||
)
|
||||
onBlocking { follow(any(), any()) } doReturn false
|
||||
onBlocking { followRequest(any(), any()) } doReturn false
|
||||
}
|
||||
val activityPubFollowService =
|
||||
ActivityPubReceiveFollowServiceImpl(
|
||||
|
|
Loading…
Reference in New Issue