From 9e3a0193040bdaf348d7ba2cc88348a3df3de1de Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 4 Jun 2023 02:09:01 +0900 Subject: [PATCH 1/4] =?UTF-8?q?feat:=20Like=E3=82=92=E5=8F=97=E3=81=91?= =?UTF-8?q?=E5=8F=96=E3=82=8C=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../usbharu/hideout/domain/model/ap/Emoji.kt | 41 ++++++++++++ .../usbharu/hideout/domain/model/ap/Like.kt | 51 +++++++++++++++ .../domain/model/ap/ObjectDeserializer.kt | 2 +- .../domain/model/hideout/entity/Reaction.kt | 3 + .../hideout/repository/ReactionRepository.kt | 8 +++ .../repository/ReactionRepositoryImpl.kt | 64 +++++++++++++++++++ .../activitypub/ActivityPubLikeService.kt | 8 +++ .../activitypub/ActivityPubLikeServiceImpl.kt | 35 ++++++++++ .../activitypub/ActivityPubServiceImpl.kt | 12 ++-- .../service/reaction/IReactionService.kt | 5 ++ .../service/reaction/ReactionServiceImpl.kt | 18 ++++++ 11 files changed, 241 insertions(+), 6 deletions(-) create mode 100644 src/main/kotlin/dev/usbharu/hideout/domain/model/ap/Emoji.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/domain/model/ap/Like.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/domain/model/hideout/entity/Reaction.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/repository/ReactionRepository.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/repository/ReactionRepositoryImpl.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubLikeService.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubLikeServiceImpl.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/service/reaction/IReactionService.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/service/reaction/ReactionServiceImpl.kt diff --git a/src/main/kotlin/dev/usbharu/hideout/domain/model/ap/Emoji.kt b/src/main/kotlin/dev/usbharu/hideout/domain/model/ap/Emoji.kt new file mode 100644 index 00000000..8012b4fd --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/domain/model/ap/Emoji.kt @@ -0,0 +1,41 @@ +package dev.usbharu.hideout.domain.model.ap + +open class Emoji : Object { + var updated: String? = null + var icon: Image? = null + + protected constructor() : super() + constructor(type: List, + name: String?, + actor: String?, + id: String?, + updated: String?, + icon: Image?) : super( + type = add(type, "Emoji"), + name = name, + actor = actor, + id = id) { + this.updated = updated + this.icon = icon + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is Emoji) return false + if (!super.equals(other)) return false + + if (updated != other.updated) return false + return icon == other.icon + } + + override fun hashCode(): Int { + var result = super.hashCode() + result = 31 * result + (updated?.hashCode() ?: 0) + result = 31 * result + (icon?.hashCode() ?: 0) + return result + } + + override fun toString(): String = "Emoji(updated=$updated, icon=$icon) ${super.toString()}" + + +} diff --git a/src/main/kotlin/dev/usbharu/hideout/domain/model/ap/Like.kt b/src/main/kotlin/dev/usbharu/hideout/domain/model/ap/Like.kt new file mode 100644 index 00000000..68a1aa47 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/domain/model/ap/Like.kt @@ -0,0 +1,51 @@ +package dev.usbharu.hideout.domain.model.ap + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize + +open class Like : Object { + var `object`: String? = null + var content: String? = null + + @JsonDeserialize(contentUsing = ObjectDeserializer::class) + var tag: List = emptyList() + + protected constructor() : super() + constructor(type: List, + name: String?, + actor: String?, + id: String?, + `object`: String?, + content: String?, + tag: List + ) : super( + type = add(type, "Like"), + name = name, + actor = actor, + id = id) { + this.`object` = `object` + this.content = content + this.tag = tag + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is Like) return false + if (!super.equals(other)) return false + + if (`object` != other.`object`) return false + if (content != other.content) return false + return tag == other.tag + } + + override fun hashCode(): Int { + var result = super.hashCode() + result = 31 * result + (`object`?.hashCode() ?: 0) + result = 31 * result + (content?.hashCode() ?: 0) + result = 31 * result + tag.hashCode() + return result + } + + override fun toString(): String = "Like(`object`=$`object`, content=$content, tag=$tag) ${super.toString()}" + + +} diff --git a/src/main/kotlin/dev/usbharu/hideout/domain/model/ap/ObjectDeserializer.kt b/src/main/kotlin/dev/usbharu/hideout/domain/model/ap/ObjectDeserializer.kt index 501682ce..4a4db457 100644 --- a/src/main/kotlin/dev/usbharu/hideout/domain/model/ap/ObjectDeserializer.kt +++ b/src/main/kotlin/dev/usbharu/hideout/domain/model/ap/ObjectDeserializer.kt @@ -42,7 +42,7 @@ class ObjectDeserializer : JsonDeserializer() { } else -> { - TODO() + TODO("$activityType is not implementation") } } } else { diff --git a/src/main/kotlin/dev/usbharu/hideout/domain/model/hideout/entity/Reaction.kt b/src/main/kotlin/dev/usbharu/hideout/domain/model/hideout/entity/Reaction.kt new file mode 100644 index 00000000..af943cf5 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/domain/model/hideout/entity/Reaction.kt @@ -0,0 +1,3 @@ +package dev.usbharu.hideout.domain.model.hideout.entity + +data class Reaction(val id: Long, val emojiId: Long, val postId: Long, val userId: Long) diff --git a/src/main/kotlin/dev/usbharu/hideout/repository/ReactionRepository.kt b/src/main/kotlin/dev/usbharu/hideout/repository/ReactionRepository.kt new file mode 100644 index 00000000..ec973ddf --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/repository/ReactionRepository.kt @@ -0,0 +1,8 @@ +package dev.usbharu.hideout.repository + +import dev.usbharu.hideout.domain.model.hideout.entity.Reaction + +interface ReactionRepository { + suspend fun generateId(): Long + suspend fun save(reaction: Reaction): Reaction +} diff --git a/src/main/kotlin/dev/usbharu/hideout/repository/ReactionRepositoryImpl.kt b/src/main/kotlin/dev/usbharu/hideout/repository/ReactionRepositoryImpl.kt new file mode 100644 index 00000000..7da36956 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/repository/ReactionRepositoryImpl.kt @@ -0,0 +1,64 @@ +package dev.usbharu.hideout.repository + +import dev.usbharu.hideout.domain.model.hideout.entity.Reaction +import dev.usbharu.hideout.service.core.IdGenerateService +import kotlinx.coroutines.Dispatchers +import org.jetbrains.exposed.dao.id.LongIdTable +import org.jetbrains.exposed.sql.* +import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction +import org.jetbrains.exposed.sql.transactions.transaction +import org.koin.core.annotation.Single + +@Single +class ReactionRepositoryImpl(private val database: Database, private val idGenerateService: IdGenerateService) : ReactionRepository { + + init { + transaction(database) { + SchemaUtils.create(Reactions) + SchemaUtils.createMissingTablesAndColumns(Reactions) + } + } + + + @Suppress("InjectDispatcher") + suspend fun query(block: suspend () -> T): T = + newSuspendedTransaction(Dispatchers.IO) { block() } + + override suspend fun generateId(): Long = idGenerateService.generateId() + + override suspend fun save(reaction: Reaction): Reaction { + query { + if (Reactions.select { Reactions.id eq reaction.id }.empty()) { + Reactions.insert { + it[id] = reaction.id + it[emojiId] = reaction.emojiId + it[postId] = reaction.postId + it[userId] = reaction.postId + } + } else { + Reactions.update({ Reactions.id eq reaction.id }) { + it[emojiId] = reaction.emojiId + it[postId] = reaction.postId + it[userId] = reaction.postId + + } + } + } + return reaction + } +} + +fun ResultRow.toReaction(): Reaction { + return Reaction( + this[Reactions.id].value, + this[Reactions.emojiId], + this[Reactions.postId], + this[Reactions.userId] + ) +} + +object Reactions : LongIdTable("reactions") { + val emojiId = long("emoji_id") + val postId = long("post_id").references(Posts.id) + val userId = long("user_id").references(Users.id) +} diff --git a/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubLikeService.kt b/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubLikeService.kt new file mode 100644 index 00000000..19d3f341 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubLikeService.kt @@ -0,0 +1,8 @@ +package dev.usbharu.hideout.service.activitypub + +import dev.usbharu.hideout.domain.model.ActivityPubResponse +import dev.usbharu.hideout.domain.model.ap.Like + +interface ActivityPubLikeService { + suspend fun receiveLike(like: Like): ActivityPubResponse +} diff --git a/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubLikeServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubLikeServiceImpl.kt new file mode 100644 index 00000000..5c1ba744 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubLikeServiceImpl.kt @@ -0,0 +1,35 @@ +package dev.usbharu.hideout.service.activitypub + +import dev.usbharu.hideout.domain.model.ActivityPubResponse +import dev.usbharu.hideout.domain.model.ActivityPubStringResponse +import dev.usbharu.hideout.domain.model.ap.Like +import dev.usbharu.hideout.exception.PostNotFoundException +import dev.usbharu.hideout.exception.ap.IllegalActivityPubObjectException +import dev.usbharu.hideout.repository.IPostRepository +import dev.usbharu.hideout.service.reaction.IReactionService +import dev.usbharu.hideout.service.user.IUserService +import io.ktor.http.* +import org.koin.core.annotation.Single + + +@Single +class ActivityPubLikeServiceImpl(private val reactionService: IReactionService, + private val activityPubUserService: ActivityPubUserService, + private val userService: IUserService, + private val postService: IPostRepository) : ActivityPubLikeService { + override suspend fun receiveLike(like: Like): ActivityPubResponse { + val actor = like.actor ?: throw IllegalActivityPubObjectException("actor is null") + val content = like.content ?: throw IllegalActivityPubObjectException("content is null") + like.`object` ?: throw IllegalActivityPubObjectException("object is null") + val person = activityPubUserService.fetchPerson(actor) + + val user = userService.findByUrl(person.url + ?: throw IllegalActivityPubObjectException("actor is not found")) + + val post = postService.findByUrl(like.`object`!!) + ?: throw PostNotFoundException("${like.`object`} was not found") + + reactionService.receiveReaction(content, actor.substringAfter("://").substringBefore("/"), user.id, post.id) + return ActivityPubStringResponse(HttpStatusCode.OK, "") + } +} diff --git a/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubServiceImpl.kt index 058fbede..b153ad19 100644 --- a/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubServiceImpl.kt @@ -17,11 +17,12 @@ import org.slf4j.LoggerFactory @Single class ActivityPubServiceImpl( - private val activityPubReceiveFollowService: ActivityPubReceiveFollowService, - private val activityPubNoteService: ActivityPubNoteService, - private val activityPubUndoService: ActivityPubUndoService, - private val activityPubAcceptService: ActivityPubAcceptService, - private val activityPubCreateService: ActivityPubCreateService + private val activityPubReceiveFollowService: ActivityPubReceiveFollowService, + private val activityPubNoteService: ActivityPubNoteService, + private val activityPubUndoService: ActivityPubUndoService, + private val activityPubAcceptService: ActivityPubAcceptService, + private val activityPubCreateService: ActivityPubCreateService, + private val activityPubLikeService: ActivityPubLikeService ) : ActivityPubService { val logger: Logger = LoggerFactory.getLogger(this::class.java) @@ -53,6 +54,7 @@ class ActivityPubServiceImpl( ) ActivityType.Create -> activityPubCreateService.receiveCreate(configData.objectMapper.readValue(json)) + ActivityType.Like -> activityPubLikeService.receiveLike(configData.objectMapper.readValue(json)) ActivityType.Undo -> activityPubUndoService.receiveUndo(configData.objectMapper.readValue(json)) else -> { diff --git a/src/main/kotlin/dev/usbharu/hideout/service/reaction/IReactionService.kt b/src/main/kotlin/dev/usbharu/hideout/service/reaction/IReactionService.kt new file mode 100644 index 00000000..13c52f88 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/service/reaction/IReactionService.kt @@ -0,0 +1,5 @@ +package dev.usbharu.hideout.service.reaction + +interface IReactionService { + suspend fun receiveReaction(name: String, domain: String, userId: Long, postId: Long) +} diff --git a/src/main/kotlin/dev/usbharu/hideout/service/reaction/ReactionServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/service/reaction/ReactionServiceImpl.kt new file mode 100644 index 00000000..1b89e5dc --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/service/reaction/ReactionServiceImpl.kt @@ -0,0 +1,18 @@ +package dev.usbharu.hideout.service.reaction + +import dev.usbharu.hideout.domain.model.hideout.entity.Reaction +import dev.usbharu.hideout.repository.ReactionRepository +import org.koin.core.annotation.Single + +@Single +class ReactionServiceImpl(private val reactionRepository: ReactionRepository) : IReactionService { + override suspend fun receiveReaction(name: String, domain: String, userId: Long, postId: Long) { + reactionRepository.save( + Reaction( + reactionRepository.generateId(), + 0, + postId, userId + ) + ) + } +} From e800fa15937d1f96d2f8a02c64c68e02518af7d3 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 4 Jun 2023 12:32:40 +0900 Subject: [PATCH 2/4] =?UTF-8?q?feat:=20=E3=83=8E=E3=83=BC=E3=83=88?= =?UTF-8?q?=E3=81=8C=E6=AD=A3=E5=B8=B8=E3=81=AB=E5=8F=96=E5=BE=97=E3=81=A7?= =?UTF-8?q?=E3=81=8D=E3=81=AA=E3=81=8F=E3=81=AA=E3=81=A3=E3=81=A6=E3=81=84?= =?UTF-8?q?=E3=81=9F=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/dev/usbharu/hideout/Application.kt | 1 + .../domain/model/ap/ObjectDeserializer.kt | 66 ++++++++++++++++--- .../usbharu/hideout/plugins/ActivityPub.kt | 6 +- .../hideout/repository/PostRepositoryImpl.kt | 4 +- .../hideout/repository/ReactionRepository.kt | 1 + .../repository/ReactionRepositoryImpl.kt | 14 +++- .../activitypub/ActivityPubLikeServiceImpl.kt | 4 +- .../activitypub/ActivityPubNoteServiceImpl.kt | 7 +- .../service/activitypub/ActivityPubService.kt | 62 +++++++++++++++++ .../service/reaction/ReactionServiceImpl.kt | 12 ++-- 10 files changed, 152 insertions(+), 25 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/Application.kt b/src/main/kotlin/dev/usbharu/hideout/Application.kt index 254d7a59..aec8f304 100644 --- a/src/main/kotlin/dev/usbharu/hideout/Application.kt +++ b/src/main/kotlin/dev/usbharu/hideout/Application.kt @@ -78,6 +78,7 @@ fun Application.parent() { install(httpSignaturePlugin) { keyMap = KtorKeyMap(get()) } + expectSuccess = true } } single { TwitterSnowflakeIdGenerateService } diff --git a/src/main/kotlin/dev/usbharu/hideout/domain/model/ap/ObjectDeserializer.kt b/src/main/kotlin/dev/usbharu/hideout/domain/model/ap/ObjectDeserializer.kt index 4a4db457..ba2f9868 100644 --- a/src/main/kotlin/dev/usbharu/hideout/domain/model/ap/ObjectDeserializer.kt +++ b/src/main/kotlin/dev/usbharu/hideout/domain/model/ap/ObjectDeserializer.kt @@ -4,7 +4,7 @@ import com.fasterxml.jackson.core.JsonParser import com.fasterxml.jackson.databind.DeserializationContext import com.fasterxml.jackson.databind.JsonDeserializer import com.fasterxml.jackson.databind.JsonNode -import dev.usbharu.hideout.service.activitypub.ActivityVocabulary +import dev.usbharu.hideout.service.activitypub.ExtendedActivityVocabulary class ObjectDeserializer : JsonDeserializer() { override fun deserialize(p: JsonParser?, ctxt: DeserializationContext?): Object { @@ -22,28 +22,78 @@ class ObjectDeserializer : JsonDeserializer() { val type = treeNode["type"] val activityType = if (type.isArray) { type.firstNotNullOf { jsonNode: JsonNode -> - ActivityVocabulary.values().firstOrNull { it.name.equals(jsonNode.asText(), true) } + ExtendedActivityVocabulary.values().firstOrNull { it.name.equals(jsonNode.asText(), true) } } } else if (type.isValueNode) { - ActivityVocabulary.values().first { it.name.equals(type.asText(), true) } + ExtendedActivityVocabulary.values().first { it.name.equals(type.asText(), true) } } else { TODO() } return when (activityType) { - ActivityVocabulary.Follow -> { + ExtendedActivityVocabulary.Follow -> { val readValue = p.codec.treeToValue(treeNode, Follow::class.java) println(readValue) readValue } - ActivityVocabulary.Note -> { + ExtendedActivityVocabulary.Note -> { p.codec.treeToValue(treeNode, Note::class.java) } - else -> { - TODO("$activityType is not implementation") - } + ExtendedActivityVocabulary.Object -> p.codec.treeToValue(treeNode, Object::class.java) + ExtendedActivityVocabulary.Link -> TODO() + ExtendedActivityVocabulary.Activity -> TODO() + ExtendedActivityVocabulary.IntransitiveActivity -> TODO() + ExtendedActivityVocabulary.Collection -> TODO() + ExtendedActivityVocabulary.OrderedCollection -> TODO() + ExtendedActivityVocabulary.CollectionPage -> TODO() + ExtendedActivityVocabulary.OrderedCollectionPage -> TODO() + ExtendedActivityVocabulary.Accept -> p.codec.treeToValue(treeNode, Accept::class.java) + ExtendedActivityVocabulary.Add -> TODO() + ExtendedActivityVocabulary.Announce -> TODO() + ExtendedActivityVocabulary.Arrive -> TODO() + ExtendedActivityVocabulary.Block -> TODO() + ExtendedActivityVocabulary.Create -> p.codec.treeToValue(treeNode, Create::class.java) + ExtendedActivityVocabulary.Delete -> TODO() + ExtendedActivityVocabulary.Dislike -> TODO() + ExtendedActivityVocabulary.Flag -> TODO() + ExtendedActivityVocabulary.Ignore -> TODO() + ExtendedActivityVocabulary.Invite -> TODO() + ExtendedActivityVocabulary.Join -> TODO() + ExtendedActivityVocabulary.Leave -> TODO() + ExtendedActivityVocabulary.Like -> p.codec.treeToValue(treeNode, Like::class.java) + ExtendedActivityVocabulary.Listen -> TODO() + ExtendedActivityVocabulary.Move -> TODO() + ExtendedActivityVocabulary.Offer -> TODO() + ExtendedActivityVocabulary.Question -> TODO() + ExtendedActivityVocabulary.Reject -> TODO() + ExtendedActivityVocabulary.Read -> TODO() + ExtendedActivityVocabulary.Remove -> TODO() + ExtendedActivityVocabulary.TentativeReject -> TODO() + ExtendedActivityVocabulary.TentativeAccept -> TODO() + ExtendedActivityVocabulary.Travel -> TODO() + ExtendedActivityVocabulary.Undo -> p.codec.treeToValue(treeNode, Undo::class.java) + ExtendedActivityVocabulary.Update -> TODO() + ExtendedActivityVocabulary.View -> TODO() + ExtendedActivityVocabulary.Application -> TODO() + ExtendedActivityVocabulary.Group -> TODO() + ExtendedActivityVocabulary.Organization -> TODO() + ExtendedActivityVocabulary.Person -> p.codec.treeToValue(treeNode, Person::class.java) + ExtendedActivityVocabulary.Service -> TODO() + ExtendedActivityVocabulary.Article -> TODO() + ExtendedActivityVocabulary.Audio -> TODO() + ExtendedActivityVocabulary.Document -> TODO() + ExtendedActivityVocabulary.Event -> TODO() + ExtendedActivityVocabulary.Image -> p.codec.treeToValue(treeNode, Image::class.java) + ExtendedActivityVocabulary.Page -> TODO() + ExtendedActivityVocabulary.Place -> TODO() + ExtendedActivityVocabulary.Profile -> TODO() + ExtendedActivityVocabulary.Relationship -> TODO() + ExtendedActivityVocabulary.Tombstone -> TODO() + ExtendedActivityVocabulary.Video -> TODO() + ExtendedActivityVocabulary.Mention -> TODO() + ExtendedActivityVocabulary.Emoji -> p.codec.treeToValue(treeNode, Emoji::class.java) } } else { TODO() diff --git a/src/main/kotlin/dev/usbharu/hideout/plugins/ActivityPub.kt b/src/main/kotlin/dev/usbharu/hideout/plugins/ActivityPub.kt index f2eb9f85..cbaec3a5 100644 --- a/src/main/kotlin/dev/usbharu/hideout/plugins/ActivityPub.kt +++ b/src/main/kotlin/dev/usbharu/hideout/plugins/ActivityPub.kt @@ -44,10 +44,12 @@ suspend fun HttpClient.postAp(urlString: String, username: String, jsonLd: JsonL } } -suspend fun HttpClient.getAp(urlString: String, username: String): HttpResponse { +suspend fun HttpClient.getAp(urlString: String, username: String?): HttpResponse { return this.get(urlString) { header("Accept", ContentType.Application.Activity) - header("Signature", "keyId=\"$username\",algorithm=\"rsa-sha256\",headers=\"(request-target) host date\"") + username?.let { + header("Signature", "keyId=\"$username\",algorithm=\"rsa-sha256\",headers=\"(request-target) host date\"") + } } } diff --git a/src/main/kotlin/dev/usbharu/hideout/repository/PostRepositoryImpl.kt b/src/main/kotlin/dev/usbharu/hideout/repository/PostRepositoryImpl.kt index 859bbd66..a92889c1 100644 --- a/src/main/kotlin/dev/usbharu/hideout/repository/PostRepositoryImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/repository/PostRepositoryImpl.kt @@ -63,7 +63,9 @@ class PostRepositoryImpl(database: Database, private val idGenerateService: IdGe } override suspend fun findOneById(id: Long, userId: Long?): Post? { - TODO("Not yet implemented") + return query { + Posts.select { Posts.id eq id }.singleOrNull()?.toPost() + } } override suspend fun findByUrl(url: String): Post? { diff --git a/src/main/kotlin/dev/usbharu/hideout/repository/ReactionRepository.kt b/src/main/kotlin/dev/usbharu/hideout/repository/ReactionRepository.kt index ec973ddf..f1c6a540 100644 --- a/src/main/kotlin/dev/usbharu/hideout/repository/ReactionRepository.kt +++ b/src/main/kotlin/dev/usbharu/hideout/repository/ReactionRepository.kt @@ -5,4 +5,5 @@ import dev.usbharu.hideout.domain.model.hideout.entity.Reaction interface ReactionRepository { suspend fun generateId(): Long suspend fun save(reaction: Reaction): Reaction + suspend fun reactionAlreadyExist(postId: Long, userId: Long, emojiId: Long): Boolean } diff --git a/src/main/kotlin/dev/usbharu/hideout/repository/ReactionRepositoryImpl.kt b/src/main/kotlin/dev/usbharu/hideout/repository/ReactionRepositoryImpl.kt index 7da36956..bb87ec37 100644 --- a/src/main/kotlin/dev/usbharu/hideout/repository/ReactionRepositoryImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/repository/ReactionRepositoryImpl.kt @@ -33,19 +33,25 @@ class ReactionRepositoryImpl(private val database: Database, private val idGener it[id] = reaction.id it[emojiId] = reaction.emojiId it[postId] = reaction.postId - it[userId] = reaction.postId + it[userId] = reaction.userId } } else { Reactions.update({ Reactions.id eq reaction.id }) { it[emojiId] = reaction.emojiId it[postId] = reaction.postId - it[userId] = reaction.postId + it[userId] = reaction.userId } } } return reaction } + + override suspend fun reactionAlreadyExist(postId: Long, userId: Long, emojiId: Long): Boolean { + return query { + Reactions.select { Reactions.postId.eq(postId).and(Reactions.userId.eq(userId)).and(Reactions.emojiId.eq(emojiId)) }.empty().not() + } + } } fun ResultRow.toReaction(): Reaction { @@ -61,4 +67,8 @@ object Reactions : LongIdTable("reactions") { val emojiId = long("emoji_id") val postId = long("post_id").references(Posts.id) val userId = long("user_id").references(Users.id) + + init { + uniqueIndex(emojiId, postId, userId) + } } diff --git a/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubLikeServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubLikeServiceImpl.kt index 5c1ba744..8b006df2 100644 --- a/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubLikeServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubLikeServiceImpl.kt @@ -16,12 +16,14 @@ import org.koin.core.annotation.Single class ActivityPubLikeServiceImpl(private val reactionService: IReactionService, private val activityPubUserService: ActivityPubUserService, private val userService: IUserService, - private val postService: IPostRepository) : ActivityPubLikeService { + private val postService: IPostRepository, + private val activityPubNoteService: ActivityPubNoteService) : ActivityPubLikeService { override suspend fun receiveLike(like: Like): ActivityPubResponse { val actor = like.actor ?: throw IllegalActivityPubObjectException("actor is null") val content = like.content ?: throw IllegalActivityPubObjectException("content is null") like.`object` ?: throw IllegalActivityPubObjectException("object is null") val person = activityPubUserService.fetchPerson(actor) + activityPubNoteService.fetchNote(like.`object`!!) val user = userService.findByUrl(person.url ?: throw IllegalActivityPubObjectException("actor is not found")) diff --git a/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubNoteServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubNoteServiceImpl.kt index 4ccd223f..117b207a 100644 --- a/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubNoteServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubNoteServiceImpl.kt @@ -14,7 +14,7 @@ import dev.usbharu.hideout.repository.IPostRepository import dev.usbharu.hideout.service.job.JobQueueParentService import dev.usbharu.hideout.service.user.IUserService import io.ktor.client.* -import io.ktor.client.call.* +import io.ktor.client.statement.* import kjob.core.job.JobProps import org.koin.core.annotation.Single import org.slf4j.LoggerFactory @@ -75,10 +75,9 @@ class ActivityPubNoteServiceImpl( return postToNote(post) } val response = httpClient.getAp( - url, - "$targetActor#pubkey" + url, targetActor?.let { "$targetActor#pubkey" } ) - val note = response.body() + val note = Config.configData.objectMapper.readValue(response.bodyAsText()) return note(note, targetActor, url) } diff --git a/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubService.kt b/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubService.kt index f61e825d..bec7f2fb 100644 --- a/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubService.kt @@ -100,3 +100,65 @@ enum class ActivityVocabulary { Video, Mention, } + +enum class ExtendedActivityVocabulary { + Object, + Link, + Activity, + IntransitiveActivity, + Collection, + OrderedCollection, + CollectionPage, + OrderedCollectionPage, + Accept, + Add, + Announce, + Arrive, + Block, + Create, + Delete, + Dislike, + Flag, + Follow, + Ignore, + Invite, + Join, + Leave, + Like, + Listen, + Move, + Offer, + Question, + Reject, + Read, + Remove, + TentativeReject, + TentativeAccept, + Travel, + Undo, + Update, + View, + Application, + Group, + Organization, + Person, + Service, + Article, + Audio, + Document, + Event, + Image, + Note, + Page, + Place, + Profile, + Relationship, + Tombstone, + Video, + Mention, + Emoji +} + +enum class ExtendedVocabulary { + Emoji +} diff --git a/src/main/kotlin/dev/usbharu/hideout/service/reaction/ReactionServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/service/reaction/ReactionServiceImpl.kt index 1b89e5dc..89c039f1 100644 --- a/src/main/kotlin/dev/usbharu/hideout/service/reaction/ReactionServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/service/reaction/ReactionServiceImpl.kt @@ -7,12 +7,10 @@ import org.koin.core.annotation.Single @Single class ReactionServiceImpl(private val reactionRepository: ReactionRepository) : IReactionService { override suspend fun receiveReaction(name: String, domain: String, userId: Long, postId: Long) { - reactionRepository.save( - Reaction( - reactionRepository.generateId(), - 0, - postId, userId - ) - ) + if (reactionRepository.reactionAlreadyExist(postId, userId, 0).not()) { + reactionRepository.save( + Reaction(reactionRepository.generateId(), 0, postId, userId) + ) + } } } From e8965f798a402afb18126ec9bbbe42fc08e8538b Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Thu, 8 Jun 2023 22:29:30 +0900 Subject: [PATCH 3/4] =?UTF-8?q?fix:=20=E3=83=89=E3=83=A1=E3=82=A4=E3=83=B3?= =?UTF-8?q?=E5=8F=96=E5=BE=97=E6=96=B9=E6=B3=95=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hideout/service/activitypub/ActivityPubUserServiceImpl.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubUserServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubUserServiceImpl.kt index 6c05fddf..5df6cb1c 100644 --- a/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubUserServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubUserServiceImpl.kt @@ -93,7 +93,7 @@ class ActivityPubUserServiceImpl( RemoteUserCreateDto( name = person.preferredUsername ?: throw IllegalActivityPubObjectException("preferredUsername is null"), - domain = url.substringAfter("://").substringBeforeLast("/"), + domain = url.substringAfter("://").substringBefore("/"), screenName = (person.name ?: person.preferredUsername) ?: throw IllegalActivityPubObjectException("preferredUsername is null"), description = person.summary.orEmpty(), From 0cb58002eb512424a3789a0a15170c8e21d402f5 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Thu, 8 Jun 2023 22:54:37 +0900 Subject: [PATCH 4/4] =?UTF-8?q?style:=20=E3=82=B9=E3=82=BF=E3=82=A4?= =?UTF-8?q?=E3=83=AB=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/dev/usbharu/hideout/Application.kt | 12 +- .../usbharu/hideout/domain/model/ap/Emoji.kt | 25 +- .../usbharu/hideout/domain/model/ap/Like.kt | 26 +- .../dev/usbharu/hideout/plugins/Routing.kt | 12 +- .../hideout/repository/IPostRepository.kt | 42 +-- .../hideout/repository/PostRepositoryImpl.kt | 64 ++--- .../repository/ReactionRepositoryImpl.kt | 23 +- .../hideout/routing/api/internal/v1/Posts.kt | 4 +- .../hideout/routing/api/internal/v1/Users.kt | 12 +- .../activitypub/ActivityPubLikeServiceImpl.kt | 21 +- .../activitypub/ActivityPubNoteServiceImpl.kt | 117 ++++----- .../activitypub/ActivityPubServiceImpl.kt | 12 +- .../hideout/service/api/IPostApiService.kt | 27 +- .../hideout/service/api/PostApiServiceImpl.kt | 66 ++--- .../hideout/service/post/PostServiceImpl.kt | 24 +- .../service/reaction/ReactionServiceImpl.kt | 2 +- .../routing/api/internal/v1/PostsTest.kt | 246 +++++++++--------- 17 files changed, 373 insertions(+), 362 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/Application.kt b/src/main/kotlin/dev/usbharu/hideout/Application.kt index aec8f304..5052c79c 100644 --- a/src/main/kotlin/dev/usbharu/hideout/Application.kt +++ b/src/main/kotlin/dev/usbharu/hideout/Application.kt @@ -108,12 +108,12 @@ fun Application.parent() { inject().value, ) configureRouting( - httpSignatureVerifyService = inject().value, - activityPubService = inject().value, - userService = inject().value, - activityPubUserService = inject().value, - postService = inject().value, - userApiService = inject().value, + httpSignatureVerifyService = inject().value, + activityPubService = inject().value, + userService = inject().value, + activityPubUserService = inject().value, + postService = inject().value, + userApiService = inject().value, ) } diff --git a/src/main/kotlin/dev/usbharu/hideout/domain/model/ap/Emoji.kt b/src/main/kotlin/dev/usbharu/hideout/domain/model/ap/Emoji.kt index 8012b4fd..35806687 100644 --- a/src/main/kotlin/dev/usbharu/hideout/domain/model/ap/Emoji.kt +++ b/src/main/kotlin/dev/usbharu/hideout/domain/model/ap/Emoji.kt @@ -5,16 +5,19 @@ open class Emoji : Object { var icon: Image? = null protected constructor() : super() - constructor(type: List, - name: String?, - actor: String?, - id: String?, - updated: String?, - icon: Image?) : super( - type = add(type, "Emoji"), - name = name, - actor = actor, - id = id) { + constructor( + type: List, + name: String?, + actor: String?, + id: String?, + updated: String?, + icon: Image? + ) : super( + type = add(type, "Emoji"), + name = name, + actor = actor, + id = id + ) { this.updated = updated this.icon = icon } @@ -36,6 +39,4 @@ open class Emoji : Object { } override fun toString(): String = "Emoji(updated=$updated, icon=$icon) ${super.toString()}" - - } diff --git a/src/main/kotlin/dev/usbharu/hideout/domain/model/ap/Like.kt b/src/main/kotlin/dev/usbharu/hideout/domain/model/ap/Like.kt index 68a1aa47..c4eb3abf 100644 --- a/src/main/kotlin/dev/usbharu/hideout/domain/model/ap/Like.kt +++ b/src/main/kotlin/dev/usbharu/hideout/domain/model/ap/Like.kt @@ -10,18 +10,20 @@ open class Like : Object { var tag: List = emptyList() protected constructor() : super() - constructor(type: List, - name: String?, - actor: String?, - id: String?, - `object`: String?, - content: String?, - tag: List + constructor( + type: List, + name: String?, + actor: String?, + id: String?, + `object`: String?, + content: String?, + tag: List ) : super( - type = add(type, "Like"), - name = name, - actor = actor, - id = id) { + type = add(type, "Like"), + name = name, + actor = actor, + id = id + ) { this.`object` = `object` this.content = content this.tag = tag @@ -46,6 +48,4 @@ open class Like : Object { } override fun toString(): String = "Like(`object`=$`object`, content=$content, tag=$tag) ${super.toString()}" - - } diff --git a/src/main/kotlin/dev/usbharu/hideout/plugins/Routing.kt b/src/main/kotlin/dev/usbharu/hideout/plugins/Routing.kt index 6a52f394..5931ad53 100644 --- a/src/main/kotlin/dev/usbharu/hideout/plugins/Routing.kt +++ b/src/main/kotlin/dev/usbharu/hideout/plugins/Routing.kt @@ -18,12 +18,12 @@ import io.ktor.server.routing.* @Suppress("LongParameterList") fun Application.configureRouting( - httpSignatureVerifyService: HttpSignatureVerifyService, - activityPubService: ActivityPubService, - userService: IUserService, - activityPubUserService: ActivityPubUserService, - postService: IPostApiService, - userApiService: IUserApiService + httpSignatureVerifyService: HttpSignatureVerifyService, + activityPubService: ActivityPubService, + userService: IUserService, + activityPubUserService: ActivityPubUserService, + postService: IPostApiService, + userApiService: IUserApiService ) { install(AutoHeadResponse) routing { diff --git a/src/main/kotlin/dev/usbharu/hideout/repository/IPostRepository.kt b/src/main/kotlin/dev/usbharu/hideout/repository/IPostRepository.kt index 8887853e..39f91ad1 100644 --- a/src/main/kotlin/dev/usbharu/hideout/repository/IPostRepository.kt +++ b/src/main/kotlin/dev/usbharu/hideout/repository/IPostRepository.kt @@ -11,33 +11,33 @@ interface IPostRepository { suspend fun findByUrl(url: String): Post? suspend fun delete(id: Long) suspend fun findAll( - since: Instant?, - until: Instant?, - minId: Long?, - maxId: Long?, - limit: Int?, - userId: Long? + since: Instant?, + until: Instant?, + minId: Long?, + maxId: Long?, + limit: Int?, + userId: Long? ): List suspend fun findByUserNameAndDomain( - username: String, - s: String, - since: Instant?, - until: Instant?, - minId: Long?, - maxId: Long?, - limit: Int?, - userId: Long? + username: String, + s: String, + since: Instant?, + until: Instant?, + minId: Long?, + maxId: Long?, + limit: Int?, + userId: Long? ): List suspend fun findByUserId( - idOrNull: Long, - since: Instant?, - until: Instant?, - minId: Long?, - maxId: Long?, - limit: Int?, - userId: Long? + idOrNull: Long, + since: Instant?, + until: Instant?, + minId: Long?, + maxId: Long?, + limit: Int?, + userId: Long? ): List suspend fun findByApId(id: String): Post? diff --git a/src/main/kotlin/dev/usbharu/hideout/repository/PostRepositoryImpl.kt b/src/main/kotlin/dev/usbharu/hideout/repository/PostRepositoryImpl.kt index a92889c1..6360e609 100644 --- a/src/main/kotlin/dev/usbharu/hideout/repository/PostRepositoryImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/repository/PostRepositoryImpl.kt @@ -81,37 +81,37 @@ class PostRepositoryImpl(database: Database, private val idGenerateService: IdGe } override suspend fun findAll( - since: Instant?, - until: Instant?, - minId: Long?, - maxId: Long?, - limit: Int?, - userId: Long? + since: Instant?, + until: Instant?, + minId: Long?, + maxId: Long?, + limit: Int?, + userId: Long? ): List { TODO("Not yet implemented") } override suspend fun findByUserNameAndDomain( - username: String, - s: String, - since: Instant?, - until: Instant?, - minId: Long?, - maxId: Long?, - limit: Int?, - userId: Long? + username: String, + s: String, + since: Instant?, + until: Instant?, + minId: Long?, + maxId: Long?, + limit: Int?, + userId: Long? ): List { TODO("Not yet implemented") } override suspend fun findByUserId( - idOrNull: Long, - since: Instant?, - until: Instant?, - minId: Long?, - maxId: Long?, - limit: Int?, - userId: Long? + idOrNull: Long, + since: Instant?, + until: Instant?, + minId: Long?, + maxId: Long?, + limit: Int?, + userId: Long? ): List { TODO("Not yet implemented") } @@ -140,16 +140,16 @@ object Posts : Table() { fun ResultRow.toPost(): Post { return Post( - id = this[Posts.id], - userId = this[Posts.userId], - overview = this[Posts.overview], - text = this[Posts.text], - createdAt = this[Posts.createdAt], - visibility = Visibility.values().first { visibility -> visibility.ordinal == this[Posts.visibility] }, - url = this[Posts.url], - repostId = this[Posts.repostId], - replyId = this[Posts.replyId], - sensitive = this[Posts.sensitive], - apId = this[Posts.apId] + id = this[Posts.id], + userId = this[Posts.userId], + overview = this[Posts.overview], + text = this[Posts.text], + createdAt = this[Posts.createdAt], + visibility = Visibility.values().first { visibility -> visibility.ordinal == this[Posts.visibility] }, + url = this[Posts.url], + repostId = this[Posts.repostId], + replyId = this[Posts.replyId], + sensitive = this[Posts.sensitive], + apId = this[Posts.apId] ) } diff --git a/src/main/kotlin/dev/usbharu/hideout/repository/ReactionRepositoryImpl.kt b/src/main/kotlin/dev/usbharu/hideout/repository/ReactionRepositoryImpl.kt index bb87ec37..0349b8b2 100644 --- a/src/main/kotlin/dev/usbharu/hideout/repository/ReactionRepositoryImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/repository/ReactionRepositoryImpl.kt @@ -10,7 +10,10 @@ import org.jetbrains.exposed.sql.transactions.transaction import org.koin.core.annotation.Single @Single -class ReactionRepositoryImpl(private val database: Database, private val idGenerateService: IdGenerateService) : ReactionRepository { +class ReactionRepositoryImpl( + private val database: Database, + private val idGenerateService: IdGenerateService +) : ReactionRepository { init { transaction(database) { @@ -19,10 +22,9 @@ class ReactionRepositoryImpl(private val database: Database, private val idGener } } - @Suppress("InjectDispatcher") suspend fun query(block: suspend () -> T): T = - newSuspendedTransaction(Dispatchers.IO) { block() } + newSuspendedTransaction(Dispatchers.IO) { block() } override suspend fun generateId(): Long = idGenerateService.generateId() @@ -40,7 +42,6 @@ class ReactionRepositoryImpl(private val database: Database, private val idGener it[emojiId] = reaction.emojiId it[postId] = reaction.postId it[userId] = reaction.userId - } } } @@ -49,17 +50,21 @@ class ReactionRepositoryImpl(private val database: Database, private val idGener override suspend fun reactionAlreadyExist(postId: Long, userId: Long, emojiId: Long): Boolean { return query { - Reactions.select { Reactions.postId.eq(postId).and(Reactions.userId.eq(userId)).and(Reactions.emojiId.eq(emojiId)) }.empty().not() + Reactions.select { + Reactions.postId.eq(postId).and(Reactions.userId.eq(userId)).and( + Reactions.emojiId.eq(emojiId) + ) + }.empty().not() } } } fun ResultRow.toReaction(): Reaction { return Reaction( - this[Reactions.id].value, - this[Reactions.emojiId], - this[Reactions.postId], - this[Reactions.userId] + this[Reactions.id].value, + this[Reactions.emojiId], + this[Reactions.postId], + this[Reactions.userId] ) } diff --git a/src/main/kotlin/dev/usbharu/hideout/routing/api/internal/v1/Posts.kt b/src/main/kotlin/dev/usbharu/hideout/routing/api/internal/v1/Posts.kt index 2312d575..55078b69 100644 --- a/src/main/kotlin/dev/usbharu/hideout/routing/api/internal/v1/Posts.kt +++ b/src/main/kotlin/dev/usbharu/hideout/routing/api/internal/v1/Posts.kt @@ -51,14 +51,14 @@ fun Route.posts(postApiService: IPostApiService) { get { val userId = call.principal()?.payload?.getClaim("uid")?.asLong() val targetUserName = call.parameters["name"] - ?: throw ParameterNotExistException("Parameter(name='userName@domain') does not exist.") + ?: throw ParameterNotExistException("Parameter(name='userName@domain') does not exist.") val posts = postApiService.getByUser(targetUserName, userId = userId) call.respond(posts) } get("/{id}") { val userId = call.principal()?.payload?.getClaim("uid")?.asLong() val id = call.parameters["id"]?.toLong() - ?: throw ParameterNotExistException("Parameter(name='postsId' does not exist.") + ?: throw ParameterNotExistException("Parameter(name='postsId' does not exist.") val post = postApiService.getById(id, userId) call.respond(post) } diff --git a/src/main/kotlin/dev/usbharu/hideout/routing/api/internal/v1/Users.kt b/src/main/kotlin/dev/usbharu/hideout/routing/api/internal/v1/Users.kt index 23142bd7..8609e439 100644 --- a/src/main/kotlin/dev/usbharu/hideout/routing/api/internal/v1/Users.kt +++ b/src/main/kotlin/dev/usbharu/hideout/routing/api/internal/v1/Users.kt @@ -43,9 +43,9 @@ fun Route.users(userService: IUserService, userApiService: IUserApiService) { get { val userParameter = ( call.parameters["name"] - ?: throw ParameterNotExistException( - "Parameter(name='userName@domain') does not exist." - ) + ?: throw ParameterNotExistException( + "Parameter(name='userName@domain') does not exist." + ) ) if (userParameter.toLongOrNull() != null) { return@get call.respond(userApiService.findById(userParameter.toLong())) @@ -93,9 +93,9 @@ fun Route.users(userService: IUserService, userApiService: IUserApiService) { get { val userParameter = ( call.parameters["name"] - ?: throw ParameterNotExistException( - "Parameter(name='userName@domain') does not exist." - ) + ?: throw ParameterNotExistException( + "Parameter(name='userName@domain') does not exist." + ) ) if (userParameter.toLongOrNull() != null) { return@get call.respond(userApiService.findFollowings(userParameter.toLong())) diff --git a/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubLikeServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubLikeServiceImpl.kt index 8b006df2..4d946e2e 100644 --- a/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubLikeServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubLikeServiceImpl.kt @@ -11,13 +11,14 @@ import dev.usbharu.hideout.service.user.IUserService import io.ktor.http.* import org.koin.core.annotation.Single - @Single -class ActivityPubLikeServiceImpl(private val reactionService: IReactionService, - private val activityPubUserService: ActivityPubUserService, - private val userService: IUserService, - private val postService: IPostRepository, - private val activityPubNoteService: ActivityPubNoteService) : ActivityPubLikeService { +class ActivityPubLikeServiceImpl( + private val reactionService: IReactionService, + private val activityPubUserService: ActivityPubUserService, + private val userService: IUserService, + private val postService: IPostRepository, + private val activityPubNoteService: ActivityPubNoteService +) : ActivityPubLikeService { override suspend fun receiveLike(like: Like): ActivityPubResponse { val actor = like.actor ?: throw IllegalActivityPubObjectException("actor is null") val content = like.content ?: throw IllegalActivityPubObjectException("content is null") @@ -25,11 +26,13 @@ class ActivityPubLikeServiceImpl(private val reactionService: IReactionService, val person = activityPubUserService.fetchPerson(actor) activityPubNoteService.fetchNote(like.`object`!!) - val user = userService.findByUrl(person.url - ?: throw IllegalActivityPubObjectException("actor is not found")) + val user = userService.findByUrl( + person.url + ?: throw IllegalActivityPubObjectException("actor is not found") + ) val post = postService.findByUrl(like.`object`!!) - ?: throw PostNotFoundException("${like.`object`} was not found") + ?: throw PostNotFoundException("${like.`object`} was not found") reactionService.receiveReaction(content, actor.substringAfter("://").substringBefore("/"), user.id, post.id) return ActivityPubStringResponse(HttpStatusCode.OK, "") diff --git a/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubNoteServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubNoteServiceImpl.kt index 117b207a..5d6338a5 100644 --- a/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubNoteServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubNoteServiceImpl.kt @@ -22,11 +22,11 @@ import java.time.Instant @Single class ActivityPubNoteServiceImpl( - private val httpClient: HttpClient, - private val jobQueueParentService: JobQueueParentService, - private val userService: IUserService, - private val postRepository: IPostRepository, - private val activityPubUserService: ActivityPubUserService + private val httpClient: HttpClient, + private val jobQueueParentService: JobQueueParentService, + private val userService: IUserService, + private val postRepository: IPostRepository, + private val activityPubUserService: ActivityPubUserService ) : ActivityPubNoteService { private val logger = LoggerFactory.getLogger(this::class.java) @@ -48,24 +48,24 @@ class ActivityPubNoteServiceImpl( val actor = props[DeliverPostJob.actor] val postEntity = Config.configData.objectMapper.readValue(props[DeliverPostJob.post]) val note = Note( - name = "Note", - id = postEntity.url, - attributedTo = actor, - content = postEntity.text, - published = Instant.ofEpochMilli(postEntity.createdAt).toString(), - to = listOf(public, actor + "/follower") + name = "Note", + id = postEntity.url, + attributedTo = actor, + content = postEntity.text, + published = Instant.ofEpochMilli(postEntity.createdAt).toString(), + to = listOf(public, actor + "/follower") ) val inbox = props[DeliverPostJob.inbox] logger.debug("createNoteJob: actor={}, note={}, inbox={}", actor, postEntity, inbox) httpClient.postAp( - urlString = inbox, - username = "$actor#pubkey", - jsonLd = Create( - name = "Create Note", - `object` = note, - actor = note.attributedTo, - id = "${Config.configData.url}/create/note/${postEntity.id}" - ) + urlString = inbox, + username = "$actor#pubkey", + jsonLd = Create( + name = "Create Note", + `object` = note, + actor = note.attributedTo, + id = "${Config.configData.url}/create/note/${postEntity.id}" + ) ) } @@ -75,7 +75,8 @@ class ActivityPubNoteServiceImpl( return postToNote(post) } val response = httpClient.getAp( - url, targetActor?.let { "$targetActor#pubkey" } + url, + targetActor?.let { "$targetActor#pubkey" } ) val note = Config.configData.objectMapper.readValue(response.bodyAsText()) return note(note, targetActor, url) @@ -85,44 +86,44 @@ class ActivityPubNoteServiceImpl( val user = userService.findById(post.userId) val reply = post.replyId?.let { postRepository.findOneById(it) } return Note( - name = "Post", - id = post.apId, - attributedTo = user.url, - content = post.text, - published = Instant.ofEpochMilli(post.createdAt).toString(), - to = listOf(public, user.url + "/follower"), - sensitive = post.sensitive, - cc = listOf(public, user.url + "/follower"), - inReplyTo = reply?.url + name = "Post", + id = post.apId, + attributedTo = user.url, + content = post.text, + published = Instant.ofEpochMilli(post.createdAt).toString(), + to = listOf(public, user.url + "/follower"), + sensitive = post.sensitive, + cc = listOf(public, user.url + "/follower"), + inReplyTo = reply?.url ) } private suspend fun ActivityPubNoteServiceImpl.note( - note: Note, - targetActor: String?, - url: String + note: Note, + targetActor: String?, + url: String ): Note { val findByApId = postRepository.findByApId(url) if (findByApId != null) { return postToNote(findByApId) } val person = activityPubUserService.fetchPerson( - note.attributedTo ?: throw IllegalActivityPubObjectException("note.attributedTo is null"), - targetActor + note.attributedTo ?: throw IllegalActivityPubObjectException("note.attributedTo is null"), + targetActor ) val user = - userService.findByUrl(person.url ?: throw IllegalActivityPubObjectException("person.url is null")) + userService.findByUrl(person.url ?: throw IllegalActivityPubObjectException("person.url is null")) val visibility = - if (note.to.contains(public) && note.cc.contains(public)) { - Visibility.PUBLIC - } else if (note.to.find { it.endsWith("/followers") } != null && note.cc.contains(public)) { - Visibility.UNLISTED - } else if (note.to.find { it.endsWith("/followers") } != null) { - Visibility.FOLLOWERS - } else { - Visibility.DIRECT - } + if (note.to.contains(public) && note.cc.contains(public)) { + Visibility.PUBLIC + } else if (note.to.find { it.endsWith("/followers") } != null && note.cc.contains(public)) { + Visibility.UNLISTED + } else if (note.to.find { it.endsWith("/followers") } != null) { + Visibility.FOLLOWERS + } else { + Visibility.DIRECT + } val reply = note.inReplyTo?.let { fetchNote(it, targetActor) @@ -130,25 +131,25 @@ class ActivityPubNoteServiceImpl( } postRepository.save( - Post( - id = postRepository.generateId(), - userId = user.id, - overview = null, - text = note.content.orEmpty(), - createdAt = Instant.parse(note.published).toEpochMilli(), - visibility = visibility, - url = note.id ?: url, - repostId = null, - replyId = reply?.id, - sensitive = note.sensitive, - apId = note.id ?: url, - ) + Post( + id = postRepository.generateId(), + userId = user.id, + overview = null, + text = note.content.orEmpty(), + createdAt = Instant.parse(note.published).toEpochMilli(), + visibility = visibility, + url = note.id ?: url, + repostId = null, + replyId = reply?.id, + sensitive = note.sensitive, + apId = note.id ?: url, + ) ) return note } override suspend fun fetchNote(note: Note, targetActor: String?): Note = - note(note, targetActor, note.id ?: throw IllegalArgumentException("note.id is null")) + note(note, targetActor, note.id ?: throw IllegalArgumentException("note.id is null")) companion object { const val public: String = "https://www.w3.org/ns/activitystreams#Public" diff --git a/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubServiceImpl.kt index b153ad19..e5360563 100644 --- a/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubServiceImpl.kt @@ -17,12 +17,12 @@ import org.slf4j.LoggerFactory @Single class ActivityPubServiceImpl( - private val activityPubReceiveFollowService: ActivityPubReceiveFollowService, - private val activityPubNoteService: ActivityPubNoteService, - private val activityPubUndoService: ActivityPubUndoService, - private val activityPubAcceptService: ActivityPubAcceptService, - private val activityPubCreateService: ActivityPubCreateService, - private val activityPubLikeService: ActivityPubLikeService + private val activityPubReceiveFollowService: ActivityPubReceiveFollowService, + private val activityPubNoteService: ActivityPubNoteService, + private val activityPubUndoService: ActivityPubUndoService, + private val activityPubAcceptService: ActivityPubAcceptService, + private val activityPubCreateService: ActivityPubCreateService, + private val activityPubLikeService: ActivityPubLikeService ) : ActivityPubService { val logger: Logger = LoggerFactory.getLogger(this::class.java) diff --git a/src/main/kotlin/dev/usbharu/hideout/service/api/IPostApiService.kt b/src/main/kotlin/dev/usbharu/hideout/service/api/IPostApiService.kt index 3d205faa..eb7eb97a 100644 --- a/src/main/kotlin/dev/usbharu/hideout/service/api/IPostApiService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/service/api/IPostApiService.kt @@ -3,25 +3,26 @@ package dev.usbharu.hideout.service.api import dev.usbharu.hideout.domain.model.hideout.entity.Post import java.time.Instant +@Suppress("LongParameterList") interface IPostApiService { suspend fun createPost(postForm: dev.usbharu.hideout.domain.model.hideout.form.Post, userId: Long): Post suspend fun getById(id: Long, userId: Long?): Post suspend fun getAll( - since: Instant? = null, - until: Instant? = null, - minId: Long? = null, - maxId: Long? = null, - limit: Int? = null, - userId: Long? = null + since: Instant? = null, + until: Instant? = null, + minId: Long? = null, + maxId: Long? = null, + limit: Int? = null, + userId: Long? = null ): List suspend fun getByUser( - nameOrId: String, - since: Instant? = null, - until: Instant? = null, - minId: Long? = null, - maxId: Long? = null, - limit: Int? = null, - userId: Long? = null + nameOrId: String, + since: Instant? = null, + until: Instant? = null, + minId: Long? = null, + maxId: Long? = null, + limit: Int? = null, + userId: Long? = null ): List } diff --git a/src/main/kotlin/dev/usbharu/hideout/service/api/PostApiServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/service/api/PostApiServiceImpl.kt index 92ceb999..35e05585 100644 --- a/src/main/kotlin/dev/usbharu/hideout/service/api/PostApiServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/service/api/PostApiServiceImpl.kt @@ -13,58 +13,58 @@ import dev.usbharu.hideout.domain.model.hideout.form.Post as FormPost @Single class PostApiServiceImpl( - private val postService: IPostService, - private val postRepository: IPostRepository + private val postService: IPostService, + private val postRepository: IPostRepository ) : IPostApiService { override suspend fun createPost(postForm: FormPost, userId: Long): Post { return postService.createLocal( - PostCreateDto( - text = postForm.text, - overview = postForm.overview, - visibility = postForm.visibility, - repostId = postForm.repostId, - repolyId = postForm.replyId, - userId = userId - ) + PostCreateDto( + text = postForm.text, + overview = postForm.overview, + visibility = postForm.visibility, + repostId = postForm.repostId, + repolyId = postForm.replyId, + userId = userId + ) ) } override suspend fun getById(id: Long, userId: Long?): Post { return postRepository.findOneById(id, userId) - ?: throw PostNotFoundException("$id was not found or is not authorized.") + ?: throw PostNotFoundException("$id was not found or is not authorized.") } override suspend fun getAll( - since: Instant?, - until: Instant?, - minId: Long?, - maxId: Long?, - limit: Int?, - userId: Long? + since: Instant?, + until: Instant?, + minId: Long?, + maxId: Long?, + limit: Int?, + userId: Long? ): List = postRepository.findAll(since, until, minId, maxId, limit, userId) override suspend fun getByUser( - nameOrId: String, - since: Instant?, - until: Instant?, - minId: Long?, - maxId: Long?, - limit: Int?, - userId: Long? + nameOrId: String, + since: Instant?, + until: Instant?, + minId: Long?, + maxId: Long?, + limit: Int?, + userId: Long? ): List { val idOrNull = nameOrId.toLongOrNull() return if (idOrNull == null) { val acct = AcctUtil.parse(nameOrId) postRepository.findByUserNameAndDomain( - acct.username, - acct.domain - ?: Config.configData.domain, - since, - until, - minId, - maxId, - limit, - userId + username = acct.username, + s = acct.domain + ?: Config.configData.domain, + since = since, + until = until, + minId = minId, + maxId = maxId, + limit = limit, + userId = userId ) } else { postRepository.findByUserId(idOrNull, since, until, minId, maxId, limit, userId) diff --git a/src/main/kotlin/dev/usbharu/hideout/service/post/PostServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/service/post/PostServiceImpl.kt index d9384fe5..fbd204e8 100644 --- a/src/main/kotlin/dev/usbharu/hideout/service/post/PostServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/service/post/PostServiceImpl.kt @@ -11,23 +11,23 @@ import java.time.Instant @Single class PostServiceImpl( - private val postRepository: IPostRepository, - private val userRepository: IUserRepository, - private val activityPubNoteService: ActivityPubNoteService + private val postRepository: IPostRepository, + private val userRepository: IUserRepository, + private val activityPubNoteService: ActivityPubNoteService ) : IPostService { override suspend fun createLocal(post: PostCreateDto): Post { val user = userRepository.findById(post.userId) ?: throw UserNotFoundException("${post.userId} was not found") val id = postRepository.generateId() val createPost = Post( - id = id, - userId = post.userId, - overview = post.overview, - text = post.text, - createdAt = Instant.now().toEpochMilli(), - visibility = post.visibility, - url = "${user.url}/posts/$id", - repostId = null, - replyId = null + id = id, + userId = post.userId, + overview = post.overview, + text = post.text, + createdAt = Instant.now().toEpochMilli(), + visibility = post.visibility, + url = "${user.url}/posts/$id", + repostId = null, + replyId = null ) activityPubNoteService.createNote(createPost) return internalCreate(createPost) diff --git a/src/main/kotlin/dev/usbharu/hideout/service/reaction/ReactionServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/service/reaction/ReactionServiceImpl.kt index 89c039f1..cbf24753 100644 --- a/src/main/kotlin/dev/usbharu/hideout/service/reaction/ReactionServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/service/reaction/ReactionServiceImpl.kt @@ -9,7 +9,7 @@ class ReactionServiceImpl(private val reactionRepository: ReactionRepository) : override suspend fun receiveReaction(name: String, domain: String, userId: Long, postId: Long) { if (reactionRepository.reactionAlreadyExist(postId, userId, 0).not()) { reactionRepository.save( - Reaction(reactionRepository.generateId(), 0, postId, userId) + Reaction(reactionRepository.generateId(), 0, postId, userId) ) } } diff --git a/src/test/kotlin/dev/usbharu/hideout/routing/api/internal/v1/PostsTest.kt b/src/test/kotlin/dev/usbharu/hideout/routing/api/internal/v1/PostsTest.kt index d4777c46..0f2c1627 100644 --- a/src/test/kotlin/dev/usbharu/hideout/routing/api/internal/v1/PostsTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/routing/api/internal/v1/PostsTest.kt @@ -41,24 +41,24 @@ class PostsTest { createdAt = Instant.now().toEpochMilli(), url = "https://example.com/posts/1" ), - Post( - id = 123456, - userId = 4322, - text = "test2", - visibility = Visibility.PUBLIC, - createdAt = Instant.now().toEpochMilli(), - url = "https://example.com/posts/2" - ) + Post( + id = 123456, + userId = 4322, + text = "test2", + visibility = Visibility.PUBLIC, + createdAt = Instant.now().toEpochMilli(), + url = "https://example.com/posts/2" + ) ) val postService = mock { onBlocking { getAll( - since = anyOrNull(), - until = anyOrNull(), - minId = anyOrNull(), - maxId = anyOrNull(), - limit = anyOrNull(), - userId = isNull() + since = anyOrNull(), + until = anyOrNull(), + minId = anyOrNull(), + maxId = anyOrNull(), + limit = anyOrNull(), + userId = isNull() ) } doReturn posts } @@ -120,12 +120,12 @@ class PostsTest { val postService = mock { onBlocking { getAll( - since = anyOrNull(), - until = anyOrNull(), - minId = anyOrNull(), - maxId = anyOrNull(), - limit = anyOrNull(), - userId = isNotNull() + since = anyOrNull(), + until = anyOrNull(), + minId = anyOrNull(), + maxId = anyOrNull(), + limit = anyOrNull(), + userId = isNotNull() ) } doReturn posts } @@ -157,12 +157,12 @@ class PostsTest { config = ApplicationConfig("empty.conf") } val post = Post( - 12345, - 1234, - text = "aaa", - visibility = Visibility.PUBLIC, - createdAt = Instant.now().toEpochMilli(), - url = "https://example.com/posts/1" + 12345, + 1234, + text = "aaa", + visibility = Visibility.PUBLIC, + createdAt = Instant.now().toEpochMilli(), + url = "https://example.com/posts/1" ) val postService = mock { onBlocking { getById(any(), anyOrNull()) } doReturn post @@ -188,12 +188,12 @@ class PostsTest { config = ApplicationConfig("empty.conf") } val post = Post( - 12345, - 1234, - text = "aaa", - visibility = Visibility.FOLLOWERS, - createdAt = Instant.now().toEpochMilli(), - url = "https://example.com/posts/1" + 12345, + 1234, + text = "aaa", + visibility = Visibility.FOLLOWERS, + createdAt = Instant.now().toEpochMilli(), + url = "https://example.com/posts/1" ) val postService = mock { onBlocking { getById(any(), isNotNull()) } doReturn post @@ -243,13 +243,13 @@ class PostsTest { val argument = it.getArgument(0) val userId = it.getArgument(1) Post( - 123L, - userId, - null, - argument.text, - Instant.now().toEpochMilli(), - Visibility.PUBLIC, - "https://example.com" + 123L, + userId, + null, + argument.text, + Instant.now().toEpochMilli(), + Visibility.PUBLIC, + "https://example.com" ) } } @@ -299,25 +299,25 @@ class PostsTest { createdAt = Instant.now().toEpochMilli(), url = "https://example.com/posts/1" ), - Post( - id = 123456, - userId = 1, - text = "test2", - visibility = Visibility.PUBLIC, - createdAt = Instant.now().toEpochMilli(), - url = "https://example.com/posts/2" - ) + Post( + id = 123456, + userId = 1, + text = "test2", + visibility = Visibility.PUBLIC, + createdAt = Instant.now().toEpochMilli(), + url = "https://example.com/posts/2" + ) ) val postService = mock { onBlocking { getByUser( - nameOrId = any(), - since = anyOrNull(), - until = anyOrNull(), - minId = anyOrNull(), - maxId = anyOrNull(), - limit = anyOrNull(), - userId = anyOrNull() + nameOrId = any(), + since = anyOrNull(), + until = anyOrNull(), + minId = anyOrNull(), + maxId = anyOrNull(), + limit = anyOrNull(), + userId = anyOrNull() ) } doReturn posts } @@ -351,25 +351,25 @@ class PostsTest { createdAt = Instant.now().toEpochMilli(), url = "https://example.com/posts/1" ), - Post( - id = 123456, - userId = 1, - text = "test2", - visibility = Visibility.PUBLIC, - createdAt = Instant.now().toEpochMilli(), - url = "https://example.com/posts/2" - ) + Post( + id = 123456, + userId = 1, + text = "test2", + visibility = Visibility.PUBLIC, + createdAt = Instant.now().toEpochMilli(), + url = "https://example.com/posts/2" + ) ) val postService = mock { onBlocking { getByUser( - nameOrId = eq("test1"), - since = anyOrNull(), - until = anyOrNull(), - minId = anyOrNull(), - maxId = anyOrNull(), - limit = anyOrNull(), - userId = anyOrNull() + nameOrId = eq("test1"), + since = anyOrNull(), + until = anyOrNull(), + minId = anyOrNull(), + maxId = anyOrNull(), + limit = anyOrNull(), + userId = anyOrNull() ) } doReturn posts } @@ -403,25 +403,25 @@ class PostsTest { createdAt = Instant.now().toEpochMilli(), url = "https://example.com/posts/1" ), - Post( - id = 123456, - userId = 1, - text = "test2", - visibility = Visibility.PUBLIC, - createdAt = Instant.now().toEpochMilli(), - url = "https://example.com/posts/2" - ) + Post( + id = 123456, + userId = 1, + text = "test2", + visibility = Visibility.PUBLIC, + createdAt = Instant.now().toEpochMilli(), + url = "https://example.com/posts/2" + ) ) val postService = mock { onBlocking { getByUser( - nameOrId = eq("test1@example.com"), - since = anyOrNull(), - until = anyOrNull(), - minId = anyOrNull(), - maxId = anyOrNull(), - limit = anyOrNull(), - userId = anyOrNull() + nameOrId = eq("test1@example.com"), + since = anyOrNull(), + until = anyOrNull(), + minId = anyOrNull(), + maxId = anyOrNull(), + limit = anyOrNull(), + userId = anyOrNull() ) } doReturn posts } @@ -455,25 +455,25 @@ class PostsTest { createdAt = Instant.now().toEpochMilli(), url = "https://example.com/posts/1" ), - Post( - id = 123456, - userId = 1, - text = "test2", - visibility = Visibility.PUBLIC, - createdAt = Instant.now().toEpochMilli(), - url = "https://example.com/posts/2" - ) + Post( + id = 123456, + userId = 1, + text = "test2", + visibility = Visibility.PUBLIC, + createdAt = Instant.now().toEpochMilli(), + url = "https://example.com/posts/2" + ) ) val postService = mock { onBlocking { getByUser( - nameOrId = eq("@test1@example.com"), - since = anyOrNull(), - until = anyOrNull(), - minId = anyOrNull(), - maxId = anyOrNull(), - limit = anyOrNull(), - userId = anyOrNull() + nameOrId = eq("@test1@example.com"), + since = anyOrNull(), + until = anyOrNull(), + minId = anyOrNull(), + maxId = anyOrNull(), + limit = anyOrNull(), + userId = anyOrNull() ) } doReturn posts } @@ -499,12 +499,12 @@ class PostsTest { config = ApplicationConfig("empty.conf") } val post = Post( - id = 123456, - userId = 1, - text = "test2", - visibility = Visibility.PUBLIC, - createdAt = Instant.now().toEpochMilli(), - url = "https://example.com/posts/2" + id = 123456, + userId = 1, + text = "test2", + visibility = Visibility.PUBLIC, + createdAt = Instant.now().toEpochMilli(), + url = "https://example.com/posts/2" ) val postService = mock { onBlocking { getById(eq(12345L), anyOrNull()) } doReturn post @@ -531,12 +531,12 @@ class PostsTest { config = ApplicationConfig("empty.conf") } val post = Post( - id = 123456, - userId = 1, - text = "test2", - visibility = Visibility.PUBLIC, - createdAt = Instant.now().toEpochMilli(), - url = "https://example.com/posts/2" + id = 123456, + userId = 1, + text = "test2", + visibility = Visibility.PUBLIC, + createdAt = Instant.now().toEpochMilli(), + url = "https://example.com/posts/2" ) val postService = mock { onBlocking { getById(eq(12345L), anyOrNull()) } doReturn post @@ -563,12 +563,12 @@ class PostsTest { config = ApplicationConfig("empty.conf") } val post = Post( - id = 123456, - userId = 1, - text = "test2", - visibility = Visibility.PUBLIC, - createdAt = Instant.now().toEpochMilli(), - url = "https://example.com/posts/2" + id = 123456, + userId = 1, + text = "test2", + visibility = Visibility.PUBLIC, + createdAt = Instant.now().toEpochMilli(), + url = "https://example.com/posts/2" ) val postService = mock { onBlocking { getById(eq(12345L), anyOrNull()) } doReturn post @@ -595,12 +595,12 @@ class PostsTest { config = ApplicationConfig("empty.conf") } val post = Post( - id = 123456, - userId = 1, - text = "test2", - visibility = Visibility.PUBLIC, - createdAt = Instant.now().toEpochMilli(), - url = "https://example.com/posts/2" + id = 123456, + userId = 1, + text = "test2", + visibility = Visibility.PUBLIC, + createdAt = Instant.now().toEpochMilli(), + url = "https://example.com/posts/2" ) val postService = mock { onBlocking { getById(eq(12345L), anyOrNull()) } doReturn post