mirror of https://github.com/usbharu/Hideout.git
Compare commits
2 Commits
98638af478
...
bf096b63ac
Author | SHA1 | Date |
---|---|---|
usbharu | bf096b63ac | |
usbharu | 2661bd3e9e |
|
@ -9,6 +9,7 @@ import dev.usbharu.hideout.config.Config
|
||||||
import dev.usbharu.hideout.config.ConfigData
|
import dev.usbharu.hideout.config.ConfigData
|
||||||
import dev.usbharu.hideout.domain.model.job.DeliverPostJob
|
import dev.usbharu.hideout.domain.model.job.DeliverPostJob
|
||||||
import dev.usbharu.hideout.domain.model.job.DeliverReactionJob
|
import dev.usbharu.hideout.domain.model.job.DeliverReactionJob
|
||||||
|
import dev.usbharu.hideout.domain.model.job.DeliverRemoveReactionJob
|
||||||
import dev.usbharu.hideout.domain.model.job.ReceiveFollowJob
|
import dev.usbharu.hideout.domain.model.job.ReceiveFollowJob
|
||||||
import dev.usbharu.hideout.plugins.*
|
import dev.usbharu.hideout.plugins.*
|
||||||
import dev.usbharu.hideout.repository.IUserRepository
|
import dev.usbharu.hideout.repository.IUserRepository
|
||||||
|
@ -25,6 +26,7 @@ import dev.usbharu.hideout.service.core.IdGenerateService
|
||||||
import dev.usbharu.hideout.service.core.TwitterSnowflakeIdGenerateService
|
import dev.usbharu.hideout.service.core.TwitterSnowflakeIdGenerateService
|
||||||
import dev.usbharu.hideout.service.job.JobQueueParentService
|
import dev.usbharu.hideout.service.job.JobQueueParentService
|
||||||
import dev.usbharu.hideout.service.job.KJobJobQueueParentService
|
import dev.usbharu.hideout.service.job.KJobJobQueueParentService
|
||||||
|
import dev.usbharu.hideout.service.reaction.IReactionService
|
||||||
import dev.usbharu.hideout.service.user.IUserAuthService
|
import dev.usbharu.hideout.service.user.IUserAuthService
|
||||||
import dev.usbharu.hideout.service.user.IUserService
|
import dev.usbharu.hideout.service.user.IUserService
|
||||||
import dev.usbharu.kjob.exposed.ExposedKJob
|
import dev.usbharu.kjob.exposed.ExposedKJob
|
||||||
|
@ -115,6 +117,7 @@ fun Application.parent() {
|
||||||
activityPubUserService = inject<ActivityPubUserService>().value,
|
activityPubUserService = inject<ActivityPubUserService>().value,
|
||||||
postService = inject<IPostApiService>().value,
|
postService = inject<IPostApiService>().value,
|
||||||
userApiService = inject<IUserApiService>().value,
|
userApiService = inject<IUserApiService>().value,
|
||||||
|
reactionService = inject<IReactionService>().value
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,4 +145,10 @@ fun Application.worker() {
|
||||||
activityPubService.processActivity(this, it)
|
activityPubService.processActivity(this, it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
kJob.register(DeliverRemoveReactionJob) {
|
||||||
|
execute {
|
||||||
|
activityPubService.processActivity(this, it)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import com.fasterxml.jackson.databind.JsonNode
|
||||||
import dev.usbharu.hideout.service.activitypub.ExtendedActivityVocabulary
|
import dev.usbharu.hideout.service.activitypub.ExtendedActivityVocabulary
|
||||||
|
|
||||||
class ObjectDeserializer : JsonDeserializer<Object>() {
|
class ObjectDeserializer : JsonDeserializer<Object>() {
|
||||||
|
@Suppress("LongMethod", "CyclomaticComplexMethod")
|
||||||
override fun deserialize(p: JsonParser?, ctxt: DeserializationContext?): Object {
|
override fun deserialize(p: JsonParser?, ctxt: DeserializationContext?): Object {
|
||||||
requireNotNull(p)
|
requireNotNull(p)
|
||||||
val treeNode: JsonNode = requireNotNull(p.codec?.readTree(p))
|
val treeNode: JsonNode = requireNotNull(p.codec?.readTree(p))
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
package dev.usbharu.hideout.domain.model.hideout.dto
|
||||||
|
|
||||||
|
data class ReactionResponse(
|
||||||
|
val reaction: String,
|
||||||
|
val isUnicodeEmoji: Boolean = true,
|
||||||
|
val iconUrl: String,
|
||||||
|
val accounts: List<Account>
|
||||||
|
)
|
||||||
|
|
||||||
|
data class Account(val screenName: String, val iconUrl: String, val url: String)
|
|
@ -0,0 +1,3 @@
|
||||||
|
package dev.usbharu.hideout.domain.model.hideout.form
|
||||||
|
|
||||||
|
data class Reaction(val reaction: String?)
|
|
@ -23,3 +23,10 @@ object DeliverReactionJob : HideoutJob("DeliverReactionJob") {
|
||||||
val inbox = string("inbox")
|
val inbox = string("inbox")
|
||||||
val id = string("id")
|
val id = string("id")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object DeliverRemoveReactionJob : HideoutJob("DeliverRemoveReactionJob") {
|
||||||
|
val id = string("id")
|
||||||
|
val inbox = string("inbox")
|
||||||
|
val actor = string("actor")
|
||||||
|
val like = string("like")
|
||||||
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import dev.usbharu.hideout.service.activitypub.ActivityPubUserService
|
||||||
import dev.usbharu.hideout.service.api.IPostApiService
|
import dev.usbharu.hideout.service.api.IPostApiService
|
||||||
import dev.usbharu.hideout.service.api.IUserApiService
|
import dev.usbharu.hideout.service.api.IUserApiService
|
||||||
import dev.usbharu.hideout.service.auth.HttpSignatureVerifyService
|
import dev.usbharu.hideout.service.auth.HttpSignatureVerifyService
|
||||||
|
import dev.usbharu.hideout.service.reaction.IReactionService
|
||||||
import dev.usbharu.hideout.service.user.IUserService
|
import dev.usbharu.hideout.service.user.IUserService
|
||||||
import io.ktor.server.application.*
|
import io.ktor.server.application.*
|
||||||
import io.ktor.server.plugins.autohead.*
|
import io.ktor.server.plugins.autohead.*
|
||||||
|
@ -23,7 +24,8 @@ fun Application.configureRouting(
|
||||||
userService: IUserService,
|
userService: IUserService,
|
||||||
activityPubUserService: ActivityPubUserService,
|
activityPubUserService: ActivityPubUserService,
|
||||||
postService: IPostApiService,
|
postService: IPostApiService,
|
||||||
userApiService: IUserApiService
|
userApiService: IUserApiService,
|
||||||
|
reactionService: IReactionService
|
||||||
) {
|
) {
|
||||||
install(AutoHeadResponse)
|
install(AutoHeadResponse)
|
||||||
routing {
|
routing {
|
||||||
|
@ -32,7 +34,7 @@ fun Application.configureRouting(
|
||||||
usersAP(activityPubUserService, userService)
|
usersAP(activityPubUserService, userService)
|
||||||
webfinger(userService)
|
webfinger(userService)
|
||||||
route("/api/internal/v1") {
|
route("/api/internal/v1") {
|
||||||
posts(postService)
|
posts(postService, reactionService)
|
||||||
users(userService, userApiService)
|
users(userService, userApiService)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,4 +6,10 @@ interface ReactionRepository {
|
||||||
suspend fun generateId(): Long
|
suspend fun generateId(): Long
|
||||||
suspend fun save(reaction: Reaction): Reaction
|
suspend fun save(reaction: Reaction): Reaction
|
||||||
suspend fun reactionAlreadyExist(postId: Long, userId: Long, emojiId: Long): Boolean
|
suspend fun reactionAlreadyExist(postId: Long, userId: Long, emojiId: Long): Boolean
|
||||||
|
suspend fun findByPostId(postId: Long): List<Reaction>
|
||||||
|
suspend fun delete(reaction: Reaction): Reaction
|
||||||
|
suspend fun deleteById(id: Long)
|
||||||
|
suspend fun deleteByPostId(postId: Long)
|
||||||
|
suspend fun deleteByUserId(userId: Long)
|
||||||
|
suspend fun deleteByPostIdAndUserId(postId: Long, userId: Long)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import dev.usbharu.hideout.service.core.IdGenerateService
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import org.jetbrains.exposed.dao.id.LongIdTable
|
import org.jetbrains.exposed.dao.id.LongIdTable
|
||||||
import org.jetbrains.exposed.sql.*
|
import org.jetbrains.exposed.sql.*
|
||||||
|
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||||
import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction
|
import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction
|
||||||
import org.jetbrains.exposed.sql.transactions.transaction
|
import org.jetbrains.exposed.sql.transactions.transaction
|
||||||
import org.koin.core.annotation.Single
|
import org.koin.core.annotation.Single
|
||||||
|
@ -57,6 +58,50 @@ class ReactionRepositoryImpl(
|
||||||
}.empty().not()
|
}.empty().not()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun findByPostId(postId: Long): List<Reaction> {
|
||||||
|
return query {
|
||||||
|
Reactions.select {
|
||||||
|
Reactions.postId.eq(postId)
|
||||||
|
}.map { it.toReaction() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun delete(reaction: Reaction): Reaction {
|
||||||
|
query {
|
||||||
|
Reactions.deleteWhere {
|
||||||
|
id.eq(reaction.id)
|
||||||
|
.and(postId.eq(reaction.postId))
|
||||||
|
.and(userId.eq(reaction.postId))
|
||||||
|
.and(emojiId.eq(reaction.emojiId))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return reaction
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun deleteById(id: Long) {
|
||||||
|
query {
|
||||||
|
Reactions.deleteWhere { Reactions.id.eq(id) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun deleteByPostId(postId: Long) {
|
||||||
|
query {
|
||||||
|
Reactions.deleteWhere { Reactions.postId.eq(postId) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun deleteByUserId(userId: Long) {
|
||||||
|
query {
|
||||||
|
Reactions.deleteWhere { Reactions.userId.eq(userId) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun deleteByPostIdAndUserId(postId: Long, userId: Long) {
|
||||||
|
query {
|
||||||
|
Reactions.deleteWhere { Reactions.postId.eq(postId).and(Reactions.userId.eq(userId)) }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun ResultRow.toReaction(): Reaction {
|
fun ResultRow.toReaction(): Reaction {
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
package dev.usbharu.hideout.routing.api.internal.v1
|
package dev.usbharu.hideout.routing.api.internal.v1
|
||||||
|
|
||||||
import dev.usbharu.hideout.domain.model.hideout.form.Post
|
import dev.usbharu.hideout.domain.model.hideout.form.Post
|
||||||
|
import dev.usbharu.hideout.domain.model.hideout.form.Reaction
|
||||||
import dev.usbharu.hideout.exception.ParameterNotExistException
|
import dev.usbharu.hideout.exception.ParameterNotExistException
|
||||||
import dev.usbharu.hideout.plugins.TOKEN_AUTH
|
import dev.usbharu.hideout.plugins.TOKEN_AUTH
|
||||||
import dev.usbharu.hideout.service.api.IPostApiService
|
import dev.usbharu.hideout.service.api.IPostApiService
|
||||||
|
import dev.usbharu.hideout.service.reaction.IReactionService
|
||||||
import dev.usbharu.hideout.util.InstantParseUtil
|
import dev.usbharu.hideout.util.InstantParseUtil
|
||||||
import io.ktor.http.*
|
import io.ktor.http.*
|
||||||
import io.ktor.server.application.*
|
import io.ktor.server.application.*
|
||||||
|
@ -14,7 +16,7 @@ import io.ktor.server.response.*
|
||||||
import io.ktor.server.routing.*
|
import io.ktor.server.routing.*
|
||||||
|
|
||||||
@Suppress("LongMethod")
|
@Suppress("LongMethod")
|
||||||
fun Route.posts(postApiService: IPostApiService) {
|
fun Route.posts(postApiService: IPostApiService, reactionService: IReactionService) {
|
||||||
route("/posts") {
|
route("/posts") {
|
||||||
authenticate(TOKEN_AUTH) {
|
authenticate(TOKEN_AUTH) {
|
||||||
post {
|
post {
|
||||||
|
@ -26,6 +28,37 @@ fun Route.posts(postApiService: IPostApiService) {
|
||||||
call.response.header("Location", create.url)
|
call.response.header("Location", create.url)
|
||||||
call.respond(HttpStatusCode.OK)
|
call.respond(HttpStatusCode.OK)
|
||||||
}
|
}
|
||||||
|
route("/{id}/reactions") {
|
||||||
|
get {
|
||||||
|
val principal = call.principal<JWTPrincipal>() ?: throw IllegalStateException("no principal")
|
||||||
|
val userId = principal.payload.getClaim("uid").asLong()
|
||||||
|
val postId = (
|
||||||
|
call.parameters["id"]?.toLong()
|
||||||
|
?: throw ParameterNotExistException("Parameter(id='postsId') does not exist.")
|
||||||
|
)
|
||||||
|
call.respond(reactionService.findByPostIdForUser(postId, userId))
|
||||||
|
}
|
||||||
|
post {
|
||||||
|
val jwtPrincipal = call.principal<JWTPrincipal>() ?: throw IllegalStateException("no principal")
|
||||||
|
val userId = jwtPrincipal.payload.getClaim("uid").asLong()
|
||||||
|
val postId = call.parameters["id"]?.toLong()
|
||||||
|
?: throw ParameterNotExistException("Parameter(id='postsId') does not exist.")
|
||||||
|
val reaction = try {
|
||||||
|
call.receive<Reaction>()
|
||||||
|
} catch (e: ContentTransformationException) {
|
||||||
|
Reaction(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
reactionService.sendReaction(reaction.reaction ?: "❤", userId, postId)
|
||||||
|
}
|
||||||
|
delete {
|
||||||
|
val jwtPrincipal = call.principal<JWTPrincipal>() ?: throw IllegalStateException("no principal")
|
||||||
|
val userId = jwtPrincipal.payload.getClaim("uid").asLong()
|
||||||
|
val postId = call.parameters["id"]?.toLong()
|
||||||
|
?: throw ParameterNotExistException("Parameter(id='postsId') does not exist.")
|
||||||
|
reactionService.removeReaction(userId, postId)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
authenticate(TOKEN_AUTH, optional = true) {
|
authenticate(TOKEN_AUTH, optional = true) {
|
||||||
get {
|
get {
|
||||||
|
|
|
@ -2,9 +2,12 @@ package dev.usbharu.hideout.service.activitypub
|
||||||
|
|
||||||
import dev.usbharu.hideout.domain.model.hideout.entity.Reaction
|
import dev.usbharu.hideout.domain.model.hideout.entity.Reaction
|
||||||
import dev.usbharu.hideout.domain.model.job.DeliverReactionJob
|
import dev.usbharu.hideout.domain.model.job.DeliverReactionJob
|
||||||
|
import dev.usbharu.hideout.domain.model.job.DeliverRemoveReactionJob
|
||||||
import kjob.core.job.JobProps
|
import kjob.core.job.JobProps
|
||||||
|
|
||||||
interface ActivityPubReactionService {
|
interface ActivityPubReactionService {
|
||||||
suspend fun reaction(like: Reaction)
|
suspend fun reaction(like: Reaction)
|
||||||
|
suspend fun removeReaction(like: Reaction)
|
||||||
suspend fun reactionJob(props: JobProps<DeliverReactionJob>)
|
suspend fun reactionJob(props: JobProps<DeliverReactionJob>)
|
||||||
|
suspend fun removeReactionJob(props: JobProps<DeliverRemoveReactionJob>)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
package dev.usbharu.hideout.service.activitypub
|
package dev.usbharu.hideout.service.activitypub
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.module.kotlin.readValue
|
||||||
import dev.usbharu.hideout.config.Config
|
import dev.usbharu.hideout.config.Config
|
||||||
import dev.usbharu.hideout.domain.model.ap.Like
|
import dev.usbharu.hideout.domain.model.ap.Like
|
||||||
|
import dev.usbharu.hideout.domain.model.ap.Undo
|
||||||
import dev.usbharu.hideout.domain.model.hideout.entity.Reaction
|
import dev.usbharu.hideout.domain.model.hideout.entity.Reaction
|
||||||
import dev.usbharu.hideout.domain.model.job.DeliverReactionJob
|
import dev.usbharu.hideout.domain.model.job.DeliverReactionJob
|
||||||
|
import dev.usbharu.hideout.domain.model.job.DeliverRemoveReactionJob
|
||||||
import dev.usbharu.hideout.exception.PostNotFoundException
|
import dev.usbharu.hideout.exception.PostNotFoundException
|
||||||
import dev.usbharu.hideout.plugins.postAp
|
import dev.usbharu.hideout.plugins.postAp
|
||||||
import dev.usbharu.hideout.repository.IPostRepository
|
import dev.usbharu.hideout.repository.IPostRepository
|
||||||
|
@ -12,6 +15,7 @@ import dev.usbharu.hideout.service.user.IUserService
|
||||||
import io.ktor.client.*
|
import io.ktor.client.*
|
||||||
import kjob.core.job.JobProps
|
import kjob.core.job.JobProps
|
||||||
import org.koin.core.annotation.Single
|
import org.koin.core.annotation.Single
|
||||||
|
import java.time.Instant
|
||||||
|
|
||||||
@Single
|
@Single
|
||||||
class ActivityPubReactionServiceImpl(
|
class ActivityPubReactionServiceImpl(
|
||||||
|
@ -36,6 +40,21 @@ class ActivityPubReactionServiceImpl(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun removeReaction(like: Reaction) {
|
||||||
|
val followers = userService.findFollowersById(like.userId)
|
||||||
|
val user = userService.findById(like.userId)
|
||||||
|
val post =
|
||||||
|
iPostRepository.findOneById(like.postId) ?: throw PostNotFoundException("${like.postId} was not found.")
|
||||||
|
followers.forEach { follower ->
|
||||||
|
jobQueueParentService.schedule(DeliverRemoveReactionJob) {
|
||||||
|
props[it.actor] = user.url
|
||||||
|
props[it.inbox] = follower.inbox
|
||||||
|
props[it.id] = post.id.toString()
|
||||||
|
props[it.like] = Config.configData.objectMapper.writeValueAsString(like)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun reactionJob(props: JobProps<DeliverReactionJob>) {
|
override suspend fun reactionJob(props: JobProps<DeliverReactionJob>) {
|
||||||
val inbox = props[DeliverReactionJob.inbox]
|
val inbox = props[DeliverReactionJob.inbox]
|
||||||
val actor = props[DeliverReactionJob.actor]
|
val actor = props[DeliverReactionJob.actor]
|
||||||
|
@ -54,4 +73,21 @@ class ActivityPubReactionServiceImpl(
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun removeReactionJob(props: JobProps<DeliverRemoveReactionJob>) {
|
||||||
|
val inbox = props[DeliverRemoveReactionJob.inbox]
|
||||||
|
val actor = props[DeliverRemoveReactionJob.actor]
|
||||||
|
val like = Config.configData.objectMapper.readValue<Like>(props[DeliverRemoveReactionJob.like])
|
||||||
|
httpClient.postAp(
|
||||||
|
urlString = inbox,
|
||||||
|
username = "$actor#pubkey",
|
||||||
|
jsonLd = Undo(
|
||||||
|
name = "Undo Reaction",
|
||||||
|
actor = actor,
|
||||||
|
`object` = like,
|
||||||
|
id = "${Config.configData.url}/undo/note/${like.id}",
|
||||||
|
published = Instant.now()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,10 +5,7 @@ import com.fasterxml.jackson.module.kotlin.readValue
|
||||||
import dev.usbharu.hideout.config.Config.configData
|
import dev.usbharu.hideout.config.Config.configData
|
||||||
import dev.usbharu.hideout.domain.model.ActivityPubResponse
|
import dev.usbharu.hideout.domain.model.ActivityPubResponse
|
||||||
import dev.usbharu.hideout.domain.model.ap.Follow
|
import dev.usbharu.hideout.domain.model.ap.Follow
|
||||||
import dev.usbharu.hideout.domain.model.job.DeliverPostJob
|
import dev.usbharu.hideout.domain.model.job.*
|
||||||
import dev.usbharu.hideout.domain.model.job.DeliverReactionJob
|
|
||||||
import dev.usbharu.hideout.domain.model.job.HideoutJob
|
|
||||||
import dev.usbharu.hideout.domain.model.job.ReceiveFollowJob
|
|
||||||
import dev.usbharu.hideout.exception.JsonParseException
|
import dev.usbharu.hideout.exception.JsonParseException
|
||||||
import kjob.core.dsl.JobContextWithProps
|
import kjob.core.dsl.JobContextWithProps
|
||||||
import kjob.core.job.JobProps
|
import kjob.core.job.JobProps
|
||||||
|
@ -74,6 +71,9 @@ class ActivityPubServiceImpl(
|
||||||
|
|
||||||
DeliverPostJob -> activityPubNoteService.createNoteJob(job.props as JobProps<DeliverPostJob>)
|
DeliverPostJob -> activityPubNoteService.createNoteJob(job.props as JobProps<DeliverPostJob>)
|
||||||
DeliverReactionJob -> activityPubReactionService.reactionJob(job.props as JobProps<DeliverReactionJob>)
|
DeliverReactionJob -> activityPubReactionService.reactionJob(job.props as JobProps<DeliverReactionJob>)
|
||||||
|
DeliverRemoveReactionJob -> activityPubReactionService.removeReactionJob(
|
||||||
|
job.props as JobProps<DeliverRemoveReactionJob>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
package dev.usbharu.hideout.service.reaction
|
package dev.usbharu.hideout.service.reaction
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.domain.model.hideout.dto.ReactionResponse
|
||||||
|
|
||||||
interface IReactionService {
|
interface IReactionService {
|
||||||
suspend fun receiveReaction(name: String, domain: String, userId: Long, postId: Long)
|
suspend fun receiveReaction(name: String, domain: String, userId: Long, postId: Long)
|
||||||
suspend fun sendReaction(name: String, userId: Long, postId: Long)
|
suspend fun sendReaction(name: String, userId: Long, postId: Long)
|
||||||
|
suspend fun removeReaction(userId: Long, postId: Long)
|
||||||
|
suspend fun findByPostIdForUser(postId: Long, userId: Long): List<ReactionResponse>
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,16 @@
|
||||||
package dev.usbharu.hideout.service.reaction
|
package dev.usbharu.hideout.service.reaction
|
||||||
|
|
||||||
|
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.domain.model.hideout.entity.Reaction
|
||||||
import dev.usbharu.hideout.repository.ReactionRepository
|
import dev.usbharu.hideout.repository.ReactionRepository
|
||||||
|
import dev.usbharu.hideout.repository.Reactions
|
||||||
|
import dev.usbharu.hideout.repository.Users
|
||||||
import dev.usbharu.hideout.service.activitypub.ActivityPubReactionService
|
import dev.usbharu.hideout.service.activitypub.ActivityPubReactionService
|
||||||
|
import org.jetbrains.exposed.sql.ResultRow
|
||||||
|
import org.jetbrains.exposed.sql.leftJoin
|
||||||
|
import org.jetbrains.exposed.sql.select
|
||||||
|
import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction
|
||||||
import org.koin.core.annotation.Single
|
import org.koin.core.annotation.Single
|
||||||
|
|
||||||
@Single
|
@Single
|
||||||
|
@ -21,10 +29,27 @@ class ReactionServiceImpl(
|
||||||
override suspend fun sendReaction(name: String, userId: Long, postId: Long) {
|
override suspend fun sendReaction(name: String, userId: Long, postId: Long) {
|
||||||
if (reactionRepository.reactionAlreadyExist(postId, userId, 0)) {
|
if (reactionRepository.reactionAlreadyExist(postId, userId, 0)) {
|
||||||
// delete
|
// delete
|
||||||
|
reactionRepository.deleteByPostIdAndUserId(postId, userId)
|
||||||
} else {
|
} else {
|
||||||
val reaction = Reaction(reactionRepository.generateId(), 0, postId, userId)
|
val reaction = Reaction(reactionRepository.generateId(), 0, postId, userId)
|
||||||
reactionRepository.save(reaction)
|
reactionRepository.save(reaction)
|
||||||
activityPubReactionService.reaction(reaction)
|
activityPubReactionService.reaction(reaction)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun removeReaction(userId: Long, postId: Long) {
|
||||||
|
reactionRepository.deleteByPostIdAndUserId(postId, userId)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun findByPostIdForUser(postId: Long, userId: Long): List<ReactionResponse> {
|
||||||
|
return newSuspendedTransaction {
|
||||||
|
Reactions
|
||||||
|
.leftJoin(Users, onColumn = { Reactions.userId }, otherColumn = { id })
|
||||||
|
.select { Reactions.postId.eq(postId) }
|
||||||
|
.groupBy { resultRow: ResultRow -> ReactionResponse("❤", true, "", listOf()) }
|
||||||
|
.map { entry: Map.Entry<ReactionResponse, List<ResultRow>> ->
|
||||||
|
entry.key.copy(accounts = entry.value.map { Account(it[Users.screenName], "", it[Users.url]) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,7 +67,7 @@ class PostsTest {
|
||||||
configureSecurity(mock(), mock(), mock(), mock(), mock())
|
configureSecurity(mock(), mock(), mock(), mock(), mock())
|
||||||
routing {
|
routing {
|
||||||
route("/api/internal/v1") {
|
route("/api/internal/v1") {
|
||||||
posts(postService)
|
posts(postService, mock())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -140,7 +140,7 @@ class PostsTest {
|
||||||
configureSerialization()
|
configureSerialization()
|
||||||
routing {
|
routing {
|
||||||
route("/api/internal/v1") {
|
route("/api/internal/v1") {
|
||||||
posts(postService)
|
posts(postService, mock())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -172,7 +172,7 @@ class PostsTest {
|
||||||
configureSecurity(mock(), mock(), mock(), mock(), mock())
|
configureSecurity(mock(), mock(), mock(), mock(), mock())
|
||||||
routing {
|
routing {
|
||||||
route("/api/internal/v1") {
|
route("/api/internal/v1") {
|
||||||
posts(postService)
|
posts(postService, mock())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -215,7 +215,7 @@ class PostsTest {
|
||||||
}
|
}
|
||||||
routing {
|
routing {
|
||||||
route("/api/internal/v1") {
|
route("/api/internal/v1") {
|
||||||
posts(postService)
|
posts(postService, mock())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -264,7 +264,7 @@ class PostsTest {
|
||||||
}
|
}
|
||||||
routing {
|
routing {
|
||||||
route("/api/internal/v1") {
|
route("/api/internal/v1") {
|
||||||
posts(postService)
|
posts(postService, mock())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
configureSerialization()
|
configureSerialization()
|
||||||
|
@ -326,7 +326,7 @@ class PostsTest {
|
||||||
configureSecurity(mock(), mock(), mock(), mock(), mock())
|
configureSecurity(mock(), mock(), mock(), mock(), mock())
|
||||||
routing {
|
routing {
|
||||||
route("/api/internal/v1") {
|
route("/api/internal/v1") {
|
||||||
posts(postService)
|
posts(postService, mock())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -378,7 +378,7 @@ class PostsTest {
|
||||||
configureSecurity(mock(), mock(), mock(), mock(), mock())
|
configureSecurity(mock(), mock(), mock(), mock(), mock())
|
||||||
routing {
|
routing {
|
||||||
route("/api/internal/v1") {
|
route("/api/internal/v1") {
|
||||||
posts(postService)
|
posts(postService, mock())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -430,7 +430,7 @@ class PostsTest {
|
||||||
configureSecurity(mock(), mock(), mock(), mock(), mock())
|
configureSecurity(mock(), mock(), mock(), mock(), mock())
|
||||||
routing {
|
routing {
|
||||||
route("/api/internal/v1") {
|
route("/api/internal/v1") {
|
||||||
posts(postService)
|
posts(postService, mock())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -482,7 +482,7 @@ class PostsTest {
|
||||||
configureSecurity(mock(), mock(), mock(), mock(), mock())
|
configureSecurity(mock(), mock(), mock(), mock(), mock())
|
||||||
routing {
|
routing {
|
||||||
route("/api/internal/v1") {
|
route("/api/internal/v1") {
|
||||||
posts(postService)
|
posts(postService, mock())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -514,7 +514,7 @@ class PostsTest {
|
||||||
configureSecurity(mock(), mock(), mock(), mock(), mock())
|
configureSecurity(mock(), mock(), mock(), mock(), mock())
|
||||||
routing {
|
routing {
|
||||||
route("/api/internal/v1") {
|
route("/api/internal/v1") {
|
||||||
posts(postService)
|
posts(postService, mock())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -546,7 +546,7 @@ class PostsTest {
|
||||||
configureSecurity(mock(), mock(), mock(), mock(), mock())
|
configureSecurity(mock(), mock(), mock(), mock(), mock())
|
||||||
routing {
|
routing {
|
||||||
route("/api/internal/v1") {
|
route("/api/internal/v1") {
|
||||||
posts(postService)
|
posts(postService, mock())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -578,7 +578,7 @@ class PostsTest {
|
||||||
configureSecurity(mock(), mock(), mock(), mock(), mock())
|
configureSecurity(mock(), mock(), mock(), mock(), mock())
|
||||||
routing {
|
routing {
|
||||||
route("/api/internal/v1") {
|
route("/api/internal/v1") {
|
||||||
posts(postService)
|
posts(postService, mock())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -610,7 +610,7 @@ class PostsTest {
|
||||||
configureSecurity(mock(), mock(), mock(), mock(), mock())
|
configureSecurity(mock(), mock(), mock(), mock(), mock())
|
||||||
routing {
|
routing {
|
||||||
route("/api/internal/v1") {
|
route("/api/internal/v1") {
|
||||||
posts(postService)
|
posts(postService, mock())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ import org.mockito.kotlin.*
|
||||||
import utils.JsonObjectMapper
|
import utils.JsonObjectMapper
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
@Suppress("LargeClass")
|
||||||
class UsersTest {
|
class UsersTest {
|
||||||
@Test
|
@Test
|
||||||
fun `users にGETするとユーザー一覧を取得できる`() = testApplication {
|
fun `users にGETするとユーザー一覧を取得できる`() = testApplication {
|
||||||
|
|
Loading…
Reference in New Issue