mirror of https://github.com/usbharu/Hideout.git
Merge pull request #33 from usbharu/feature/resource-not-found-exception
Feature/resource not found exception
This commit is contained in:
commit
5a56c61ee4
|
@ -8,6 +8,7 @@ open class Person : Object {
|
|||
var url: String? = null
|
||||
private var icon: Image? = null
|
||||
var publicKey: Key? = null
|
||||
var endpoints: Map<String, String> = emptyMap()
|
||||
|
||||
protected constructor() : super()
|
||||
|
||||
|
@ -22,7 +23,8 @@ open class Person : Object {
|
|||
outbox: String?,
|
||||
url: String?,
|
||||
icon: Image?,
|
||||
publicKey: Key?
|
||||
publicKey: Key?,
|
||||
endpoints: Map<String, String> = emptyMap()
|
||||
) : super(add(type, "Person"), name, id = id) {
|
||||
this.preferredUsername = preferredUsername
|
||||
this.summary = summary
|
||||
|
@ -31,24 +33,28 @@ open class Person : Object {
|
|||
this.url = url
|
||||
this.icon = icon
|
||||
this.publicKey = publicKey
|
||||
this.endpoints = endpoints
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is Person) return false
|
||||
if (!super.equals(other)) return false
|
||||
|
||||
if (id != other.id) return false
|
||||
if (preferredUsername != other.preferredUsername) return false
|
||||
if (summary != other.summary) return false
|
||||
if (inbox != other.inbox) return false
|
||||
if (outbox != other.outbox) return false
|
||||
if (url != other.url) return false
|
||||
if (icon != other.icon) return false
|
||||
return publicKey == other.publicKey
|
||||
if (publicKey != other.publicKey) return false
|
||||
if (endpoints != other.endpoints) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = id?.hashCode() ?: 0
|
||||
var result = super.hashCode()
|
||||
result = 31 * result + (preferredUsername?.hashCode() ?: 0)
|
||||
result = 31 * result + (summary?.hashCode() ?: 0)
|
||||
result = 31 * result + (inbox?.hashCode() ?: 0)
|
||||
|
@ -56,6 +62,7 @@ open class Person : Object {
|
|||
result = 31 * result + (url?.hashCode() ?: 0)
|
||||
result = 31 * result + (icon?.hashCode() ?: 0)
|
||||
result = 31 * result + (publicKey?.hashCode() ?: 0)
|
||||
result = 31 * result + endpoints.hashCode()
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
package dev.usbharu.hideout.exception
|
||||
|
||||
open class FailedToGetResourcesException : IllegalArgumentException {
|
||||
constructor() : super()
|
||||
constructor(s: String?) : super(s)
|
||||
constructor(message: String?, cause: Throwable?) : super(message, cause)
|
||||
constructor(cause: Throwable?) : super(cause)
|
||||
}
|
|
@ -10,13 +10,14 @@ fun Application.configureStatusPages() {
|
|||
install(StatusPages) {
|
||||
exception<IllegalArgumentException> { call, cause ->
|
||||
call.respondText(text = "400: $cause", status = HttpStatusCode.BadRequest)
|
||||
call.application.log.warn("Bad Request", cause)
|
||||
}
|
||||
exception<InvalidUsernameOrPasswordException> { call, _ ->
|
||||
call.respond(HttpStatusCode.Unauthorized)
|
||||
}
|
||||
exception<Throwable> { call, cause ->
|
||||
call.respondText(text = "500: ${cause.stackTraceToString()}", status = HttpStatusCode.InternalServerError)
|
||||
cause.printStackTrace()
|
||||
call.application.log.error("Internal Server Error", cause)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,4 +9,5 @@ interface FollowerQueryService {
|
|||
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
|
||||
}
|
||||
|
|
|
@ -198,6 +198,12 @@ class FollowerQueryServiceImpl : FollowerQueryService {
|
|||
}
|
||||
|
||||
override suspend fun removeFollower(user: Long, follower: Long) {
|
||||
UsersFollowers.deleteWhere { Users.id eq user and (followerId eq follower) }
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
package dev.usbharu.hideout.query
|
||||
|
||||
import dev.usbharu.hideout.domain.model.hideout.entity.JwtRefreshToken
|
||||
import dev.usbharu.hideout.exception.FailedToGetResourcesException
|
||||
import dev.usbharu.hideout.repository.JwtRefreshTokens
|
||||
import dev.usbharu.hideout.repository.toJwtRefreshToken
|
||||
import dev.usbharu.hideout.util.singleOr
|
||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||
import org.jetbrains.exposed.sql.deleteAll
|
||||
import org.jetbrains.exposed.sql.deleteWhere
|
||||
|
@ -12,13 +14,19 @@ import org.koin.core.annotation.Single
|
|||
@Single
|
||||
class JwtRefreshTokenQueryServiceImpl : JwtRefreshTokenQueryService {
|
||||
override suspend fun findById(id: Long): JwtRefreshToken =
|
||||
JwtRefreshTokens.select { JwtRefreshTokens.id.eq(id) }.single().toJwtRefreshToken()
|
||||
JwtRefreshTokens.select { JwtRefreshTokens.id.eq(id) }
|
||||
.singleOr { FailedToGetResourcesException("id: $id is a duplicate or does not exist.", it) }
|
||||
.toJwtRefreshToken()
|
||||
|
||||
override suspend fun findByToken(token: String): JwtRefreshToken =
|
||||
JwtRefreshTokens.select { JwtRefreshTokens.refreshToken.eq(token) }.single().toJwtRefreshToken()
|
||||
JwtRefreshTokens.select { JwtRefreshTokens.refreshToken.eq(token) }
|
||||
.singleOr { FailedToGetResourcesException("token: $token is a duplicate or does not exist.", it) }
|
||||
.toJwtRefreshToken()
|
||||
|
||||
override suspend fun findByUserId(userId: Long): JwtRefreshToken =
|
||||
JwtRefreshTokens.select { JwtRefreshTokens.userId.eq(userId) }.single().toJwtRefreshToken()
|
||||
JwtRefreshTokens.select { JwtRefreshTokens.userId.eq(userId) }
|
||||
.singleOr { FailedToGetResourcesException("userId: $userId is a duplicate or does not exist.", it) }
|
||||
.toJwtRefreshToken()
|
||||
|
||||
override suspend fun deleteById(id: Long) {
|
||||
JwtRefreshTokens.deleteWhere { JwtRefreshTokens.id eq id }
|
||||
|
|
|
@ -1,16 +1,22 @@
|
|||
package dev.usbharu.hideout.query
|
||||
|
||||
import dev.usbharu.hideout.domain.model.hideout.entity.Post
|
||||
import dev.usbharu.hideout.exception.FailedToGetResourcesException
|
||||
import dev.usbharu.hideout.repository.Posts
|
||||
import dev.usbharu.hideout.repository.toPost
|
||||
import dev.usbharu.hideout.util.singleOr
|
||||
import org.jetbrains.exposed.sql.select
|
||||
import org.koin.core.annotation.Single
|
||||
|
||||
@Single
|
||||
class PostQueryServiceImpl : PostQueryService {
|
||||
override suspend fun findById(id: Long): Post = Posts.select { Posts.id eq id }.single().toPost()
|
||||
override suspend fun findById(id: Long): Post =
|
||||
Posts.select { Posts.id eq id }
|
||||
.singleOr { FailedToGetResourcesException("id: $id is duplicate or does not exist.", it) }.toPost()
|
||||
|
||||
override suspend fun findByUrl(url: String): Post = Posts.select { Posts.url eq url }.single().toPost()
|
||||
override suspend fun findByUrl(url: String): Post = Posts.select { Posts.url eq url }
|
||||
.singleOr { FailedToGetResourcesException("url: $url is duplicate or does not exist.", it) }.toPost()
|
||||
|
||||
override suspend fun findByApId(string: String): Post = Posts.select { Posts.apId eq string }.single().toPost()
|
||||
override suspend fun findByApId(string: String): Post = Posts.select { Posts.apId eq string }
|
||||
.singleOr { FailedToGetResourcesException("apId: $string is duplicate or does not exist.", it) }.toPost()
|
||||
}
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
package dev.usbharu.hideout.query
|
||||
|
||||
import dev.usbharu.hideout.domain.model.hideout.dto.PostResponse
|
||||
import dev.usbharu.hideout.exception.FailedToGetResourcesException
|
||||
import dev.usbharu.hideout.repository.Posts
|
||||
import dev.usbharu.hideout.repository.Users
|
||||
import dev.usbharu.hideout.repository.toPost
|
||||
import dev.usbharu.hideout.repository.toUser
|
||||
import dev.usbharu.hideout.util.singleOr
|
||||
import org.jetbrains.exposed.sql.and
|
||||
import org.jetbrains.exposed.sql.innerJoin
|
||||
import org.jetbrains.exposed.sql.select
|
||||
|
@ -17,7 +19,7 @@ class PostResponseQueryServiceImpl : PostResponseQueryService {
|
|||
return Posts
|
||||
.innerJoin(Users, onColumn = { Posts.userId }, otherColumn = { Users.id })
|
||||
.select { Posts.id eq id }
|
||||
.single()
|
||||
.singleOr { FailedToGetResourcesException("id: $id,userId: $userId is a duplicate or does not exist.", it) }
|
||||
.let { PostResponse.from(it.toPost(), it.toUser()) }
|
||||
}
|
||||
|
||||
|
|
|
@ -3,9 +3,11 @@ package dev.usbharu.hideout.query
|
|||
import dev.usbharu.hideout.domain.model.hideout.dto.Account
|
||||
import dev.usbharu.hideout.domain.model.hideout.dto.ReactionResponse
|
||||
import dev.usbharu.hideout.domain.model.hideout.entity.Reaction
|
||||
import dev.usbharu.hideout.exception.FailedToGetResourcesException
|
||||
import dev.usbharu.hideout.repository.Reactions
|
||||
import dev.usbharu.hideout.repository.Users
|
||||
import dev.usbharu.hideout.repository.toReaction
|
||||
import dev.usbharu.hideout.util.singleOr
|
||||
import org.jetbrains.exposed.sql.*
|
||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||
import org.koin.core.annotation.Single
|
||||
|
@ -26,7 +28,12 @@ class ReactionQueryServiceImpl : ReactionQueryService {
|
|||
Reactions.emojiId.eq(emojiId)
|
||||
)
|
||||
}
|
||||
.single()
|
||||
.singleOr {
|
||||
FailedToGetResourcesException(
|
||||
"postId: $postId,userId: $userId,emojiId: $emojiId is duplicate or does not exist.",
|
||||
it
|
||||
)
|
||||
}
|
||||
.toReaction()
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
package dev.usbharu.hideout.query
|
||||
|
||||
import dev.usbharu.hideout.domain.model.hideout.entity.User
|
||||
import dev.usbharu.hideout.exception.FailedToGetResourcesException
|
||||
import dev.usbharu.hideout.repository.Users
|
||||
import dev.usbharu.hideout.repository.toUser
|
||||
import dev.usbharu.hideout.util.singleOr
|
||||
import org.jetbrains.exposed.sql.and
|
||||
import org.jetbrains.exposed.sql.select
|
||||
import org.jetbrains.exposed.sql.selectAll
|
||||
|
@ -13,14 +15,22 @@ class UserQueryServiceImpl : UserQueryService {
|
|||
override suspend fun findAll(limit: Int, offset: Long): List<User> =
|
||||
Users.selectAll().limit(limit, offset).map { it.toUser() }
|
||||
|
||||
override suspend fun findById(id: Long): User = Users.select { Users.id eq id }.single().toUser()
|
||||
override suspend fun findById(id: Long): User = Users.select { Users.id eq id }
|
||||
.singleOr { FailedToGetResourcesException("id: $id is duplicate or does not exist.", it) }.toUser()
|
||||
|
||||
override suspend fun findByName(name: String): List<User> = Users.select { Users.name eq name }.map { it.toUser() }
|
||||
|
||||
override suspend fun findByNameAndDomain(name: String, domain: String): User =
|
||||
Users.select { Users.name eq name and (Users.domain eq domain) }.single().toUser()
|
||||
Users
|
||||
.select { Users.name eq name and (Users.domain eq domain) }
|
||||
.singleOr {
|
||||
FailedToGetResourcesException("name: $name,domain: $domain is duplicate or does not exist.", it)
|
||||
}
|
||||
.toUser()
|
||||
|
||||
override suspend fun findByUrl(url: String): User = Users.select { Users.url eq url }.single().toUser()
|
||||
override suspend fun findByUrl(url: String): User = Users.select { Users.url eq url }
|
||||
.singleOr { FailedToGetResourcesException("url: $url is duplicate or does not exist.", it) }
|
||||
.toUser()
|
||||
|
||||
override suspend fun findByIds(ids: List<Long>): List<User> =
|
||||
Users.select { Users.id inList ids }.map { it.toUser() }
|
||||
|
|
|
@ -2,6 +2,7 @@ package dev.usbharu.hideout.repository
|
|||
|
||||
import dev.usbharu.hideout.domain.model.hideout.entity.Post
|
||||
import dev.usbharu.hideout.domain.model.hideout.entity.Visibility
|
||||
import dev.usbharu.hideout.exception.FailedToGetResourcesException
|
||||
import dev.usbharu.hideout.service.core.IdGenerateService
|
||||
import org.jetbrains.exposed.sql.*
|
||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||
|
@ -53,7 +54,8 @@ class PostRepositoryImpl(database: Database, private val idGenerateService: IdGe
|
|||
return post
|
||||
}
|
||||
|
||||
override suspend fun findById(id: Long): Post = Posts.select { Posts.id eq id }.single().toPost()
|
||||
override suspend fun findById(id: Long): Post = Posts.select { Posts.id eq id }.singleOrNull()?.toPost()
|
||||
?: throw FailedToGetResourcesException("id: $id was not found.")
|
||||
|
||||
override suspend fun delete(id: Long) {
|
||||
Posts.deleteWhere { Posts.id eq id }
|
||||
|
|
|
@ -28,10 +28,7 @@ fun Routing.usersAP(
|
|||
val name =
|
||||
call.parameters["name"] ?: throw ParameterNotExistException("Parameter(name='name') does not exist.")
|
||||
val person = apUserService.getPersonByName(name)
|
||||
return@handle call.respondAp(
|
||||
person,
|
||||
HttpStatusCode.OK
|
||||
)
|
||||
return@handle call.respondAp(person, HttpStatusCode.OK)
|
||||
}
|
||||
get {
|
||||
// TODO: 暫定処置なので治す
|
||||
|
@ -41,7 +38,13 @@ fun Routing.usersAP(
|
|||
?: throw ParameterNotExistException("Parameter(name='name') does not exist."),
|
||||
Config.configData.domain
|
||||
)
|
||||
call.respondText(userEntity.toString() + "\n" + followerQueryService.findFollowersById(userEntity.id))
|
||||
val personByName = apUserService.getPersonByName(userEntity.name)
|
||||
call.respondText(
|
||||
userEntity.toString() + "\n" + followerQueryService.findFollowersById(userEntity.id) +
|
||||
"\n" + Config.configData.objectMapper.writeValueAsString(
|
||||
personByName
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -51,9 +54,7 @@ class ContentTypeRouteSelector(private vararg val contentType: ContentType) : Ro
|
|||
override fun evaluate(context: RoutingResolveContext, segmentIndex: Int): RouteSelectorEvaluation {
|
||||
context.call.application.log.debug("Accept: ${context.call.request.accept()}")
|
||||
val requestContentType = context.call.request.accept() ?: return RouteSelectorEvaluation.FailedParameter
|
||||
return if (requestContentType.split(",")
|
||||
.any { contentType.any { contentType -> contentType.match(it) } }
|
||||
) {
|
||||
return if (requestContentType.split(",").any { contentType.any { contentType -> contentType.match(it) } }) {
|
||||
RouteSelectorEvaluation.Constant
|
||||
} else {
|
||||
RouteSelectorEvaluation.FailedParameter
|
||||
|
|
|
@ -72,19 +72,16 @@ fun Route.users(userService: UserService, userApiService: UserApiService) {
|
|||
?: throw IllegalStateException("no principal")
|
||||
val userParameter = call.parameters["name"]
|
||||
?: throw ParameterNotExistException("Parameter(name='userName@domain') does not exist.")
|
||||
if (userParameter.toLongOrNull() != null) {
|
||||
if (userService.followRequest(userParameter.toLong(), userId)) {
|
||||
return@post call.respond(HttpStatusCode.OK)
|
||||
if (if (userParameter.toLongOrNull() != null) {
|
||||
userApiService.follow(userParameter.toLong(), userId)
|
||||
} else {
|
||||
return@post call.respond(HttpStatusCode.Accepted)
|
||||
val parse = AcctUtil.parse(userParameter)
|
||||
userApiService.follow(parse, userId)
|
||||
}
|
||||
}
|
||||
val acct = AcctUtil.parse(userParameter)
|
||||
val targetUser = userApiService.findByAcct(acct)
|
||||
if (userService.followRequest(targetUser.id.toLong(), userId)) {
|
||||
return@post call.respond(HttpStatusCode.OK)
|
||||
) {
|
||||
call.respond(HttpStatusCode.OK)
|
||||
} else {
|
||||
return@post call.respond(HttpStatusCode.Accepted)
|
||||
call.respond(HttpStatusCode.Accepted)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,9 @@ import dev.usbharu.hideout.domain.model.ActivityPubStringResponse
|
|||
import dev.usbharu.hideout.domain.model.ap.Accept
|
||||
import dev.usbharu.hideout.domain.model.ap.Follow
|
||||
import dev.usbharu.hideout.exception.ap.IllegalActivityPubObjectException
|
||||
import dev.usbharu.hideout.query.FollowerQueryService
|
||||
import dev.usbharu.hideout.query.UserQueryService
|
||||
import dev.usbharu.hideout.service.core.Transaction
|
||||
import dev.usbharu.hideout.service.user.UserService
|
||||
import io.ktor.http.*
|
||||
import org.koin.core.annotation.Single
|
||||
|
@ -17,9 +19,12 @@ interface APAcceptService {
|
|||
@Single
|
||||
class APAcceptServiceImpl(
|
||||
private val userService: UserService,
|
||||
private val userQueryService: UserQueryService
|
||||
private val userQueryService: UserQueryService,
|
||||
private val followerQueryService: FollowerQueryService,
|
||||
private val transaction: Transaction
|
||||
) : APAcceptService {
|
||||
override suspend fun receiveAccept(accept: Accept): ActivityPubResponse {
|
||||
return transaction.transaction {
|
||||
val value = accept.`object` ?: throw IllegalActivityPubObjectException("object is null")
|
||||
if (value.type.contains("Follow").not()) {
|
||||
throw IllegalActivityPubObjectException("Invalid type ${value.type}")
|
||||
|
@ -30,7 +35,11 @@ class APAcceptServiceImpl(
|
|||
val followerUrl = follow.actor ?: throw IllegalActivityPubObjectException("actor is null")
|
||||
val user = userQueryService.findByUrl(userUrl)
|
||||
val follower = userQueryService.findByUrl(followerUrl)
|
||||
if (followerQueryService.alreadyFollow(user.id, follower.id)) {
|
||||
return@transaction ActivityPubStringResponse(HttpStatusCode.OK, "accepted")
|
||||
}
|
||||
userService.follow(user.id, follower.id)
|
||||
return ActivityPubStringResponse(HttpStatusCode.OK, "accepted")
|
||||
ActivityPubStringResponse(HttpStatusCode.OK, "accepted")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import dev.usbharu.hideout.domain.model.ap.Note
|
|||
import dev.usbharu.hideout.domain.model.hideout.entity.Post
|
||||
import dev.usbharu.hideout.domain.model.hideout.entity.Visibility
|
||||
import dev.usbharu.hideout.domain.model.job.DeliverPostJob
|
||||
import dev.usbharu.hideout.exception.FailedToGetResourcesException
|
||||
import dev.usbharu.hideout.exception.ap.IllegalActivityPubObjectException
|
||||
import dev.usbharu.hideout.plugins.getAp
|
||||
import dev.usbharu.hideout.plugins.postAp
|
||||
|
@ -83,11 +84,10 @@ class APNoteServiceImpl(
|
|||
}
|
||||
|
||||
override suspend fun fetchNote(url: String, targetActor: String?): Note {
|
||||
val post = postQueryService.findByUrl(url)
|
||||
try {
|
||||
val post = postQueryService.findByUrl(url)
|
||||
return postToNote(post)
|
||||
} catch (_: NoSuchElementException) {
|
||||
} catch (_: IllegalArgumentException) {
|
||||
} catch (_: FailedToGetResourcesException) {
|
||||
}
|
||||
|
||||
val response = httpClient.getAp(
|
||||
|
@ -119,23 +119,23 @@ class APNoteServiceImpl(
|
|||
targetActor: String?,
|
||||
url: String
|
||||
): Note {
|
||||
val findByApId = try {
|
||||
postQueryService.findByApId(url)
|
||||
} catch (_: NoSuchElementException) {
|
||||
if (note.id == null) {
|
||||
return internalNote(note, targetActor, url)
|
||||
} catch (_: IllegalArgumentException) {
|
||||
}
|
||||
|
||||
val findByApId = try {
|
||||
postQueryService.findByApId(note.id!!)
|
||||
} catch (_: FailedToGetResourcesException) {
|
||||
return internalNote(note, targetActor, url)
|
||||
}
|
||||
return postToNote(findByApId)
|
||||
}
|
||||
|
||||
private suspend fun internalNote(note: Note, targetActor: String?, url: String): Note {
|
||||
val person = apUserService.fetchPerson(
|
||||
val person = apUserService.fetchPersonWithEntity(
|
||||
note.attributedTo ?: throw IllegalActivityPubObjectException("note.attributedTo is null"),
|
||||
targetActor
|
||||
)
|
||||
val user =
|
||||
userQueryService.findByUrl(person.url ?: throw IllegalActivityPubObjectException("person.url is null"))
|
||||
|
||||
val visibility =
|
||||
if (note.to.contains(public) && note.cc.contains(public)) {
|
||||
|
@ -156,7 +156,7 @@ class APNoteServiceImpl(
|
|||
postRepository.save(
|
||||
Post(
|
||||
id = postRepository.generateId(),
|
||||
userId = user.id,
|
||||
userId = person.second.id,
|
||||
overview = null,
|
||||
text = note.content.orEmpty(),
|
||||
createdAt = Instant.parse(note.published).toEpochMilli(),
|
||||
|
|
|
@ -6,6 +6,8 @@ import dev.usbharu.hideout.domain.model.ap.Image
|
|||
import dev.usbharu.hideout.domain.model.ap.Key
|
||||
import dev.usbharu.hideout.domain.model.ap.Person
|
||||
import dev.usbharu.hideout.domain.model.hideout.dto.RemoteUserCreateDto
|
||||
import dev.usbharu.hideout.domain.model.hideout.entity.User
|
||||
import dev.usbharu.hideout.exception.FailedToGetResourcesException
|
||||
import dev.usbharu.hideout.exception.ap.IllegalActivityPubObjectException
|
||||
import dev.usbharu.hideout.plugins.getAp
|
||||
import dev.usbharu.hideout.query.UserQueryService
|
||||
|
@ -29,6 +31,8 @@ interface APUserService {
|
|||
* @return
|
||||
*/
|
||||
suspend fun fetchPerson(url: String, targetActor: String? = null): Person
|
||||
|
||||
suspend fun fetchPersonWithEntity(url: String, targetActor: String? = null): Pair<Person, User>
|
||||
}
|
||||
|
||||
@Single
|
||||
|
@ -67,11 +71,15 @@ class APUserServiceImpl(
|
|||
id = "$userUrl#pubkey",
|
||||
owner = userUrl,
|
||||
publicKeyPem = userEntity.publicKey
|
||||
)
|
||||
),
|
||||
endpoints = mapOf("sharedInbox" to "${Config.configData.url}/inbox")
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun fetchPerson(url: String, targetActor: String?): Person {
|
||||
override suspend fun fetchPerson(url: String, targetActor: String?): Person =
|
||||
fetchPersonWithEntity(url, targetActor).first
|
||||
|
||||
override suspend fun fetchPersonWithEntity(url: String, targetActor: String?): Pair<Person, User> {
|
||||
return try {
|
||||
val userEntity = userQueryService.findByUrl(url)
|
||||
return Person(
|
||||
|
@ -95,9 +103,10 @@ class APUserServiceImpl(
|
|||
id = "$url#pubkey",
|
||||
owner = url,
|
||||
publicKeyPem = userEntity.publicKey
|
||||
)
|
||||
)
|
||||
} catch (ignore: NoSuchElementException) {
|
||||
),
|
||||
endpoints = mapOf("sharedInbox" to "${Config.configData.url}/inbox")
|
||||
) to userEntity
|
||||
} catch (ignore: FailedToGetResourcesException) {
|
||||
val httpResponse = if (targetActor != null) {
|
||||
httpClient.getAp(url, "$targetActor#pubkey")
|
||||
} else {
|
||||
|
@ -107,7 +116,7 @@ class APUserServiceImpl(
|
|||
}
|
||||
val person = Config.configData.objectMapper.readValue<Person>(httpResponse.bodyAsText())
|
||||
|
||||
userService.createRemoteUser(
|
||||
person to userService.createRemoteUser(
|
||||
RemoteUserCreateDto(
|
||||
name = person.preferredUsername
|
||||
?: throw IllegalActivityPubObjectException("preferredUsername is null"),
|
||||
|
@ -122,7 +131,6 @@ class APUserServiceImpl(
|
|||
?: throw IllegalActivityPubObjectException("publicKey is null"),
|
||||
)
|
||||
)
|
||||
person
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import dev.usbharu.hideout.service.user.UserService
|
|||
import org.koin.core.annotation.Single
|
||||
import kotlin.math.min
|
||||
|
||||
@Suppress("TooManyFunctions")
|
||||
interface UserApiService {
|
||||
suspend fun findAll(limit: Int? = 100, offset: Long = 0): List<UserResponse>
|
||||
|
||||
|
@ -30,6 +31,9 @@ interface UserApiService {
|
|||
suspend fun findFollowingsByAcct(acct: Acct): List<UserResponse>
|
||||
|
||||
suspend fun createUser(username: String, password: String): UserResponse
|
||||
|
||||
suspend fun follow(targetId: Long, sourceId: Long): Boolean
|
||||
suspend fun follow(targetAcct: Acct, sourceId: Long): Boolean
|
||||
}
|
||||
|
||||
@Single
|
||||
|
@ -39,30 +43,47 @@ class UserApiServiceImpl(
|
|||
private val userService: UserService,
|
||||
private val transaction: Transaction
|
||||
) : UserApiService {
|
||||
override suspend fun findAll(limit: Int?, offset: Long): List<UserResponse> =
|
||||
override suspend fun findAll(limit: Int?, offset: Long): List<UserResponse> = transaction.transaction {
|
||||
userQueryService.findAll(min(limit ?: 100, 100), offset).map { UserResponse.from(it) }
|
||||
}
|
||||
|
||||
override suspend fun findById(id: Long): UserResponse = UserResponse.from(userQueryService.findById(id))
|
||||
override suspend fun findById(id: Long): UserResponse =
|
||||
transaction.transaction { UserResponse.from(userQueryService.findById(id)) }
|
||||
|
||||
override suspend fun findByIds(ids: List<Long>): List<UserResponse> =
|
||||
override suspend fun findByIds(ids: List<Long>): List<UserResponse> {
|
||||
return transaction.transaction {
|
||||
userQueryService.findByIds(ids).map { UserResponse.from(it) }
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun findByAcct(acct: Acct): UserResponse =
|
||||
UserResponse.from(userQueryService.findByNameAndDomain(acct.username, acct.domain ?: Config.configData.domain))
|
||||
override suspend fun findByAcct(acct: Acct): UserResponse {
|
||||
return transaction.transaction {
|
||||
UserResponse.from(
|
||||
userQueryService.findByNameAndDomain(
|
||||
acct.username,
|
||||
acct.domain ?: Config.configData.domain
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun findFollowers(userId: Long): List<UserResponse> =
|
||||
override suspend fun findFollowers(userId: Long): List<UserResponse> = transaction.transaction {
|
||||
followerQueryService.findFollowersById(userId).map { UserResponse.from(it) }
|
||||
}
|
||||
|
||||
override suspend fun findFollowings(userId: Long): List<UserResponse> =
|
||||
override suspend fun findFollowings(userId: Long): List<UserResponse> = transaction.transaction {
|
||||
followerQueryService.findFollowingById(userId).map { UserResponse.from(it) }
|
||||
}
|
||||
|
||||
override suspend fun findFollowersByAcct(acct: Acct): List<UserResponse> =
|
||||
override suspend fun findFollowersByAcct(acct: Acct): List<UserResponse> = transaction.transaction {
|
||||
followerQueryService.findFollowersByNameAndDomain(acct.username, acct.domain ?: Config.configData.domain)
|
||||
.map { UserResponse.from(it) }
|
||||
}
|
||||
|
||||
override suspend fun findFollowingsByAcct(acct: Acct): List<UserResponse> =
|
||||
override suspend fun findFollowingsByAcct(acct: Acct): List<UserResponse> = transaction.transaction {
|
||||
followerQueryService.findFollowingByNameAndDomain(acct.username, acct.domain ?: Config.configData.domain)
|
||||
.map { UserResponse.from(it) }
|
||||
}
|
||||
|
||||
override suspend fun createUser(username: String, password: String): UserResponse {
|
||||
return transaction.transaction {
|
||||
|
@ -72,4 +93,22 @@ class UserApiServiceImpl(
|
|||
UserResponse.from(userService.createLocalUser(UserCreateDto(username, username, "", password)))
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun follow(targetId: Long, sourceId: Long): Boolean {
|
||||
return transaction.transaction {
|
||||
userService.followRequest(targetId, sourceId)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun follow(targetAcct: Acct, sourceId: Long): Boolean {
|
||||
return transaction.transaction {
|
||||
userService.followRequest(
|
||||
userQueryService.findByNameAndDomain(
|
||||
targetAcct.username,
|
||||
targetAcct.domain ?: Config.configData.domain
|
||||
).id,
|
||||
sourceId
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
package dev.usbharu.hideout.util
|
||||
|
||||
class CollectionUtil
|
||||
|
||||
fun <T> Iterable<T>.singleOr(block: (e: RuntimeException) -> Throwable): T {
|
||||
return try {
|
||||
this.single()
|
||||
} catch (e: NoSuchElementException) {
|
||||
throw block(e)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
throw block(e)
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@
|
|||
<pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
<root level="INFO">
|
||||
<root level="TRACE">
|
||||
<appender-ref ref="STDOUT"/>
|
||||
</root>
|
||||
<logger name="org.eclipse.jetty" level="INFO"/>
|
||||
|
|
|
@ -4,6 +4,7 @@ import com.auth0.jwt.interfaces.Claim
|
|||
import com.auth0.jwt.interfaces.Payload
|
||||
import com.fasterxml.jackson.module.kotlin.readValue
|
||||
import dev.usbharu.hideout.config.Config
|
||||
import dev.usbharu.hideout.domain.model.Acct
|
||||
import dev.usbharu.hideout.domain.model.hideout.dto.UserResponse
|
||||
import dev.usbharu.hideout.domain.model.hideout.entity.User
|
||||
import dev.usbharu.hideout.domain.model.hideout.form.UserCreate
|
||||
|
@ -433,9 +434,7 @@ class UsersTest {
|
|||
"https://example.com/test",
|
||||
Instant.now().toEpochMilli()
|
||||
)
|
||||
}
|
||||
val userService = mock<UserService> {
|
||||
onBlocking { followRequest(eq(1235), eq(1234)) } doReturn true
|
||||
onBlocking { follow(any<Acct>(), eq(1234)) } doReturn true
|
||||
}
|
||||
application {
|
||||
configureSerialization()
|
||||
|
@ -448,7 +447,7 @@ class UsersTest {
|
|||
}
|
||||
routing {
|
||||
route("/api/internal/v1") {
|
||||
users(userService, userApiService)
|
||||
users(mock(), userApiService)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -483,9 +482,7 @@ class UsersTest {
|
|||
"https://example.com/test",
|
||||
Instant.now().toEpochMilli()
|
||||
)
|
||||
}
|
||||
val userService = mock<UserService> {
|
||||
onBlocking { followRequest(eq(1235), eq(1234)) } doReturn false
|
||||
onBlocking { follow(any<Acct>(), eq(1234)) } doReturn false
|
||||
}
|
||||
application {
|
||||
configureSerialization()
|
||||
|
@ -498,7 +495,7 @@ class UsersTest {
|
|||
}
|
||||
routing {
|
||||
route("/api/internal/v1") {
|
||||
users(userService, userApiService)
|
||||
users(mock(), userApiService)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -533,9 +530,7 @@ class UsersTest {
|
|||
"https://example.com/test",
|
||||
Instant.now().toEpochMilli()
|
||||
)
|
||||
}
|
||||
val userService = mock<UserService> {
|
||||
onBlocking { followRequest(eq(1235), eq(1234)) } doReturn false
|
||||
onBlocking { follow(eq(1235), eq(1234)) } doReturn false
|
||||
}
|
||||
application {
|
||||
configureSerialization()
|
||||
|
@ -548,7 +543,7 @@ class UsersTest {
|
|||
}
|
||||
routing {
|
||||
route("/api/internal/v1") {
|
||||
users(userService, userApiService)
|
||||
users(mock(), userApiService)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue