From 68c6c08e5621a9d031150bd8c3970d9b32639a06 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Thu, 7 Dec 2023 16:02:12 +0900 Subject: [PATCH 01/43] =?UTF-8?q?fix:=20#195=20ActivityPubProcessor=20not?= =?UTF-8?q?=20found.=20=E3=81=AE=E4=BE=8B=E5=A4=96=E3=83=A1=E3=83=83?= =?UTF-8?q?=E3=82=BB=E3=83=BC=E3=82=B8=E3=82=92=E3=82=8F=E3=81=8B=E3=82=8A?= =?UTF-8?q?=E3=82=84=E3=81=99=E3=81=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hideout/activitypub/service/inbox/InboxJobProcessor.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/inbox/InboxJobProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/inbox/InboxJobProcessor.kt index 301ac7ce..ec25c727 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/inbox/InboxJobProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/inbox/InboxJobProcessor.kt @@ -106,7 +106,7 @@ class InboxJobProcessor( if (activityPubProcessor == null) { logger.warn("ActivityType {} is not support.", param.type) - throw IllegalStateException("ActivityPubProcessor not found.") + throw IllegalStateException("ActivityPubProcessor not found. type: ${param.type}") } val value = objectMapper.treeToValue(jsonNode, activityPubProcessor.type()) From 62a65d1c16f8b510e51e281a83dcc93859871d31 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Thu, 7 Dec 2023 16:27:55 +0900 Subject: [PATCH 02/43] =?UTF-8?q?feat:=20Block=E3=81=AEJSON=E3=83=87?= =?UTF-8?q?=E3=82=B7=E3=83=AA=E3=82=A2=E3=83=A9=E3=82=A4=E3=82=BA=E7=94=A8?= =?UTF-8?q?POJO=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hideout/activitypub/domain/model/Block.kt | 45 +++++++++++++++++++ .../model/objects/ObjectDeserializer.kt | 2 +- 2 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Block.kt diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Block.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Block.kt new file mode 100644 index 00000000..b1bde6d5 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Block.kt @@ -0,0 +1,45 @@ +package dev.usbharu.hideout.activitypub.domain.model + +import com.fasterxml.jackson.annotation.JsonProperty +import dev.usbharu.hideout.activitypub.domain.model.objects.Object + +open class Block( + override val actor: String, + override val id: String, + @JsonProperty("object") val apObject: String +) : + Object(listOf("Block")), HasId, HasActor { + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + if (!super.equals(other)) return false + + other as Block + + if (actor != other.actor) return false + if (id != other.id) return false + if (apObject != other.apObject) return false + + return true + } + + override fun hashCode(): Int { + var result = super.hashCode() + result = 31 * result + actor.hashCode() + result = 31 * result + id.hashCode() + result = 31 * result + apObject.hashCode() + return result + } + + override fun toString(): String { + return "Block(" + + "actor='$actor', " + + "id='$id', " + + "apObject='$apObject'" + + ")" + + " ${super.toString()}" + } + + +} diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectDeserializer.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectDeserializer.kt index f28070e6..677d3c7d 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectDeserializer.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectDeserializer.kt @@ -44,7 +44,7 @@ class ObjectDeserializer : JsonDeserializer() { ExtendedActivityVocabulary.Add -> TODO() ExtendedActivityVocabulary.Announce -> TODO() ExtendedActivityVocabulary.Arrive -> TODO() - ExtendedActivityVocabulary.Block -> TODO() + ExtendedActivityVocabulary.Block -> p.codec.treeToValue(treeNode, Block::class.java) ExtendedActivityVocabulary.Create -> p.codec.treeToValue(treeNode, Create::class.java) ExtendedActivityVocabulary.Delete -> p.codec.treeToValue(treeNode, Delete::class.java) ExtendedActivityVocabulary.Dislike -> TODO() From 434d786fb415044fe2f793ee637b1509e77a2ad4 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Thu, 7 Dec 2023 16:32:25 +0900 Subject: [PATCH 03/43] =?UTF-8?q?fix:=20=E4=BD=95=E6=95=85=E3=81=8Babstrac?= =?UTF-8?q?t=20class=E3=81=8CBean=E7=99=BB=E9=8C=B2=E3=81=95=E3=82=8C?= =?UTF-8?q?=E3=81=A6=E3=81=84=E3=81=9F=E3=81=AE=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../activitypub/service/common/AbstractActivityPubProcessor.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/AbstractActivityPubProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/AbstractActivityPubProcessor.kt index 0e04262e..1bb106e3 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/AbstractActivityPubProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/AbstractActivityPubProcessor.kt @@ -7,9 +7,7 @@ import dev.usbharu.hideout.activitypub.domain.model.objects.Object import dev.usbharu.hideout.application.external.Transaction import org.slf4j.Logger import org.slf4j.LoggerFactory -import org.springframework.stereotype.Service -@Service abstract class AbstractActivityPubProcessor( private val transaction: Transaction, private val allowUnauthorized: Boolean = false From dd89b48d57b892cc950252dfdcc555ef199df445 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Thu, 7 Dec 2023 17:19:33 +0900 Subject: [PATCH 04/43] =?UTF-8?q?test:=20Block=E3=81=AE=E3=82=B7=E3=83=AA?= =?UTF-8?q?=E3=82=A2=E3=83=A9=E3=82=A4=E3=82=BA=E3=80=81=E3=83=87=E3=82=B7?= =?UTF-8?q?=E3=83=AA=E3=82=A2=E3=83=A9=E3=82=A4=E3=82=BA=E3=83=86=E3=82=B9?= =?UTF-8?q?=E3=83=88=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle.kts | 1 + .../activitypub/domain/model/BlockTest.kt | 77 +++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/BlockTest.kt diff --git a/build.gradle.kts b/build.gradle.kts index c8d31ca7..4ecf8a33 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -217,6 +217,7 @@ dependencies { testImplementation("org.mockito:mockito-inline:5.2.0") testImplementation("nl.jqno.equalsverifier:equalsverifier:3.15.3") testImplementation("com.jparams:to-string-verifier:1.4.8") + testImplementation("com.toomuchcoding.jsonassert:jsonassert:0.7.0") implementation("org.drewcarlson:kjob-core:0.6.0") implementation("org.drewcarlson:kjob-mongo:0.6.0") diff --git a/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/BlockTest.kt b/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/BlockTest.kt new file mode 100644 index 00000000..d7e2f700 --- /dev/null +++ b/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/BlockTest.kt @@ -0,0 +1,77 @@ +package dev.usbharu.hideout.activitypub.domain.model + +import com.fasterxml.jackson.module.kotlin.readValue +import dev.usbharu.hideout.application.config.ActivityPubConfig +import org.assertj.core.api.Assertions.assertThat +import org.intellij.lang.annotations.Language +import org.junit.jupiter.api.Test +import org.springframework.boot.test.json.BasicJsonTester + +class BlockTest { + @Test + fun blockDeserializeTest() { + @Language("JSON") val json = """{ + "@context" : [ "https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1", { + "manuallyApprovesFollowers" : "as:manuallyApprovesFollowers", + "sensitive" : "as:sensitive", + "Hashtag" : "as:Hashtag", + "quoteUrl" : "as:quoteUrl", + "toot" : "http://joinmastodon.org/ns#", + "Emoji" : "toot:Emoji", + "featured" : "toot:featured", + "discoverable" : "toot:discoverable", + "schema" : "http://schema.org#", + "PropertyValue" : "schema:PropertyValue", + "value" : "schema:value", + "misskey" : "https://misskey-hub.net/ns#", + "_misskey_content" : "misskey:_misskey_content", + "_misskey_quote" : "misskey:_misskey_quote", + "_misskey_reaction" : "misskey:_misskey_reaction", + "_misskey_votes" : "misskey:_misskey_votes", + "_misskey_summary" : "misskey:_misskey_summary", + "isCat" : "misskey:isCat", + "vcard" : "http://www.w3.org/2006/vcard/ns#" + } ], + "type" : "Block", + "id" : "https://misskey.usbharu.dev/blocks/9myf6e40vm", + "actor" : "https://misskey.usbharu.dev/users/97ws8y3rj6", + "object" : "https://test-hideout.usbharu.dev/users/test-user2" +} +""" + + val objectMapper = ActivityPubConfig().objectMapper() + + val block = objectMapper.readValue(json) + + val expected = Block( + "https://misskey.usbharu.dev/users/97ws8y3rj6", + "https://misskey.usbharu.dev/blocks/9myf6e40vm", + "https://test-hideout.usbharu.dev/users/test-user2" + ).apply { context = listOf("https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1") } + assertThat(block).isEqualTo(expected) + } + + @Test + fun blockSerializeTest() { + val basicJsonTester = BasicJsonTester(javaClass) + + val block = Block( + "https://misskey.usbharu.dev/users/97ws8y3rj6", + "https://misskey.usbharu.dev/blocks/9myf6e40vm", + "https://test-hideout.usbharu.dev/users/test-user2" + ).apply { context = listOf("https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1") } + + val objectMapper = ActivityPubConfig().objectMapper() + + val writeValueAsString = objectMapper.writeValueAsString(block) + + val from = basicJsonTester.from(writeValueAsString) + assertThat(from).extractingJsonPathStringValue("$.actor") + .isEqualTo("https://misskey.usbharu.dev/users/97ws8y3rj6") + assertThat(from).extractingJsonPathStringValue("$.id") + .isEqualTo("https://misskey.usbharu.dev/blocks/9myf6e40vm") + assertThat(from).extractingJsonPathStringValue("$.object") + .isEqualTo("https://test-hideout.usbharu.dev/users/test-user2") + + } +} From 002a452c332f8d1d6c0f382a9becb8ec223f8890 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Thu, 7 Dec 2023 17:21:37 +0900 Subject: [PATCH 05/43] =?UTF-8?q?chore:=20=E4=B8=8D=E8=A6=81=E3=81=AA?= =?UTF-8?q?=E3=83=A9=E3=82=A4=E3=83=96=E3=83=A9=E3=83=AA=E3=82=92=E5=89=8A?= =?UTF-8?q?=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle.kts | 1 - 1 file changed, 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 4ecf8a33..c8d31ca7 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -217,7 +217,6 @@ dependencies { testImplementation("org.mockito:mockito-inline:5.2.0") testImplementation("nl.jqno.equalsverifier:equalsverifier:3.15.3") testImplementation("com.jparams:to-string-verifier:1.4.8") - testImplementation("com.toomuchcoding.jsonassert:jsonassert:0.7.0") implementation("org.drewcarlson:kjob-core:0.6.0") implementation("org.drewcarlson:kjob-mongo:0.6.0") From 61a1f5dcc9be3d54a92e71f09b7df969481d6255 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Thu, 7 Dec 2023 17:29:57 +0900 Subject: [PATCH 06/43] =?UTF-8?q?test:=20Block=E3=81=AE=E3=82=B7=E3=83=AA?= =?UTF-8?q?=E3=82=A2=E3=83=A9=E3=82=A4=E3=82=BA=E3=83=86=E3=82=B9=E3=83=88?= =?UTF-8?q?=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dev/usbharu/hideout/activitypub/domain/model/BlockTest.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/BlockTest.kt b/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/BlockTest.kt index d7e2f700..093aa98d 100644 --- a/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/BlockTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/BlockTest.kt @@ -72,6 +72,7 @@ class BlockTest { .isEqualTo("https://misskey.usbharu.dev/blocks/9myf6e40vm") assertThat(from).extractingJsonPathStringValue("$.object") .isEqualTo("https://test-hideout.usbharu.dev/users/test-user2") + assertThat(from).extractingJsonPathStringValue("$.type").isEqualTo("Block") } } From 287cd0a7f30b9cffbaa49d0adfea173f2e3a33e1 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Thu, 7 Dec 2023 17:52:12 +0900 Subject: [PATCH 07/43] =?UTF-8?q?feat:=20Reject=E3=81=AEJSON=E3=83=87?= =?UTF-8?q?=E3=82=B7=E3=83=AA=E3=82=A2=E3=83=A9=E3=82=A4=E3=82=BA=E7=94=A8?= =?UTF-8?q?=E3=81=AEPOJO=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../activitypub/domain/model/Reject.kt | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Reject.kt diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Reject.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Reject.kt new file mode 100644 index 00000000..fd0e980e --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Reject.kt @@ -0,0 +1,45 @@ +package dev.usbharu.hideout.activitypub.domain.model + +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import dev.usbharu.hideout.activitypub.domain.model.objects.Object +import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer + +open class Reject( + override val actor: String, + override val id: String, + @JsonDeserialize(using = ObjectDeserializer::class) @JsonProperty("object") val apObject: Object +) : Object(listOf("Reject")), HasId, HasActor { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + if (!super.equals(other)) return false + + other as Reject + + if (actor != other.actor) return false + if (id != other.id) return false + if (apObject != other.apObject) return false + + return true + } + + override fun hashCode(): Int { + var result = super.hashCode() + result = 31 * result + actor.hashCode() + result = 31 * result + id.hashCode() + result = 31 * result + apObject.hashCode() + return result + } + + override fun toString(): String { + return "Reject(" + + "actor='$actor', " + + "id='$id', " + + "apObject=$apObject" + + ")" + + " ${super.toString()}" + } + + +} From e353012936d772d886c850a990b4b076db527f27 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Thu, 7 Dec 2023 17:52:57 +0900 Subject: [PATCH 08/43] =?UTF-8?q?test:=20Reject=E3=81=AE=E3=82=B7=E3=83=AA?= =?UTF-8?q?=E3=82=A2=E3=83=A9=E3=82=A4=E3=82=BA=E3=83=87=E3=82=B7=E3=83=AA?= =?UTF-8?q?=E3=82=A2=E3=83=A9=E3=82=A4=E3=82=BA=E3=81=AE=E3=83=86=E3=82=B9?= =?UTF-8?q?=E3=83=88=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../activitypub/domain/model/RejectTest.kt | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/RejectTest.kt diff --git a/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/RejectTest.kt b/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/RejectTest.kt new file mode 100644 index 00000000..19c48209 --- /dev/null +++ b/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/RejectTest.kt @@ -0,0 +1,93 @@ +package dev.usbharu.hideout.activitypub.domain.model + +import com.fasterxml.jackson.module.kotlin.readValue +import dev.usbharu.hideout.application.config.ActivityPubConfig +import org.assertj.core.api.Assertions.assertThat +import org.intellij.lang.annotations.Language +import org.junit.jupiter.api.Test +import org.springframework.boot.test.json.BasicJsonTester + +class RejectTest { + @Test + fun rejectDeserializeTest() { + @Language("JSON") val json = """{ + "@context" : [ "https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1", { + "manuallyApprovesFollowers" : "as:manuallyApprovesFollowers", + "sensitive" : "as:sensitive", + "Hashtag" : "as:Hashtag", + "quoteUrl" : "as:quoteUrl", + "toot" : "http://joinmastodon.org/ns#", + "Emoji" : "toot:Emoji", + "featured" : "toot:featured", + "discoverable" : "toot:discoverable", + "schema" : "http://schema.org#", + "PropertyValue" : "schema:PropertyValue", + "value" : "schema:value", + "misskey" : "https://misskey-hub.net/ns#", + "_misskey_content" : "misskey:_misskey_content", + "_misskey_quote" : "misskey:_misskey_quote", + "_misskey_reaction" : "misskey:_misskey_reaction", + "_misskey_votes" : "misskey:_misskey_votes", + "_misskey_summary" : "misskey:_misskey_summary", + "isCat" : "misskey:isCat", + "vcard" : "http://www.w3.org/2006/vcard/ns#" + } ], + "type" : "Reject", + "actor" : "https://misskey.usbharu.dev/users/97ws8y3rj6", + "object" : { + "id" : "https://misskey.usbharu.dev/follows/9mxh6mawru/97ws8y3rj6", + "type" : "Follow", + "actor" : "https://test-hideout.usbharu.dev/users/test-user2", + "object" : "https://misskey.usbharu.dev/users/97ws8y3rj6" + }, + "id" : "https://misskey.usbharu.dev/06407419-5aeb-4e2d-8885-aa54b03decf0" +} +""" + + val objectMapper = ActivityPubConfig().objectMapper() + + val reject = objectMapper.readValue(json) + + val expected = Reject( + "https://misskey.usbharu.dev/users/97ws8y3rj6", + "https://misskey.usbharu.dev/06407419-5aeb-4e2d-8885-aa54b03decf0", + Follow( + apObject = "https://misskey.usbharu.dev/users/97ws8y3rj6", + actor = "https://test-hideout.usbharu.dev/users/test-user2" + ) + ).apply { context = listOf("https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1") } + + assertThat(reject).isEqualTo(expected) + } + + @Test + fun rejectSerializeTest() { + val basicJsonTester = BasicJsonTester(javaClass) + + val reject = Reject( + "https://misskey.usbharu.dev/users/97ws8y3rj6", + "https://misskey.usbharu.dev/06407419-5aeb-4e2d-8885-aa54b03decf0", + Follow( + apObject = "https://misskey.usbharu.dev/users/97ws8y3rj6", + actor = "https://test-hideout.usbharu.dev/users/test-user2" + ) + ).apply { context = listOf("https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1") } + + val objectMapper = ActivityPubConfig().objectMapper() + + val writeValueAsString = objectMapper.writeValueAsString(reject) + + val from = basicJsonTester.from(writeValueAsString) + + assertThat(from).extractingJsonPathStringValue("$.actor") + .isEqualTo("https://misskey.usbharu.dev/users/97ws8y3rj6") + assertThat(from).extractingJsonPathStringValue("$.id") + .isEqualTo("https://misskey.usbharu.dev/06407419-5aeb-4e2d-8885-aa54b03decf0") + assertThat(from).extractingJsonPathStringValue("$.type").isEqualTo("Reject") + assertThat(from).extractingJsonPathStringValue("$.object.actor") + .isEqualTo("https://test-hideout.usbharu.dev/users/test-user2") + assertThat(from).extractingJsonPathStringValue("$.object.object") + .isEqualTo("https://misskey.usbharu.dev/users/97ws8y3rj6") + assertThat(from).extractingJsonPathStringValue("$.object.type").isEqualTo("Follow") + } +} From b0f3ae4d8d319f39e7127064319bd62dc7a6f672 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Fri, 8 Dec 2023 12:21:23 +0900 Subject: [PATCH 09/43] =?UTF-8?q?feat:=20ObjectDeserializer=E3=81=ABReject?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../activitypub/domain/model/objects/ObjectDeserializer.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectDeserializer.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectDeserializer.kt index 677d3c7d..caa6caff 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectDeserializer.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectDeserializer.kt @@ -58,7 +58,7 @@ class ObjectDeserializer : JsonDeserializer() { ExtendedActivityVocabulary.Move -> TODO() ExtendedActivityVocabulary.Offer -> TODO() ExtendedActivityVocabulary.Question -> TODO() - ExtendedActivityVocabulary.Reject -> TODO() + ExtendedActivityVocabulary.Reject -> p.codec.treeToValue(treeNode, Reject::class.java) ExtendedActivityVocabulary.Read -> TODO() ExtendedActivityVocabulary.Remove -> TODO() ExtendedActivityVocabulary.TentativeReject -> TODO() From a6a455a7ae59b595dc8ee4a843ffce29803218ce Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Fri, 8 Dec 2023 12:22:08 +0900 Subject: [PATCH 10/43] =?UTF-8?q?feat:=20BlockService=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hideout/core/domain/model/block/Block.kt | 6 ++ .../domain/model/block/BlockRepository.kt | 7 ++ .../exposedrepository/BlockRepositoryImpl.kt | 53 ++++++++++++ .../core/service/block/BlockService.kt | 6 ++ .../core/service/block/BlockServiceImpl.kt | 84 +++++++++++++++++++ 5 files changed, 156 insertions(+) create mode 100644 src/main/kotlin/dev/usbharu/hideout/core/domain/model/block/Block.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/core/domain/model/block/BlockRepository.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/BlockRepositoryImpl.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/core/service/block/BlockService.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/core/service/block/BlockServiceImpl.kt diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/block/Block.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/block/Block.kt new file mode 100644 index 00000000..a2ddf940 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/block/Block.kt @@ -0,0 +1,6 @@ +package dev.usbharu.hideout.core.domain.model.block + +data class Block( + val userId: Long, + val target: Long +) diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/block/BlockRepository.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/block/BlockRepository.kt new file mode 100644 index 00000000..a847bb4a --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/block/BlockRepository.kt @@ -0,0 +1,7 @@ +package dev.usbharu.hideout.core.domain.model.block + +interface BlockRepository { + suspend fun save(block: Block): Block + suspend fun delete(block: Block) + suspend fun findByUserIdAndTarget(userId: Long, target: Long): Block +} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/BlockRepositoryImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/BlockRepositoryImpl.kt new file mode 100644 index 00000000..c3d60ea9 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/BlockRepositoryImpl.kt @@ -0,0 +1,53 @@ +package dev.usbharu.hideout.core.infrastructure.exposedrepository + +import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException +import dev.usbharu.hideout.core.domain.model.block.Block +import dev.usbharu.hideout.core.domain.model.block.BlockRepository +import dev.usbharu.hideout.util.singleOr +import org.jetbrains.exposed.dao.id.LongIdTable +import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq +import org.jetbrains.exposed.sql.and +import org.jetbrains.exposed.sql.deleteWhere +import org.jetbrains.exposed.sql.insert +import org.jetbrains.exposed.sql.select +import org.springframework.stereotype.Repository + +@Repository +class BlockRepositoryImpl : BlockRepository { + override suspend fun save(block: Block): Block { + Blocks.insert { + it[userId] = block.userId + it[target] = block.target + } + return block + } + + override suspend fun delete(block: Block) { + Blocks.deleteWhere { Blocks.userId eq block.userId and (Blocks.target eq block.target) } + } + + override suspend fun findByUserIdAndTarget(userId: Long, target: Long): Block { + val singleOr = Blocks + .select { Blocks.userId eq userId and (Blocks.target eq target) } + .singleOr { + FailedToGetResourcesException( + "userId: $userId target: $target is duplicate or not exist.", + it + ) + } + + return Block( + singleOr[Blocks.userId], + singleOr[Blocks.target] + ) + } +} + +object Blocks : LongIdTable("blocks") { + val userId = long("user_id").references(Users.id).index() + val target = long("target").references(Users.id) + + init { + uniqueIndex(userId, target) + } +} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/block/BlockService.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/block/BlockService.kt new file mode 100644 index 00000000..8988a15d --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/block/BlockService.kt @@ -0,0 +1,6 @@ +package dev.usbharu.hideout.core.service.block + +interface BlockService { + suspend fun block(userId: Long, target: Long) + suspend fun unblock(userId: Long, target: Long) +} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/block/BlockServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/block/BlockServiceImpl.kt new file mode 100644 index 00000000..bc0eef96 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/block/BlockServiceImpl.kt @@ -0,0 +1,84 @@ +package dev.usbharu.hideout.core.service.block + +import dev.usbharu.hideout.activitypub.domain.model.Follow +import dev.usbharu.hideout.activitypub.domain.model.Reject +import dev.usbharu.hideout.application.config.ApplicationConfig +import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException +import dev.usbharu.hideout.core.domain.model.block.Block +import dev.usbharu.hideout.core.domain.model.block.BlockRepository +import dev.usbharu.hideout.core.domain.model.user.UserRepository +import dev.usbharu.hideout.core.external.job.DeliverBlockJob +import dev.usbharu.hideout.core.external.job.DeliverBlockJobParam +import dev.usbharu.hideout.core.query.FollowerQueryService +import dev.usbharu.hideout.core.service.job.JobQueueParentService +import dev.usbharu.hideout.core.service.user.UserService +import org.slf4j.LoggerFactory +import org.springframework.stereotype.Service + +@Service +class BlockServiceImpl( + private val transaction: Transaction, + private val blockRepository: BlockRepository, + private val followerQueryService: FollowerQueryService, + private val userService: UserService, + private val jobQueueParentService: JobQueueParentService, + private val deliverBlockJob: DeliverBlockJob, + private val userRepository: UserRepository, + private val applicationConfig: ApplicationConfig +) : + BlockService { + override suspend fun block(userId: Long, target: Long): Unit = transaction.transaction { + logger.debug("Block userId: {} → target: {}", userId, target) + blockRepository.save(Block(userId, target)) + if (followerQueryService.alreadyFollow(userId, target)) { + logger.debug("Unfollow (Block) userId: {} → target: {}", userId, target) + userService.unfollow(userId, target) + } + + val user = userRepository.findById(userId) ?: throw IllegalStateException("Block user was not found.") + + if (user.domain == applicationConfig.url.host) { + return@transaction + } + + val target = userRepository.findById(target) ?: throw IllegalStateException("Block use was not found.") + + if (target.domain == applicationConfig.url.host) { + return@transaction + } + + val blockJobParam = DeliverBlockJobParam( + user.id, + dev.usbharu.hideout.activitypub.domain.model.Block( + user.url, + "${applicationConfig.url}/block/${user.id}/${target.id}", + target.url + ), + Reject( + user.url, + "${applicationConfig.url}/reject/${user.id}/${target.id}", + Follow( + apObject = user.url, + actor = target.url + ) + ), + target.inbox + ) + jobQueueParentService.scheduleTypeSafe(deliverBlockJob, blockJobParam) + } + + override suspend fun unblock(userId: Long, target: Long) = transaction.transaction { + logger.debug("Unblock userId: {} → target: {}", userId, target) + try { + val block = blockRepository.findByUserIdAndTarget(userId, target) + blockRepository.delete(block) + } catch (e: FailedToGetResourcesException) { + logger.warn("FAILED Unblock userId: {} target: {}", userId, target, e) + } + } + + companion object { + private val logger = LoggerFactory.getLogger(BlockServiceImpl::class.java) + } +} From a00c9e6313caacab1309e919db8e277c1a9f7496 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Fri, 8 Dec 2023 12:22:24 +0900 Subject: [PATCH 11/43] =?UTF-8?q?feat:=20Block=E3=81=AE=E9=85=8D=E9=80=81?= =?UTF-8?q?=E5=87=A6=E7=90=86=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../block/APDeliverBlockJobProcessor.kt | 34 +++++++++++++++ .../core/external/job/DeliverBlockJob.kt | 43 +++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/block/APDeliverBlockJobProcessor.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverBlockJob.kt diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/block/APDeliverBlockJobProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/block/APDeliverBlockJobProcessor.kt new file mode 100644 index 00000000..0b56ac8c --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/block/APDeliverBlockJobProcessor.kt @@ -0,0 +1,34 @@ +package dev.usbharu.hideout.activitypub.service.activity.block + +import dev.usbharu.hideout.activitypub.service.common.APRequestService +import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.domain.model.user.UserRepository +import dev.usbharu.hideout.core.external.job.DeliverBlockJob +import dev.usbharu.hideout.core.external.job.DeliverBlockJobParam +import dev.usbharu.hideout.core.service.job.JobProcessor +import org.springframework.stereotype.Service + +@Service +class APDeliverBlockJobProcessor( + private val apRequestService: APRequestService, + private val userRepository: UserRepository, + private val transaction: Transaction, + private val deliverBlockJob: DeliverBlockJob +) : JobProcessor { + override suspend fun process(param: DeliverBlockJobParam): Unit = transaction.transaction { + + val signer = userRepository.findById(param.signer) + apRequestService.apPost( + param.inbox, + param.reject, + signer + ) + apRequestService.apPost( + param.inbox, + param.block, + signer + ) + } + + override fun job(): DeliverBlockJob = deliverBlockJob +} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverBlockJob.kt b/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverBlockJob.kt new file mode 100644 index 00000000..8f55831e --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverBlockJob.kt @@ -0,0 +1,43 @@ +package dev.usbharu.hideout.core.external.job + +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.module.kotlin.readValue +import dev.usbharu.hideout.activitypub.domain.model.Block +import dev.usbharu.hideout.activitypub.domain.model.Reject +import kjob.core.dsl.ScheduleContext +import kjob.core.job.JobProps +import org.springframework.beans.factory.annotation.Qualifier +import org.springframework.stereotype.Component + +data class DeliverBlockJobParam( + val signer: Long, + val block: Block, + val reject: Reject, + val inbox: String +) + +@Component +class DeliverBlockJob(@Qualifier("activitypub") private val objectMapper: ObjectMapper) : + HideoutJob("DeliverBlockJob") { + + val block = string("block") + val reject = string("reject") + val inbox = string("inbox") + val signer = long("signer") + + override fun convert(value: DeliverBlockJobParam): ScheduleContext.(DeliverBlockJob) -> Unit = { + props[block] = objectMapper.writeValueAsString(value.block) + props[reject] = objectMapper.writeValueAsString(value.reject) + props[reject] = value.inbox + props[signer] = value.signer + } + + override fun convert(props: JobProps): DeliverBlockJobParam = DeliverBlockJobParam( + props[signer], + objectMapper.readValue(props[block]), + objectMapper.readValue(props[reject]), + props[inbox] + ) + + +} From 40ae4e81ba64ece9c22cc40a0f339ae5e32f5c2c Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Fri, 8 Dec 2023 12:22:38 +0900 Subject: [PATCH 12/43] =?UTF-8?q?feat:=20Block=E3=82=A2=E3=82=AF=E3=83=86?= =?UTF-8?q?=E3=82=A3=E3=83=93=E3=83=86=E3=82=A3=E5=8F=97=E8=A8=BA=E6=99=82?= =?UTF-8?q?=E3=81=AE=E5=87=A6=E7=90=86=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../block/BlockActivityPubProcessor.kt | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/block/BlockActivityPubProcessor.kt diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/block/BlockActivityPubProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/block/BlockActivityPubProcessor.kt new file mode 100644 index 00000000..9dd2276d --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/block/BlockActivityPubProcessor.kt @@ -0,0 +1,29 @@ +package dev.usbharu.hideout.activitypub.service.activity.block + +import dev.usbharu.hideout.activitypub.domain.model.Block +import dev.usbharu.hideout.activitypub.service.common.AbstractActivityPubProcessor +import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessContext +import dev.usbharu.hideout.activitypub.service.common.ActivityType +import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.query.UserQueryService +import dev.usbharu.hideout.core.service.block.BlockService +import org.springframework.stereotype.Service + + +@Service +class BlockActivityPubProcessor( + private val blockService: BlockService, + private val userQueryService: UserQueryService, + transaction: Transaction +) : + AbstractActivityPubProcessor(transaction) { + override suspend fun internalProcess(activity: ActivityPubProcessContext) { + val user = userQueryService.findByUrl(activity.activity.actor) + val target = userQueryService.findByUrl(activity.activity.apObject) + blockService.block(user.id, target.id) + } + + override fun isSupported(activityType: ActivityType): Boolean = activityType == ActivityType.Block + + override fun type(): Class = Block::class.java +} From 5298d61497878c6ecc3c4bdf8a7d8977dec41bac Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Fri, 8 Dec 2023 12:28:21 +0900 Subject: [PATCH 13/43] =?UTF-8?q?feat:=20=E3=83=86=E3=83=BC=E3=83=96?= =?UTF-8?q?=E3=83=AB=E5=AE=9A=E7=BE=A9=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/db/migration/V2__Add_Block.sql | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/main/resources/db/migration/V2__Add_Block.sql diff --git a/src/main/resources/db/migration/V2__Add_Block.sql b/src/main/resources/db/migration/V2__Add_Block.sql new file mode 100644 index 00000000..d82d705e --- /dev/null +++ b/src/main/resources/db/migration/V2__Add_Block.sql @@ -0,0 +1,8 @@ +create table if not exists blocks +( + id bigserial primary key, + user_id bigint not null, + target bigint not null, + constraint fk_blocks_user_id__id foreign key (user_id) references users (id) on delete restrict on update restrict, + constraint fk_blocks_target_id__id foreign key (target) references users (id) on delete restrict on update restrict +); From 7249cf046ddf267382ec1e300ad0b492c6e50e68 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Fri, 8 Dec 2023 12:29:26 +0900 Subject: [PATCH 14/43] fix: #201 --- .../activitypub/domain/model/Create.kt | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Create.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Create.kt index d5b269dd..3a27e800 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Create.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Create.kt @@ -7,7 +7,7 @@ import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer open class Create( type: List = emptyList(), - override val name: String, + val name: String? = null, @JsonDeserialize(using = ObjectDeserializer::class) @JsonProperty("object") val apObject: Object, @@ -19,7 +19,6 @@ open class Create( type = add(type, "Create") ), HasId, - HasName, HasActor { override fun equals(other: Any?): Boolean { @@ -41,7 +40,7 @@ open class Create( override fun hashCode(): Int { var result = super.hashCode() - result = 31 * result + name.hashCode() + result = 31 * result + (name?.hashCode() ?: 0) result = 31 * result + apObject.hashCode() result = 31 * result + actor.hashCode() result = 31 * result + id.hashCode() @@ -52,13 +51,13 @@ open class Create( override fun toString(): String { return "Create(" + - "name='$name', " + - "apObject=$apObject, " + - "actor='$actor', " + - "id='$id', " + - "to=$to, " + - "cc=$cc" + - ")" + - " ${super.toString()}" + "name=$name, " + + "apObject=$apObject, " + + "actor='$actor', " + + "id='$id', " + + "to=$to, " + + "cc=$cc" + + ")" + + " ${super.toString()}" } } From 2f9126bb045f67ad0b7d74b72c4e21d3b42ba0a4 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Fri, 8 Dec 2023 12:46:52 +0900 Subject: [PATCH 15/43] =?UTF-8?q?feat:=20=E3=83=96=E3=83=AD=E3=83=83?= =?UTF-8?q?=E3=82=AF=E3=81=AEMastodon=20API=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/config/SecurityConfig.kt | 1 + .../core/service/block/BlockServiceImpl.kt | 6 +-- .../account/MastodonAccountApiController.kt | 10 +++++ .../service/account/AccountApiService.kt | 37 ++++++++++++++++++- src/main/resources/openapi/mastodon.yaml | 21 +++++++++++ 5 files changed, 71 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt b/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt index eb19802f..e42ad9b9 100644 --- a/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt +++ b/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt @@ -197,6 +197,7 @@ class SecurityConfig { authorize(GET, "/api/v1/accounts/*", permitAll) authorize(GET, "/api/v1/accounts/*/statuses", permitAll) authorize(POST, "/api/v1/accounts/*/follow", hasAnyScope("write", "write:follows")) + authorize(POST, "/api/v1/accounts/*/block", hasAnyScope("write", "write:blocks")) authorize(POST, "/api/v1/media", hasAnyScope("write", "write:media")) authorize(POST, "/api/v1/statuses", hasAnyScope("write", "write:statuses")) diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/block/BlockServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/block/BlockServiceImpl.kt index bc0eef96..77209855 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/block/BlockServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/block/BlockServiceImpl.kt @@ -28,7 +28,7 @@ class BlockServiceImpl( private val applicationConfig: ApplicationConfig ) : BlockService { - override suspend fun block(userId: Long, target: Long): Unit = transaction.transaction { + override suspend fun block(userId: Long, target: Long) { logger.debug("Block userId: {} → target: {}", userId, target) blockRepository.save(Block(userId, target)) if (followerQueryService.alreadyFollow(userId, target)) { @@ -39,13 +39,13 @@ class BlockServiceImpl( val user = userRepository.findById(userId) ?: throw IllegalStateException("Block user was not found.") if (user.domain == applicationConfig.url.host) { - return@transaction + return } val target = userRepository.findById(target) ?: throw IllegalStateException("Block use was not found.") if (target.domain == applicationConfig.url.host) { - return@transaction + return } val blockJobParam = DeliverBlockJobParam( diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt index a7f3741c..0ebed08e 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt @@ -103,4 +103,14 @@ class MastodonAccountApiController( .asFlow() ) } + + override suspend fun apiV1AccountsIdBlockPost(id: String): ResponseEntity { + val principal = SecurityContextHolder.getContext().getAuthentication().principal as Jwt + + val userid = principal.getClaim("uid").toLong() + + val block = accountApiService.block(userid, id.toLong()) + + return ResponseEntity.ok(block) + } } diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt index a3315107..2e9f76f4 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt @@ -1,8 +1,11 @@ package dev.usbharu.hideout.mastodon.service.account import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException +import dev.usbharu.hideout.core.domain.model.block.BlockRepository import dev.usbharu.hideout.core.domain.model.user.UserRepository import dev.usbharu.hideout.core.query.FollowerQueryService +import dev.usbharu.hideout.core.service.block.BlockService import dev.usbharu.hideout.core.service.user.UserCreateDto import dev.usbharu.hideout.core.service.user.UserService import dev.usbharu.hideout.domain.mastodon.model.generated.* @@ -32,6 +35,7 @@ interface AccountApiService { suspend fun follow(userid: Long, followeeId: Long): Relationship suspend fun account(id: Long): Account suspend fun relationships(userid: Long, id: List, withSuspended: Boolean): List + suspend fun block(userid: Long, target: Long): Relationship } @Service @@ -41,7 +45,9 @@ class AccountApiServiceImpl( private val userService: UserService, private val followerQueryService: FollowerQueryService, private val userRepository: UserRepository, - private val statusQueryService: StatusQueryService + private val statusQueryService: StatusQueryService, + private val blockService: BlockService, + private val blockRepository: BlockRepository ) : AccountApiService { override suspend fun accountsStatuses( @@ -160,6 +166,35 @@ class AccountApiServiceImpl( } } + override suspend fun block(userid: Long, target: Long): Relationship = transaction.transaction { + blockService.block(userid, target) + + val blocked = try { + blockRepository.findByUserIdAndTarget(target, userid) + true + } catch (e: FailedToGetResourcesException) { + false + } + + val requested = userRepository.findFollowRequestsById(target, userid) + + Relationship( + target.toString(), + false, + true, + false, + false, + true, + blocked, + false, + false, + requested, + false, + false, + "" + ) + } + private fun from(account: Account): CredentialAccount { return CredentialAccount( id = account.id, diff --git a/src/main/resources/openapi/mastodon.yaml b/src/main/resources/openapi/mastodon.yaml index 3049c1d9..592726ae 100644 --- a/src/main/resources/openapi/mastodon.yaml +++ b/src/main/resources/openapi/mastodon.yaml @@ -287,6 +287,27 @@ paths: schema: $ref: "#/components/schemas/Relationship" + /api/v1/accounts/{id}/block: + post: + tags: + - account + security: + - OAuth2: + - "write:blocks" + parameters: + - in: path + name: id + required: true + schema: + type: string + responses: + 200: + description: 成功 + content: + application/json: + schema: + $ref: "#/components/schemas/Relationship" + /api/v1/accounts/{id}/statuses: get: tags: From 0b27b0cb7b2731c0f49825840348af14a4266d85 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Fri, 8 Dec 2023 12:59:43 +0900 Subject: [PATCH 16/43] =?UTF-8?q?doc:=20=E3=83=89=E3=82=AD=E3=83=A5?= =?UTF-8?q?=E3=83=A1=E3=83=B3=E3=83=88=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../block/APDeliverBlockJobProcessor.kt | 3 +++ .../block/BlockActivityPubProcessor.kt | 3 +++ .../hideout/core/domain/model/block/Block.kt | 6 +++++ .../domain/model/block/BlockRepository.kt | 24 +++++++++++++++++++ .../core/external/job/DeliverBlockJob.kt | 11 +++++++++ .../core/service/block/BlockService.kt | 19 +++++++++++++++ .../service/account/AccountApiService.kt | 8 +++++++ 7 files changed, 74 insertions(+) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/block/APDeliverBlockJobProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/block/APDeliverBlockJobProcessor.kt index 0b56ac8c..37236c0f 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/block/APDeliverBlockJobProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/block/APDeliverBlockJobProcessor.kt @@ -8,6 +8,9 @@ import dev.usbharu.hideout.core.external.job.DeliverBlockJobParam import dev.usbharu.hideout.core.service.job.JobProcessor import org.springframework.stereotype.Service +/** + * ブロックアクティビティ配送を処理します + */ @Service class APDeliverBlockJobProcessor( private val apRequestService: APRequestService, diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/block/BlockActivityPubProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/block/BlockActivityPubProcessor.kt index 9dd2276d..1bb40c19 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/block/BlockActivityPubProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/block/BlockActivityPubProcessor.kt @@ -10,6 +10,9 @@ import dev.usbharu.hideout.core.service.block.BlockService import org.springframework.stereotype.Service +/** + * ブロックアクティビティを処理します + */ @Service class BlockActivityPubProcessor( private val blockService: BlockService, diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/block/Block.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/block/Block.kt index a2ddf940..7435183e 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/block/Block.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/block/Block.kt @@ -1,5 +1,11 @@ package dev.usbharu.hideout.core.domain.model.block +/** + * ブロック関係を表します + * + * @property userId ブロックの動作を行ったユーザーid + * @property target ブロックの対象のユーザーid + */ data class Block( val userId: Long, val target: Long diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/block/BlockRepository.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/block/BlockRepository.kt index a847bb4a..3504ffea 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/block/BlockRepository.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/block/BlockRepository.kt @@ -1,7 +1,31 @@ package dev.usbharu.hideout.core.domain.model.block +/** + * ブロックの状態を永続化します + * + */ interface BlockRepository { + /** + * ブロックの状態を永続化します + * + * @param block 永続化するブロック + * @return 永続化されたブロック + */ suspend fun save(block: Block): Block + + /** + * ブロックの状態を削除します + * + * @param block 削除する永続化されたブロック + */ suspend fun delete(block: Block) + + /** + * + * + * @param userId + * @param target + * @return + */ suspend fun findByUserIdAndTarget(userId: Long, target: Long): Block } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverBlockJob.kt b/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverBlockJob.kt index 8f55831e..3e8688b9 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverBlockJob.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverBlockJob.kt @@ -9,6 +9,14 @@ import kjob.core.job.JobProps import org.springframework.beans.factory.annotation.Qualifier import org.springframework.stereotype.Component +/** + * ブロックアクティビティ配送のジョブパラメーター + * + * @property signer ブロック操作を行ったユーザーid + * @property block 配送するブロックアクティビティ + * @property reject 配送するフォロー解除アクティビティ + * @property inbox 配送先url + */ data class DeliverBlockJobParam( val signer: Long, val block: Block, @@ -16,6 +24,9 @@ data class DeliverBlockJobParam( val inbox: String ) +/** + * ブロックアクティビティ配送のジョブ + */ @Component class DeliverBlockJob(@Qualifier("activitypub") private val objectMapper: ObjectMapper) : HideoutJob("DeliverBlockJob") { diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/block/BlockService.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/block/BlockService.kt index 8988a15d..83d1512c 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/block/BlockService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/block/BlockService.kt @@ -1,6 +1,25 @@ package dev.usbharu.hideout.core.service.block +/** + * ブロックに関する処理を行います + * + */ interface BlockService { + /** + * ブロックします + * 実装はリモートユーザーへのブロックの場合ブロックアクティビティを配送するべきです。 + * + * @param userId ブロックの動作を行ったユーザーid + * @param target ブロック対象のユーザーid + */ suspend fun block(userId: Long, target: Long) + + /** + * ブロックを解除します + * 実装はリモートユーザーへのブロック解除の場合Undo Blockアクティビティを配送するべきです + * + * @param userId ブロック解除の動作を行ったユーザーid + * @param target ブロック解除の対象のユーザーid + */ suspend fun unblock(userId: Long, target: Long) } diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt index 2e9f76f4..15d7f308 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt @@ -35,6 +35,14 @@ interface AccountApiService { suspend fun follow(userid: Long, followeeId: Long): Relationship suspend fun account(id: Long): Account suspend fun relationships(userid: Long, id: List, withSuspended: Boolean): List + + /** + * ブロック操作を行う + * + * @param userid ブロック操作を行ったユーザーid + * @param target ブロック対象のユーザーid + * @return ブロック後のブロック対象ユーザーとの[Relationship] + */ suspend fun block(userid: Long, target: Long): Relationship } From 606cf3ec128c6e24ab393ff761eafde85ac7b935 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Fri, 8 Dec 2023 13:12:40 +0900 Subject: [PATCH 17/43] =?UTF-8?q?feat:=20=E3=83=96=E3=83=AD=E3=83=83?= =?UTF-8?q?=E3=82=AF=E6=93=8D=E4=BD=9C=E3=83=A6=E3=83=BC=E3=82=B6=E3=83=BC?= =?UTF-8?q?=E3=81=A8=E3=83=96=E3=83=AD=E3=83=83=E3=82=AF=E5=AF=BE=E8=B1=A1?= =?UTF-8?q?=E3=83=A6=E3=83=BC=E3=82=B6=E3=83=BC=E3=81=8C=E4=B8=A1=E6=96=B9?= =?UTF-8?q?=E3=83=AA=E3=83=A2=E3=83=BC=E3=83=88=E3=81=AB=E3=81=AA=E3=81=A3?= =?UTF-8?q?=E3=81=A6=E3=81=84=E3=82=8B=E7=95=B0=E5=B8=B8=E3=81=AA=E3=82=A2?= =?UTF-8?q?=E3=82=AF=E3=83=86=E3=82=A3=E3=83=93=E3=83=86=E3=82=A3=E3=82=92?= =?UTF-8?q?=E7=84=A1=E8=A6=96=E3=81=99=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 --- .../core/service/block/BlockServiceImpl.kt | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/block/BlockServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/block/BlockServiceImpl.kt index 77209855..761308e4 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/block/BlockServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/block/BlockServiceImpl.kt @@ -30,21 +30,28 @@ class BlockServiceImpl( BlockService { override suspend fun block(userId: Long, target: Long) { logger.debug("Block userId: {} → target: {}", userId, target) + + val user = userRepository.findById(userId) ?: throw IllegalStateException("Block user was not found.") + + val targetEntity = userRepository.findById(target) ?: throw IllegalStateException("Block use was not found.") + + if (user.domain != applicationConfig.url.host && targetEntity.domain != applicationConfig.url.host) { + logger.warn("Invalid Block activity. Both user and target are remote users.") + return + } + blockRepository.save(Block(userId, target)) if (followerQueryService.alreadyFollow(userId, target)) { logger.debug("Unfollow (Block) userId: {} → target: {}", userId, target) userService.unfollow(userId, target) } - val user = userRepository.findById(userId) ?: throw IllegalStateException("Block user was not found.") if (user.domain == applicationConfig.url.host) { return } - val target = userRepository.findById(target) ?: throw IllegalStateException("Block use was not found.") - - if (target.domain == applicationConfig.url.host) { + if (targetEntity.domain == applicationConfig.url.host) { return } @@ -52,18 +59,18 @@ class BlockServiceImpl( user.id, dev.usbharu.hideout.activitypub.domain.model.Block( user.url, - "${applicationConfig.url}/block/${user.id}/${target.id}", - target.url + "${applicationConfig.url}/block/${user.id}/${targetEntity.id}", + targetEntity.url ), Reject( user.url, - "${applicationConfig.url}/reject/${user.id}/${target.id}", + "${applicationConfig.url}/reject/${user.id}/${targetEntity.id}", Follow( apObject = user.url, - actor = target.url + actor = targetEntity.url ) ), - target.inbox + targetEntity.inbox ) jobQueueParentService.scheduleTypeSafe(deliverBlockJob, blockJobParam) } From 136c238d206e0ce1c53fe8dbfeb6d7856701957d Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sat, 9 Dec 2023 21:09:52 +0900 Subject: [PATCH 18/43] =?UTF-8?q?feat:=20=E3=83=A6=E3=83=BC=E3=82=B6?= =?UTF-8?q?=E3=83=BC=E9=96=93=E3=81=AE=E9=96=A2=E4=BF=82=E3=82=92Relations?= =?UTF-8?q?hipService=E3=81=A7=E7=AE=A1=E7=90=86=E3=81=99=E3=82=8B?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../activity/accept/ApSendAcceptService.kt | 16 ++ .../activity/block/APSendBlockService.kt | 43 +++ .../activity/reject/ApSendRejectService.kt | 7 + .../activity/undo/APSendUndoService.kt | 8 + .../domain/model/relationship/Relationship.kt | 22 ++ .../relationship/RelationshipRepository.kt | 31 ++ .../relationship/RelationshipService.kt | 14 + .../relationship/RelationshipServiceImpl.kt | 266 ++++++++++++++++++ 8 files changed, 407 insertions(+) create mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApSendAcceptService.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/block/APSendBlockService.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/ApSendRejectService.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APSendUndoService.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/Relationship.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepository.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipService.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImpl.kt diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApSendAcceptService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApSendAcceptService.kt new file mode 100644 index 00000000..59f4f4ec --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApSendAcceptService.kt @@ -0,0 +1,16 @@ +package dev.usbharu.hideout.activitypub.service.activity.accept + +import dev.usbharu.hideout.core.domain.model.user.User +import org.springframework.stereotype.Service + +interface ApSendAcceptService { + suspend fun sendAccept(user: User, target: User) +} + +@Service +class ApSendAcceptServiceImpl : ApSendAcceptService { + override suspend fun sendAccept(user: User, target: User) { + TODO("Not yet implemented") + } + +} diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/block/APSendBlockService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/block/APSendBlockService.kt new file mode 100644 index 00000000..f814da7e --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/block/APSendBlockService.kt @@ -0,0 +1,43 @@ +package dev.usbharu.hideout.activitypub.service.activity.block + +import dev.usbharu.hideout.activitypub.domain.model.Block +import dev.usbharu.hideout.activitypub.domain.model.Follow +import dev.usbharu.hideout.activitypub.domain.model.Reject +import dev.usbharu.hideout.application.config.ApplicationConfig +import dev.usbharu.hideout.core.domain.model.user.User +import dev.usbharu.hideout.core.external.job.DeliverBlockJob +import dev.usbharu.hideout.core.external.job.DeliverBlockJobParam +import dev.usbharu.hideout.core.service.job.JobQueueParentService +import org.springframework.stereotype.Service + +interface APSendBlockService { + suspend fun sendBlock(user: User, target: User) +} + +@Service +class ApSendBlockServiceImpl( + private val applicationConfig: ApplicationConfig, + private val jobQueueParentService: JobQueueParentService, + private val deliverBlockJob: DeliverBlockJob +) : APSendBlockService { + override suspend fun sendBlock(user: User, target: User) { + val blockJobParam = DeliverBlockJobParam( + user.id, + Block( + user.url, + "${applicationConfig.url}/block/${user.id}/${target.id}", + target.url + ), + Reject( + user.url, + "${applicationConfig.url}/reject/${user.id}/${target.id}", + Follow( + apObject = user.url, + actor = target.url + ) + ), + target.inbox + ) + jobQueueParentService.scheduleTypeSafe(deliverBlockJob, blockJobParam) + } +} diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/ApSendRejectService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/ApSendRejectService.kt new file mode 100644 index 00000000..29c66e28 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/ApSendRejectService.kt @@ -0,0 +1,7 @@ +package dev.usbharu.hideout.activitypub.service.activity.reject + +import dev.usbharu.hideout.core.domain.model.user.User + +interface ApSendRejectService { + suspend fun sendReject(user: User, target: User) +} diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APSendUndoService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APSendUndoService.kt new file mode 100644 index 00000000..827b186e --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APSendUndoService.kt @@ -0,0 +1,8 @@ +package dev.usbharu.hideout.activitypub.service.activity.undo + +import dev.usbharu.hideout.core.domain.model.user.User + +interface APSendUndoService { + suspend fun sendUndoFollow(user: User, target: User) + suspend fun sendUndoBlock(user: User, target: User) +} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/Relationship.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/Relationship.kt new file mode 100644 index 00000000..51a3da60 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/Relationship.kt @@ -0,0 +1,22 @@ +package dev.usbharu.hideout.core.domain.model.relationship + +/** + * ユーザーとの関係を表します + * + * @property userId ユーザー + * @property targetUserId 相手ユーザー + * @property following フォローしているか + * @property blocking ブロックしているか + * @property muting ミュートしているか + * @property followRequest フォローリクエストを送っているか + * @property ignoreFollowRequestFromTarget フォローリクエストを無視しているか + */ +data class Relationship( + val userId: Long, + val targetUserId: Long, + val following: Boolean, + val blocking: Boolean, + val muting: Boolean, + val followRequest: Boolean, + val ignoreFollowRequestFromTarget: Boolean +) diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepository.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepository.kt new file mode 100644 index 00000000..4b3fc6bc --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepository.kt @@ -0,0 +1,31 @@ +package dev.usbharu.hideout.core.domain.model.relationship + +/** + * [Relationship]の永続化 + * + */ +interface RelationshipRepository { + /** + * 永続化します + * + * @param relationship 永続化する[Relationship] + * @return 永続化された[Relationship] + */ + suspend fun save(relationship: Relationship): Relationship + + /** + * 永続化されたものを削除します + * + * @param relationship 削除する[Relationship] + */ + suspend fun delete(relationship: Relationship) + + /** + * userIdとtargetUserIdで[Relationship]を取得します + * + * @param userId 取得するユーザーID + * @param targetUserId 対象ユーザーID + * @return 取得された[Relationship] 存在しない場合nullが返ります + */ + suspend fun findByUserIdAndTargetUserId(userId: Long, targetUserId: Long): Relationship? +} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipService.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipService.kt new file mode 100644 index 00000000..4f0a164a --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipService.kt @@ -0,0 +1,14 @@ +package dev.usbharu.hideout.core.service.relationship + +interface RelationshipService { + suspend fun followRequest(userId: Long, targetId: Long) + suspend fun block(userId: Long, targetId: Long) + suspend fun acceptFollowRequest(userId: Long, targetId: Long) + suspend fun rejectFollowRequest(userId: Long, targetId: Long) + suspend fun ignoreFollowRequest(userId: Long, targetId: Long) + suspend fun unfollow(userId: Long, targetId: Long) + suspend fun unblock(userId: Long, targetId: Long) + suspend fun mute(userId: Long, targetId: Long) + suspend fun unmute(userId: Long, targetId: Long) + +} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImpl.kt new file mode 100644 index 00000000..1ee53312 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImpl.kt @@ -0,0 +1,266 @@ +package dev.usbharu.hideout.core.service.relationship + +import dev.usbharu.hideout.activitypub.service.activity.accept.ApSendAcceptService +import dev.usbharu.hideout.activitypub.service.activity.block.APSendBlockService +import dev.usbharu.hideout.activitypub.service.activity.follow.APSendFollowService +import dev.usbharu.hideout.activitypub.service.activity.reject.ApSendRejectService +import dev.usbharu.hideout.activitypub.service.activity.undo.APSendUndoService +import dev.usbharu.hideout.application.config.ApplicationConfig +import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException +import dev.usbharu.hideout.core.domain.model.relationship.Relationship +import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository +import dev.usbharu.hideout.core.domain.model.user.User +import dev.usbharu.hideout.core.query.UserQueryService +import dev.usbharu.hideout.core.service.follow.SendFollowDto +import org.slf4j.LoggerFactory +import org.springframework.stereotype.Service + +@Service +class RelationshipServiceImpl( + private val applicationConfig: ApplicationConfig, + private val userQueryService: UserQueryService, + private val relationshipRepository: RelationshipRepository, + private val apSendFollowService: APSendFollowService, + private val apSendBlockService: APSendBlockService, + private val apSendAcceptService: ApSendAcceptService, + private val apSendRejectService: ApSendRejectService, + private val apSendUndoService: APSendUndoService +) : RelationshipService { + override suspend fun followRequest(userId: Long, targetId: Long) { + val relationship = + relationshipRepository.findByUserIdAndTargetUserId(userId, targetId)?.copy(followRequest = true) + ?: Relationship( + userId = userId, + targetUserId = targetId, + following = false, + blocking = false, + muting = false, + followRequest = true, + ignoreFollowRequestFromTarget = false + ) + + val inverseRelationship = relationshipRepository.findByUserIdAndTargetUserId(targetId, userId) ?: Relationship( + userId = targetId, + targetUserId = userId, + following = false, + blocking = false, + muting = false, + followRequest = false, + ignoreFollowRequestFromTarget = false + ) + + if (inverseRelationship.blocking) { + logger.debug("FAILED Blocked by target. userId: {} targetId: {}", userId, targetId) + return + } + + if (relationship.blocking) { + logger.debug("FAILED Blocking user. userId: {} targetId: {}", userId, targetId) + return + } + if (inverseRelationship.ignoreFollowRequestFromTarget) { + logger.debug("SUCCESS Ignore Follow Request. userId: {} targetId: {}", userId, targetId) + return + } + + + relationshipRepository.save(relationship) + + val remoteUser = isRemoteUser(targetId) + + if (remoteUser != null) { + val user = userQueryService.findById(userId) + apSendFollowService.sendFollow(SendFollowDto(user, remoteUser)) + } else { + //TODO: フォロー許可制ユーザーを実装したら消す + acceptFollowRequest(userId, targetId) + } + + } + + override suspend fun block(userId: Long, targetId: Long) { + val relationship = relationshipRepository.findByUserIdAndTargetUserId(userId, targetId) + ?.copy(blocking = true, followRequest = false, following = false) ?: Relationship( + userId = userId, + targetUserId = targetId, + following = false, + blocking = true, + muting = false, + followRequest = false, + ignoreFollowRequestFromTarget = false + ) + + relationshipRepository.save(relationship) + + val remoteUser = isRemoteUser(targetId) + + if (remoteUser != null) { + val user = userQueryService.findById(userId) + apSendBlockService.sendBlock(user, remoteUser) + } + } + + override suspend fun acceptFollowRequest(userId: Long, targetId: Long) { + val relationship = relationshipRepository.findByUserIdAndTargetUserId(userId, targetId) + + if (relationship == null) { + logger.warn("FAILED Follow Request Not Found. (Relationship) userId: {} targetId: {}", userId, targetId) + return + } + + if (relationship.followRequest.not()) { + logger.warn("FAILED Follow Request Not Found. (Follow Request) userId: {} targetId: {}", userId, targetId) + return + } + + if (relationship.blocking) { + logger.warn("FAILED Blocking user userId: {} targetId: {}", userId, targetId) + throw IllegalStateException("Cannot accept a follow request from a blocked user. userId: $userId targetId: $targetId") + } + + val copy = relationship.copy(followRequest = false, following = true, blocking = false) + + relationshipRepository.save(copy) + + val remoteUser = isRemoteUser(targetId) + + if (remoteUser != null) { + val user = userQueryService.findById(userId) + apSendAcceptService.sendAccept(user, remoteUser) + } + } + + override suspend fun rejectFollowRequest(userId: Long, targetId: Long) { + val relationship = relationshipRepository.findByUserIdAndTargetUserId(userId, targetId) + + if (relationship == null) { + logger.warn("FAILED Follow Request Not Found. (Relationship) userId: {} targetId: {}", userId, targetId) + return + } + + if (relationship.followRequest.not()) { + logger.warn("FAILED Follow Request Not Found. (Follow Request) userId: {} targetId: {}", userId, targetId) + return + } + + val copy = relationship.copy(followRequest = false, following = false, blocking = false) + + relationshipRepository.save(copy) + + val remoteUser = isRemoteUser(targetId) + + if (remoteUser != null) { + val user = userQueryService.findById(userId) + apSendRejectService.sendReject(user, remoteUser) + } + } + + override suspend fun ignoreFollowRequest(userId: Long, targetId: Long) { + val relationship = relationshipRepository.findByUserIdAndTargetUserId(userId, targetId) + ?.copy(ignoreFollowRequestFromTarget = true) + ?: Relationship( + userId = userId, + targetUserId = targetId, + following = false, + blocking = false, + muting = false, + followRequest = false, + ignoreFollowRequestFromTarget = true + ) + + relationshipRepository.save(relationship) + } + + override suspend fun unfollow(userId: Long, targetId: Long) { + val relationship = relationshipRepository.findByUserIdAndTargetUserId(userId, targetId) + + if (relationship == null) { + logger.warn("FAILED Unfollow. (Relationship) userId: {} targetId: {}", userId, targetId) + return + } + + if (relationship.following.not()) { + logger.warn("SUCCESS User already unfollow. userId: {} targetId: {}", userId, targetId) + return + } + + val copy = relationship.copy(following = false) + + relationshipRepository.save(copy) + + val remoteUser = isRemoteUser(targetId) + + if (remoteUser != null) { + val user = userQueryService.findById(userId) + apSendUndoService.sendUndoFollow(user, remoteUser) + } + } + + override suspend fun unblock(userId: Long, targetId: Long) { + val relationship = relationshipRepository.findByUserIdAndTargetUserId(userId, targetId) + + if (relationship == null) { + logger.warn("FAILED Unblock. (Relationship) userId: {} targetId: {}", userId, targetId) + return + } + + if (relationship.blocking.not()) { + logger.warn("SUCCESS User is not blocking. userId: {] targetId: {}", userId, targetId) + return + } + + val copy = relationship.copy(blocking = false) + relationshipRepository.save(copy) + + + val remoteUser = isRemoteUser(targetId) + if (remoteUser == null) { + val user = userQueryService.findById(userId) + apSendUndoService.sendUndoBlock(user, targetId) + } + } + + override suspend fun mute(userId: Long, targetId: Long) { + val relationship = relationshipRepository.findByUserIdAndTargetUserId(userId, targetId)?.copy(muting = true) + ?: Relationship( + userId = userId, + targetUserId = targetId, + following = false, + blocking = false, + muting = true, + followRequest = false, + ignoreFollowRequestFromTarget = false + ) + + relationshipRepository.save(relationship) + } + + override suspend fun unmute(userId: Long, targetId: Long) { + val relationship = relationshipRepository.findByUserIdAndTargetUserId(userId, targetId)?.copy(muting = false) + + if (relationship == null) { + logger.warn("FAILED Mute. (Relationship) userId: {} targetId: {}", userId, targetId) + return + } + + relationshipRepository.save(relationship) + } + + private suspend fun isRemoteUser(userId: Long): User? { + val user = try { + userQueryService.findById(userId) + } catch (e: FailedToGetResourcesException) { + logger.warn("User not found.", e) + throw IllegalStateException("User not found.", e) + } + + if (user.domain == applicationConfig.url.host) { + return null + } + return user + } + + companion object { + private val logger = LoggerFactory.getLogger(RelationshipServiceImpl::class.java) + } +} From 9d0ed309d0f329fa6fc3e04e199d7d52b9e9b290 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 10 Dec 2023 14:32:13 +0900 Subject: [PATCH 19/43] =?UTF-8?q?feat:=20=E3=83=AA=E3=83=A2=E3=83=BC?= =?UTF-8?q?=E3=83=88=E3=81=A8=E3=81=AE=E4=B8=8D=E6=95=B4=E5=90=88=E3=81=AE?= =?UTF-8?q?=E8=A7=A3=E6=B1=BA=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle.kts | 2 +- .../relationship/RelationshipService.kt | 2 +- .../relationship/RelationshipServiceImpl.kt | 30 +++++++++++++++---- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index c8d31ca7..9c892615 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -287,7 +287,7 @@ project.gradle.taskGraph.whenReady { kover { excludeSourceSets { - names("aot") + names("aot", "e2eTest", "intTest") } } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipService.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipService.kt index 4f0a164a..663ec70a 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipService.kt @@ -3,7 +3,7 @@ package dev.usbharu.hideout.core.service.relationship interface RelationshipService { suspend fun followRequest(userId: Long, targetId: Long) suspend fun block(userId: Long, targetId: Long) - suspend fun acceptFollowRequest(userId: Long, targetId: Long) + suspend fun acceptFollowRequest(userId: Long, targetId: Long, force: Boolean = false) suspend fun rejectFollowRequest(userId: Long, targetId: Long) suspend fun ignoreFollowRequest(userId: Long, targetId: Long) suspend fun unfollow(userId: Long, targetId: Long) diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImpl.kt index 1ee53312..9e3b98ce 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImpl.kt @@ -63,6 +63,11 @@ class RelationshipServiceImpl( return } + if (relationship.following) { + logger.debug("SUCCESS User already follow. userId: {} targetId: {}", userId, targetId) + acceptFollowRequest(userId, targetId, true) + return + } relationshipRepository.save(relationship) @@ -100,15 +105,25 @@ class RelationshipServiceImpl( } } - override suspend fun acceptFollowRequest(userId: Long, targetId: Long) { + override suspend fun acceptFollowRequest(userId: Long, targetId: Long, force: Boolean) { val relationship = relationshipRepository.findByUserIdAndTargetUserId(userId, targetId) + val inverseRelationship = relationshipRepository.findByUserIdAndTargetUserId(targetId, userId) ?: Relationship( + userId = targetId, + targetUserId = userId, + following = false, + blocking = false, + muting = false, + followRequest = false, + ignoreFollowRequestFromTarget = false + ) + if (relationship == null) { logger.warn("FAILED Follow Request Not Found. (Relationship) userId: {} targetId: {}", userId, targetId) return } - if (relationship.followRequest.not()) { + if (relationship.followRequest.not() && force.not()) { logger.warn("FAILED Follow Request Not Found. (Follow Request) userId: {} targetId: {}", userId, targetId) return } @@ -118,6 +133,11 @@ class RelationshipServiceImpl( throw IllegalStateException("Cannot accept a follow request from a blocked user. userId: $userId targetId: $targetId") } + if (inverseRelationship.blocking) { + logger.warn("FAILED BLocked by user userId: {} targetId: {}", userId, targetId) + throw IllegalStateException("Cannot accept a follow request from a blocking user. userId: $userId targetId: $targetId") + } + val copy = relationship.copy(followRequest = false, following = true, blocking = false) relationshipRepository.save(copy) @@ -143,7 +163,7 @@ class RelationshipServiceImpl( return } - val copy = relationship.copy(followRequest = false, following = false, blocking = false) + val copy = relationship.copy(followRequest = false, following = false) relationshipRepository.save(copy) @@ -214,9 +234,9 @@ class RelationshipServiceImpl( val remoteUser = isRemoteUser(targetId) - if (remoteUser == null) { + if (remoteUser != null) { val user = userQueryService.findById(userId) - apSendUndoService.sendUndoBlock(user, targetId) + apSendUndoService.sendUndoBlock(user, remoteUser) } } From c1397471db9537115671e7b0085d9c2c476e44c6 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 10 Dec 2023 14:32:43 +0900 Subject: [PATCH 20/43] =?UTF-8?q?test:=20RelationshipServiceImplTest?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RelationshipServiceImplTest.kt | 786 ++++++++++++++++++ 1 file changed, 786 insertions(+) create mode 100644 src/test/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImplTest.kt diff --git a/src/test/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImplTest.kt new file mode 100644 index 00000000..5f1d6141 --- /dev/null +++ b/src/test/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImplTest.kt @@ -0,0 +1,786 @@ +package dev.usbharu.hideout.core.service.relationship + +import dev.usbharu.hideout.activitypub.service.activity.accept.ApSendAcceptService +import dev.usbharu.hideout.activitypub.service.activity.block.APSendBlockService +import dev.usbharu.hideout.activitypub.service.activity.follow.APSendFollowService +import dev.usbharu.hideout.activitypub.service.activity.reject.ApSendRejectService +import dev.usbharu.hideout.activitypub.service.activity.undo.APSendUndoService +import dev.usbharu.hideout.application.config.ApplicationConfig +import dev.usbharu.hideout.core.domain.model.relationship.Relationship +import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository +import dev.usbharu.hideout.core.query.UserQueryService +import dev.usbharu.hideout.core.service.follow.SendFollowDto +import kotlinx.coroutines.test.runTest +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.api.extension.ExtendWith +import org.mockito.InjectMocks +import org.mockito.Mock +import org.mockito.Spy +import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.kotlin.* +import utils.UserBuilder +import java.net.URL + +@ExtendWith(MockitoExtension::class) +class RelationshipServiceImplTest { + + @Spy + private val applicationConfig = ApplicationConfig(URL("https://example.com")) + + @Mock + private lateinit var userQueryService: UserQueryService + + @Mock + private lateinit var relationshipRepository: RelationshipRepository + + @Mock + private lateinit var apSendFollowService: APSendFollowService + + @Mock + private lateinit var apSendBlockService: APSendBlockService + + @Mock + private lateinit var apSendAcceptService: ApSendAcceptService + + @Mock + private lateinit var apSendRejectService: ApSendRejectService + + @Mock + private lateinit var apSendUndoService: APSendUndoService + + @InjectMocks + private lateinit var relationshipServiceImpl: RelationshipServiceImpl + + @Test + fun `followRequest ローカルの場合followRequestフラグがtrueで永続化される`() = runTest { + whenever(userQueryService.findById(eq(5678))).doReturn(UserBuilder.localUserOf(domain = "example.com")) + + relationshipServiceImpl.followRequest(1234, 5678) + + verify(relationshipRepository, times(1)).save( + eq( + Relationship( + userId = 1234, + targetUserId = 5678, + following = false, + blocking = false, + muting = false, + followRequest = true, + ignoreFollowRequestFromTarget = false + ) + ) + ) + } + + @Test + fun `followRequest リモートの場合Followアクティビティが配送される`() = runTest { + val localUser = UserBuilder.localUserOf(domain = "example.com") + whenever(userQueryService.findById(eq(1234))).doReturn(localUser) + val remoteUser = UserBuilder.remoteUserOf(domain = "remote.example.com") + whenever(userQueryService.findById(eq(5678))).doReturn(remoteUser) + + relationshipServiceImpl.followRequest(1234, 5678) + + verify(relationshipRepository, times(1)).save( + eq( + Relationship( + userId = 1234, + targetUserId = 5678, + following = false, + blocking = false, + muting = false, + followRequest = true, + ignoreFollowRequestFromTarget = false + ) + ) + ) + + verify(apSendFollowService, times(1)).sendFollow(eq(SendFollowDto(localUser, remoteUser))) + } + + @Test + fun `followRequest ブロックされている場合フォローリクエスト出来ない`() = runTest { + whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn(null) + whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(5678), eq(1234))).doReturn( + Relationship( + userId = 5678, + targetUserId = 1234, + following = false, + blocking = true, + muting = false, + followRequest = false, + ignoreFollowRequestFromTarget = false + ) + ) + + relationshipServiceImpl.followRequest(1234, 5678) + + verify(relationshipRepository, never()).save(any()) + } + + @Test + fun `followRequest ブロックしている場合フォローリクエスト出来ない`() = runTest { + whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn( + Relationship( + userId = 1234, + targetUserId = 5678, + following = false, + blocking = true, + muting = false, + followRequest = false, + ignoreFollowRequestFromTarget = false + ) + ) + + relationshipServiceImpl.followRequest(1234, 5678) + + verify(relationshipRepository, never()).save(any()) + } + + @Test + fun `followRequest 既にフォローしている場合は念の為フォロー承認を自動で行う`() = runTest { + val localUser = UserBuilder.localUserOf(domain = "example.com") + whenever(userQueryService.findById(eq(1234))).doReturn(localUser) + val remoteUser = UserBuilder.remoteUserOf(domain = "remote.example.com") + whenever(userQueryService.findById(eq(5678))).doReturn(remoteUser) + whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn( + Relationship( + userId = 1234, + targetUserId = 5678, + following = true, + blocking = false, + muting = false, + followRequest = false, + ignoreFollowRequestFromTarget = false + ) + ) + + relationshipServiceImpl.followRequest(1234, 5678) + + verify(relationshipRepository, times(1)).save( + eq( + Relationship( + userId = 1234, + targetUserId = 5678, + following = true, + blocking = false, + muting = false, + followRequest = false, + ignoreFollowRequestFromTarget = false + ) + ) + ) + + verify(apSendAcceptService, times(1)).sendAccept(eq(localUser), eq(remoteUser)) + verify(apSendFollowService, never()).sendFollow(any()) + } + + @Test + fun `followRequest フォローリクエスト無視の場合は無視する`() = runTest { + whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn( + Relationship( + userId = 1234, + targetUserId = 5678, + following = false, + blocking = false, + muting = false, + followRequest = false, + ignoreFollowRequestFromTarget = false + ) + ) + + whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(5678), eq(1234))).doReturn( + Relationship( + userId = 5678, + targetUserId = 1234, + following = false, + blocking = false, + muting = false, + followRequest = false, + ignoreFollowRequestFromTarget = true + ) + ) + + relationshipServiceImpl.followRequest(1234, 5678) + + verify(relationshipRepository, never()).save(any()) + } + + @Test + fun `block ローカルユーザーの場合永続化される`() = runTest { + whenever(userQueryService.findById(eq(5678))).doReturn(UserBuilder.localUserOf(domain = "example.com")) + + relationshipServiceImpl.block(1234, 5678) + + verify(relationshipRepository, times(1)).save( + eq( + Relationship( + userId = 1234, + targetUserId = 5678, + following = false, + blocking = true, + muting = false, + followRequest = false, + ignoreFollowRequestFromTarget = false + ) + ) + ) + } + + @Test + fun `block リモートユーザーの場合永続化されて配送される`() = runTest { + val localUser = UserBuilder.localUserOf(domain = "example.com") + whenever(userQueryService.findById(eq(1234))).doReturn(localUser) + val remoteUser = UserBuilder.remoteUserOf(domain = "remote.example.com") + whenever(userQueryService.findById(eq(5678))).doReturn(remoteUser) + + relationshipServiceImpl.block(1234, 5678) + + verify(relationshipRepository, times(1)).save( + eq( + Relationship( + userId = 1234, + targetUserId = 5678, + following = false, + blocking = true, + muting = false, + followRequest = false, + ignoreFollowRequestFromTarget = false + ) + ) + ) + + verify(apSendBlockService, times(1)).sendBlock(eq(localUser), eq(remoteUser)) + } + + @Test + fun `acceptFollowRequest ローカルユーザーの場合永続化される`() = runTest { + whenever(userQueryService.findById(eq(5678))).doReturn(UserBuilder.localUserOf(domain = "example.com")) + + whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn( + Relationship( + userId = 1234, + targetUserId = 5678, + following = false, + blocking = false, + muting = false, + followRequest = true, + ignoreFollowRequestFromTarget = false + ) + ) + + relationshipServiceImpl.acceptFollowRequest(1234, 5678, false) + + verify(relationshipRepository, times(1)).save( + Relationship( + userId = 1234, + targetUserId = 5678, + following = true, + blocking = false, + muting = false, + followRequest = false, + ignoreFollowRequestFromTarget = false + ) + ) + + verify(apSendAcceptService, never()).sendAccept(any(), any()) + } + + @Test + fun `acceptFollowRequest リモートユーザーの場合永続化されて配送される`() = runTest { + val localUser = UserBuilder.localUserOf(domain = "example.com") + whenever(userQueryService.findById(eq(1234))).doReturn(localUser) + val remoteUser = UserBuilder.remoteUserOf(domain = "remote.example.com") + whenever(userQueryService.findById(eq(5678))).doReturn(remoteUser) + + whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn( + Relationship( + userId = 1234, + targetUserId = 5678, + following = false, + blocking = false, + muting = false, + followRequest = true, + ignoreFollowRequestFromTarget = false + ) + ) + + relationshipServiceImpl.acceptFollowRequest(1234, 5678, false) + + verify(relationshipRepository, times(1)).save( + eq( + Relationship( + userId = 1234, + targetUserId = 5678, + following = true, + blocking = false, + muting = false, + followRequest = false, + ignoreFollowRequestFromTarget = false + ) + ) + ) + + verify(apSendAcceptService, times(1)).sendAccept(eq(localUser), eq(remoteUser)) + } + + @Test + fun `acceptFollowRequest Relationshipが存在しないときは何もしない`() = runTest { + relationshipServiceImpl.acceptFollowRequest(1234, 5678, false) + + verify(apSendAcceptService, never()).sendAccept(any(), any()) + } + + @Test + fun `acceptFollowRequest フォローリクエストが存在せずforceがfalseのとき何もしない`() = runTest { + whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn( + Relationship( + 1234, 5678, false, false, false, false, false + ) + ) + + relationshipServiceImpl.acceptFollowRequest(1234, 5678, false) + + verify(apSendAcceptService, never()).sendAccept(any(), any()) + } + + @Test + fun `acceptFollowRequest フォローリクエストが存在せずforceがtrueのときフォローを承認する`() = runTest { + whenever(userQueryService.findById(eq(5678))).doReturn(UserBuilder.remoteUserOf(domain = "remote.example.com")) + + whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn( + Relationship( + 1234, 5678, false, false, false, false, false + ) + ) + + relationshipServiceImpl.acceptFollowRequest(1234, 5678, true) + + verify(relationshipRepository, times(1)).save( + eq( + Relationship( + userId = 1234, + targetUserId = 5678, + following = true, + blocking = false, + muting = false, + followRequest = false, + ignoreFollowRequestFromTarget = false + ) + ) + ) + } + + @Test + fun `acceptFollowRequest ブロックしている場合は何もしない`() = runTest { + whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn( + Relationship( + 1234, 5678, false, true, false, true, false + ) + ) + + assertThrows { + relationshipServiceImpl.acceptFollowRequest(1234, 5678, false) + } + + verify(relationshipRepository, never()).save(any()) + } + + @Test + fun `acceptFollowRequest ブロックされている場合は何もしない`() = runTest { + whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn( + Relationship( + 1234, 5678, false, false, false, true, false + ) + ) + + whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(5678), eq(1234))).doReturn( + Relationship( + 5678, 1234, false, true, false, true, false + ) + ) + + assertThrows { + relationshipServiceImpl.acceptFollowRequest(1234, 5678, false) + } + + verify(relationshipRepository, never()).save(any()) + } + + @Test + fun `rejectFollowRequest ローカルユーザーの場合永続化される`() = runTest { + whenever(userQueryService.findById(eq(5678))).doReturn(UserBuilder.localUserOf(domain = "example.com")) + + whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn( + Relationship( + userId = 1234, + targetUserId = 5678, + following = false, + blocking = false, + muting = false, + followRequest = true, + ignoreFollowRequestFromTarget = false + ) + ) + + relationshipServiceImpl.rejectFollowRequest(1234, 5678) + + verify(relationshipRepository, times(1)).save( + eq( + Relationship( + userId = 1234, + targetUserId = 5678, + following = false, + blocking = false, + muting = false, + followRequest = false, + ignoreFollowRequestFromTarget = false + ) + ) + ) + + verify(apSendRejectService, never()).sendReject(any(), any()) + } + + @Test + fun `rejectFollowRequest リモートユーザーの場合永続化されて配送される`() = runTest { + val localUser = UserBuilder.localUserOf(domain = "example.com") + whenever(userQueryService.findById(eq(1234))).doReturn(localUser) + + val remoteUser = UserBuilder.remoteUserOf(domain = "remote.example.com") + whenever(userQueryService.findById(eq(5678))).doReturn(remoteUser) + + whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn( + Relationship( + userId = 1234, + targetUserId = 5678, + following = false, + blocking = false, + muting = false, + followRequest = true, + ignoreFollowRequestFromTarget = false + ) + ) + + relationshipServiceImpl.rejectFollowRequest(1234, 5678) + + verify(relationshipRepository, times(1)).save( + eq( + Relationship( + userId = 1234, + targetUserId = 5678, + following = false, + blocking = false, + muting = false, + followRequest = false, + ignoreFollowRequestFromTarget = false + ) + ) + ) + + verify(apSendRejectService, times(1)).sendReject(eq(localUser), eq(remoteUser)) + } + + @Test + fun `rejectFollowRequest Relationshipが存在しないとき何もしない`() = runTest { + + relationshipServiceImpl.rejectFollowRequest(1234, 5678) + + verify(relationshipRepository, never()).save(any()) + } + + @Test + fun `rejectFollowRequest フォローリクエストが存在しない場合何もしない`() = runTest { + whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn( + Relationship( + userId = 1234, + targetUserId = 5678, + following = false, + blocking = false, + muting = false, + followRequest = false, + ignoreFollowRequestFromTarget = false + ) + ) + + relationshipServiceImpl.rejectFollowRequest(1234, 5678) + + verify(relationshipRepository, never()).save(any()) + } + + @Test + fun `ignoreFollowRequest 永続化される`() = runTest { + relationshipServiceImpl.ignoreFollowRequest(1234, 5678) + + verify(relationshipRepository, times(1)).save( + eq( + Relationship( + userId = 1234, + targetUserId = 5678, + following = false, + blocking = false, + muting = false, + followRequest = false, + ignoreFollowRequestFromTarget = true + ) + ) + ) + } + + @Test + fun `unfollow ローカルユーザーの場合永続化される`() = runTest { + whenever(userQueryService.findById(eq(5678))).doReturn(UserBuilder.localUserOf(domain = "example.com")) + whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn( + Relationship( + userId = 1234, + targetUserId = 5678, + following = true, + blocking = false, + muting = false, + followRequest = false, + ignoreFollowRequestFromTarget = false + ) + ) + + relationshipServiceImpl.unfollow(1234, 5678) + + verify(relationshipRepository, times(1)).save( + eq( + Relationship( + userId = 1234, + targetUserId = 5678, + following = false, + blocking = false, + muting = false, + followRequest = false, + ignoreFollowRequestFromTarget = false + ) + ) + ) + + verify(apSendUndoService, never()).sendUndoFollow(any(), any()) + } + + @Test + fun `unfollow リモートユーザー場合永続化されて配送される`() = runTest { + val localUser = UserBuilder.localUserOf(domain = "example.com") + whenever(userQueryService.findById(eq(1234))).doReturn(localUser) + + val remoteUser = UserBuilder.remoteUserOf(domain = "remote.example.com") + whenever(userQueryService.findById(eq(5678))).doReturn(remoteUser) + + whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn( + Relationship( + userId = 1234, + targetUserId = 5678, + following = true, + blocking = false, + muting = false, + followRequest = false, + ignoreFollowRequestFromTarget = false + ) + ) + + relationshipServiceImpl.unfollow(1234, 5678) + + verify(relationshipRepository, times(1)).save( + eq( + Relationship( + userId = 1234, + targetUserId = 5678, + following = false, + blocking = false, + muting = false, + followRequest = false, + ignoreFollowRequestFromTarget = false + ) + ) + ) + + verify(apSendUndoService, times(1)).sendUndoFollow(eq(localUser), eq(remoteUser)) + } + + @Test + fun `unfollow Relationshipが存在しないときは何もしない`() = runTest { + relationshipServiceImpl.unfollow(1234, 5678) + + verify(relationshipRepository, never()).save(any()) + } + + @Test + fun `unfollow フォローしていなかった場合は何もしない`() = runTest { + whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn( + Relationship( + userId = 1234, + targetUserId = 5678, + following = false, + blocking = false, + muting = false, + followRequest = false, + ignoreFollowRequestFromTarget = false + ) + ) + + relationshipServiceImpl.unfollow(1234, 5678) + + verify(relationshipRepository, never()).save(any()) + } + + @Test + fun `unblock ローカルユーザーの場合永続化される`() = runTest { + whenever(userQueryService.findById(eq(5678))).doReturn(UserBuilder.localUserOf(domain = "example.com")) + whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn( + Relationship( + userId = 1234, + targetUserId = 5678, + following = false, + blocking = true, + muting = false, + followRequest = false, + ignoreFollowRequestFromTarget = false + ) + ) + + relationshipServiceImpl.unblock(1234, 5678) + + verify(relationshipRepository, times(1)).save( + eq( + Relationship( + userId = 1234, + targetUserId = 5678, + following = false, + blocking = false, + muting = false, + followRequest = false, + ignoreFollowRequestFromTarget = false + ) + ) + ) + + verify(apSendUndoService, never()).sendUndoBlock(any(), any()) + } + + @Test + fun `unblock リモートユーザーの場合永続化されて配送される`() = runTest { + val localUser = UserBuilder.localUserOf(domain = "example.com") + whenever(userQueryService.findById(eq(1234))).doReturn(localUser) + + val remoteUser = UserBuilder.remoteUserOf(domain = "remote.example.com") + whenever(userQueryService.findById(eq(5678))).doReturn(remoteUser) + + whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn( + Relationship( + userId = 1234, + targetUserId = 5678, + following = false, + blocking = true, + muting = false, + followRequest = false, + ignoreFollowRequestFromTarget = false + ) + ) + + relationshipServiceImpl.unblock(1234, 5678) + + verify(relationshipRepository, times(1)).save( + eq( + Relationship( + 1234, + 5678, + false, + false, + false, + false, + false + ) + ) + ) + + verify(apSendUndoService, times(1)).sendUndoBlock(eq(localUser), eq(remoteUser)) + } + + @Test + fun `unblock Relationshipがない場合何もしない`() = runTest { + relationshipServiceImpl.unblock(1234, 5678) + + verify(relationshipRepository, never()).save(any()) + } + + @Test + fun `unblock ブロックしていない場合は何もしない`() = runTest { + whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn( + Relationship( + userId = 1234, + targetUserId = 5678, + following = false, + blocking = false, + muting = false, + followRequest = false, + ignoreFollowRequestFromTarget = false + ) + ) + + relationshipServiceImpl.unblock(1234, 5678) + + verify(relationshipRepository, never()).save(any()) + } + + @Test + fun `mute ミュートが永続化される`() = runTest { + relationshipServiceImpl.mute(1234, 5678) + + verify(relationshipRepository, times(1)).save( + eq( + Relationship( + userId = 1234, + targetUserId = 5678, + following = false, + blocking = false, + muting = true, + followRequest = false, + ignoreFollowRequestFromTarget = false + ) + ) + ) + } + + @Test + fun `unmute 永続化される`() = runTest { + + whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn( + Relationship( + userId = 1234, + targetUserId = 5678, + following = false, + blocking = false, + muting = true, + followRequest = false, + ignoreFollowRequestFromTarget = false + ) + ) + + relationshipServiceImpl.unmute(1234, 5678) + + verify(relationshipRepository, times(1)).save( + eq( + Relationship( + userId = 1234, + targetUserId = 5678, + following = false, + blocking = false, + muting = false, + followRequest = false, + ignoreFollowRequestFromTarget = false + ) + ) + ) + } + + @Test + fun `unmute Relationshipが存在しない場合は何もしない`() = runTest { + relationshipServiceImpl.unmute(1234, 5678) + + verify(relationshipRepository, never()).save(any()) + } +} From 649bc95829d21ec647f1540e5a7f944f2a54eb27 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 10 Dec 2023 15:13:13 +0900 Subject: [PATCH 21/43] =?UTF-8?q?refactor:=20MastodonAPI=E3=82=92Relations?= =?UTF-8?q?hipService=E3=81=A7=E3=81=AE=E6=93=8D=E4=BD=9C=E3=81=AB?= =?UTF-8?q?=E7=BD=AE=E3=81=8D=E6=8F=9B=E3=81=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../account/MastodonAccountApiController.kt | 2 +- .../service/account/AccountApiService.kt | 139 +++++++----------- 2 files changed, 56 insertions(+), 85 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt index 0ebed08e..0f22e2db 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt @@ -28,7 +28,7 @@ class MastodonAccountApiController( ): ResponseEntity { val principal = SecurityContextHolder.getContext().getAuthentication().principal as Jwt - return ResponseEntity.ok(accountApiService.follow(principal.getClaim("uid").toLong(), id.toLong())) + return ResponseEntity.ok(accountApiService.follow2(principal.getClaim("uid").toLong(), id.toLong())) } override suspend fun apiV1AccountsIdGet(id: String): ResponseEntity = diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt index 15d7f308..e78b8b78 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt @@ -1,11 +1,8 @@ package dev.usbharu.hideout.mastodon.service.account import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException -import dev.usbharu.hideout.core.domain.model.block.BlockRepository -import dev.usbharu.hideout.core.domain.model.user.UserRepository -import dev.usbharu.hideout.core.query.FollowerQueryService -import dev.usbharu.hideout.core.service.block.BlockService +import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository +import dev.usbharu.hideout.core.service.relationship.RelationshipService import dev.usbharu.hideout.core.service.user.UserCreateDto import dev.usbharu.hideout.core.service.user.UserService import dev.usbharu.hideout.domain.mastodon.model.generated.* @@ -32,7 +29,7 @@ interface AccountApiService { suspend fun verifyCredentials(userid: Long): CredentialAccount suspend fun registerAccount(userCreateDto: UserCreateDto): Unit - suspend fun follow(userid: Long, followeeId: Long): Relationship + suspend fun follow2(loginUser: Long, followTargetUserId: Long): Relationship suspend fun account(id: Long): Account suspend fun relationships(userid: Long, id: List, withSuspended: Boolean): List @@ -51,11 +48,9 @@ class AccountApiServiceImpl( private val accountService: AccountService, private val transaction: Transaction, private val userService: UserService, - private val followerQueryService: FollowerQueryService, - private val userRepository: UserRepository, private val statusQueryService: StatusQueryService, - private val blockService: BlockService, - private val blockRepository: BlockRepository + private val relationshipService: RelationshipService, + private val relationshipRepository: RelationshipRepository ) : AccountApiService { override suspend fun accountsStatuses( @@ -75,7 +70,7 @@ class AccountApiServiceImpl( false } else { transaction.transaction { - followerQueryService.alreadyFollow(userid, loginUser) + isFollowing(loginUser, userid) } } @@ -105,34 +100,10 @@ class AccountApiServiceImpl( userService.createLocalUser(UserCreateDto(userCreateDto.name, userCreateDto.name, "", userCreateDto.password)) } - override suspend fun follow(userid: Long, followeeId: Long): Relationship = transaction.transaction { - val alreadyFollow = followerQueryService.alreadyFollow(followeeId, userid) + override suspend fun follow2(loginUser: Long, followTargetUserId: Long): Relationship { + relationshipService.followRequest(loginUser, followTargetUserId) - val followRequest = if (alreadyFollow) { - true - } else { - userService.followRequest(followeeId, userid) - } - - val alreadyFollow1 = followerQueryService.alreadyFollow(userid, followeeId) - - val followRequestsById = userRepository.findFollowRequestsById(followeeId, userid) - - return@transaction Relationship( - followeeId.toString(), - followRequest, - true, - false, - alreadyFollow1, - false, - false, - false, - false, - followRequestsById, - false, - false, - "" - ) + return fetchRelationship(loginUser, followTargetUserId) } override suspend fun account(id: Long): Account = transaction.transaction { @@ -150,57 +121,14 @@ class AccountApiServiceImpl( val subList = id.subList(0, min(id.size, 20)) return@transaction subList.map { - val alreadyFollow = followerQueryService.alreadyFollow(userid, it) - - val followed = followerQueryService.alreadyFollow(it, userid) - - val requested = userRepository.findFollowRequestsById(it, userid) - - Relationship( - id = it.toString(), - following = alreadyFollow, - showingReblogs = true, - notifying = false, - followedBy = followed, - blocking = false, - blockedBy = false, - muting = false, - mutingNotifications = false, - requested = requested, - domainBlocking = false, - endorsed = false, - note = "" - ) + fetchRelationship(userid, it) } } override suspend fun block(userid: Long, target: Long): Relationship = transaction.transaction { - blockService.block(userid, target) + relationshipService.block(userid, target) - val blocked = try { - blockRepository.findByUserIdAndTarget(target, userid) - true - } catch (e: FailedToGetResourcesException) { - false - } - - val requested = userRepository.findFollowRequestsById(target, userid) - - Relationship( - target.toString(), - false, - true, - false, - false, - true, - blocked, - false, - false, - requested, - false, - false, - "" - ) + fetchRelationship(userid, target) } private fun from(account: Account): CredentialAccount { @@ -241,6 +169,49 @@ class AccountApiServiceImpl( ) } + private suspend fun fetchRelationship(userid: Long, targetId: Long): Relationship { + val relationship = relationshipRepository.findByUserIdAndTargetUserId(userid, targetId) + ?: dev.usbharu.hideout.core.domain.model.relationship.Relationship( + userId = userid, + targetUserId = targetId, + following = false, + blocking = false, + muting = false, + followRequest = false, + ignoreFollowRequestFromTarget = false + ) + + val inverseRelationship = relationshipRepository.findByUserIdAndTargetUserId(targetId, userid) + ?: dev.usbharu.hideout.core.domain.model.relationship.Relationship( + userId = targetId, + targetUserId = userid, + following = false, + blocking = false, + muting = false, + followRequest = false, + ignoreFollowRequestFromTarget = false + ) + + return Relationship( + id = targetId.toString(), + following = relationship.following, + showingReblogs = true, + notifying = false, + followedBy = inverseRelationship.following, + blocking = relationship.blocking, + blockedBy = inverseRelationship.blocking, + muting = relationship.muting, + mutingNotifications = relationship.muting, + requested = relationship.followRequest, + domainBlocking = false, + endorsed = false, + note = "" + ) + } + + private suspend fun isFollowing(userid: Long, target: Long): Boolean = + relationshipRepository.findByUserIdAndTargetUserId(userid, target)?.following ?: false + companion object { private val logger = LoggerFactory.getLogger(AccountApiServiceImpl::class.java) } From bc56aca96571414ce45b13c66c780ef6bbba7274 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 10 Dec 2023 15:42:59 +0900 Subject: [PATCH 22/43] =?UTF-8?q?refactor:=20=E3=81=9D=E3=81=AE=E4=BB=96?= =?UTF-8?q?=E3=81=AE=E9=83=A8=E5=88=86=E3=81=A7RelationshipService?= =?UTF-8?q?=E3=81=AB=E7=BD=AE=E3=81=8D=E6=8F=9B=E3=81=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../activity/accept/ApAcceptProcessor.kt | 15 +-- .../block/BlockActivityPubProcessor.kt | 6 +- .../follow/APReceiveFollowJobProcessor.kt | 26 +----- .../service/activity/undo/APUndoProcessor.kt | 10 +- .../core/service/block/BlockService.kt | 25 ----- .../core/service/block/BlockServiceImpl.kt | 91 ------------------- .../hideout/core/service/user/UserService.kt | 19 ---- .../core/service/user/UserServiceImpl.kt | 34 ------- .../account/MastodonAccountApiController.kt | 2 +- .../service/account/AccountApiService.kt | 4 +- 10 files changed, 21 insertions(+), 211 deletions(-) delete mode 100644 src/main/kotlin/dev/usbharu/hideout/core/service/block/BlockService.kt delete mode 100644 src/main/kotlin/dev/usbharu/hideout/core/service/block/BlockServiceImpl.kt diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessor.kt index 2ee0f07e..bf5c0f8a 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessor.kt @@ -7,8 +7,8 @@ import dev.usbharu.hideout.activitypub.service.common.AbstractActivityPubProcess import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessContext import dev.usbharu.hideout.activitypub.service.common.ActivityType import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.query.FollowerQueryService import dev.usbharu.hideout.core.query.UserQueryService +import dev.usbharu.hideout.core.service.relationship.RelationshipService import dev.usbharu.hideout.core.service.user.UserService import org.springframework.stereotype.Service @@ -16,13 +16,13 @@ import org.springframework.stereotype.Service class ApAcceptProcessor( transaction: Transaction, private val userQueryService: UserQueryService, - private val followerQueryService: FollowerQueryService, - private val userService: UserService + private val userService: UserService, + private val relationshipService: RelationshipService ) : AbstractActivityPubProcessor(transaction) { override suspend fun internalProcess(activity: ActivityPubProcessContext) { - val value = activity.activity.apObject ?: throw IllegalActivityPubObjectException("object is null") + val value = activity.activity.apObject if (value.type.contains("Follow").not()) { logger.warn("FAILED Activity type isn't Follow.") @@ -37,12 +37,7 @@ class ApAcceptProcessor( val user = userQueryService.findByUrl(userUrl) val follower = userQueryService.findByUrl(followerUrl) - if (followerQueryService.alreadyFollow(user.id, follower.id)) { - logger.debug("END User already follow from ${follower.url} to ${user.url}.") - return - } - - userService.follow(user.id, follower.id) + relationshipService.acceptFollowRequest(follower.id, user.id) logger.debug("SUCCESS Follow from ${follower.url} to ${user.url}.") } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/block/BlockActivityPubProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/block/BlockActivityPubProcessor.kt index 1bb40c19..fa67be3d 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/block/BlockActivityPubProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/block/BlockActivityPubProcessor.kt @@ -6,7 +6,7 @@ import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessContext import dev.usbharu.hideout.activitypub.service.common.ActivityType import dev.usbharu.hideout.application.external.Transaction import dev.usbharu.hideout.core.query.UserQueryService -import dev.usbharu.hideout.core.service.block.BlockService +import dev.usbharu.hideout.core.service.relationship.RelationshipService import org.springframework.stereotype.Service @@ -15,15 +15,15 @@ import org.springframework.stereotype.Service */ @Service class BlockActivityPubProcessor( - private val blockService: BlockService, private val userQueryService: UserQueryService, + private val relationshipService: RelationshipService, transaction: Transaction ) : AbstractActivityPubProcessor(transaction) { override suspend fun internalProcess(activity: ActivityPubProcessContext) { val user = userQueryService.findByUrl(activity.activity.actor) val target = userQueryService.findByUrl(activity.activity.apObject) - blockService.block(user.id, target.id) + relationshipService.block(user.id, target.id) } override fun isSupported(activityType: ActivityType): Boolean = activityType == ActivityType.Block diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowJobProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowJobProcessor.kt index 0c3957b1..1f79af46 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowJobProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowJobProcessor.kt @@ -2,16 +2,14 @@ package dev.usbharu.hideout.activitypub.service.activity.follow import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.module.kotlin.readValue -import dev.usbharu.hideout.activitypub.domain.model.Accept import dev.usbharu.hideout.activitypub.domain.model.Follow -import dev.usbharu.hideout.activitypub.service.common.APRequestService import dev.usbharu.hideout.activitypub.service.objects.user.APUserService import dev.usbharu.hideout.application.external.Transaction import dev.usbharu.hideout.core.external.job.ReceiveFollowJob import dev.usbharu.hideout.core.external.job.ReceiveFollowJobParam import dev.usbharu.hideout.core.query.UserQueryService import dev.usbharu.hideout.core.service.job.JobProcessor -import dev.usbharu.hideout.core.service.user.UserService +import dev.usbharu.hideout.core.service.relationship.RelationshipService import org.slf4j.LoggerFactory import org.springframework.stereotype.Service @@ -21,35 +19,21 @@ class APReceiveFollowJobProcessor( private val userQueryService: UserQueryService, private val apUserService: APUserService, private val objectMapper: ObjectMapper, - private val apRequestService: APRequestService, - private val userService: UserService + private val relationshipService: RelationshipService ) : JobProcessor { override suspend fun process(param: ReceiveFollowJobParam) = transaction.transaction { - val person = apUserService.fetchPerson(param.actor, param.targetActor) + apUserService.fetchPerson(param.actor, param.targetActor) val follow = objectMapper.readValue(param.follow) logger.info("START Follow from: {} to {}", param.targetActor, param.actor) - val signer = userQueryService.findByUrl(param.targetActor) - - val urlString = person.inbox - - apRequestService.apPost( - url = urlString, - body = Accept( - name = "Follow", - apObject = follow, - actor = param.targetActor - ), - signer = signer - ) - val targetEntity = userQueryService.findByUrl(param.targetActor) val followActorEntity = userQueryService.findByUrl(follow.actor) - userService.followRequest(targetEntity.id, followActorEntity.id) + relationshipService.followRequest(followActorEntity.id, targetEntity.id) + logger.info("SUCCESS Follow from: {} to: {}", param.targetActor, param.actor) } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt index 2c8067a4..6f44abc6 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt @@ -8,6 +8,7 @@ import dev.usbharu.hideout.activitypub.service.common.ActivityType import dev.usbharu.hideout.activitypub.service.objects.user.APUserService import dev.usbharu.hideout.application.external.Transaction import dev.usbharu.hideout.core.query.UserQueryService +import dev.usbharu.hideout.core.service.relationship.RelationshipService import dev.usbharu.hideout.core.service.user.UserService import org.springframework.stereotype.Service @@ -16,7 +17,8 @@ class APUndoProcessor( transaction: Transaction, private val apUserService: APUserService, private val userQueryService: UserQueryService, - private val userService: UserService + private val userService: UserService, + private val relationshipService: RelationshipService ) : AbstractActivityPubProcessor(transaction) { override suspend fun internalProcess(activity: ActivityPubProcessContext) { @@ -34,13 +36,11 @@ class APUndoProcessor( "Follow" -> { val follow = undo.`object` as Follow - if (follow.apObject == null) { - return - } apUserService.fetchPerson(undo.actor, follow.apObject) val follower = userQueryService.findByUrl(undo.actor) val target = userQueryService.findByUrl(follow.apObject) - userService.unfollow(target.id, follower.id) + + relationshipService.unfollow(follower.id, target.id) return } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/block/BlockService.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/block/BlockService.kt deleted file mode 100644 index 83d1512c..00000000 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/block/BlockService.kt +++ /dev/null @@ -1,25 +0,0 @@ -package dev.usbharu.hideout.core.service.block - -/** - * ブロックに関する処理を行います - * - */ -interface BlockService { - /** - * ブロックします - * 実装はリモートユーザーへのブロックの場合ブロックアクティビティを配送するべきです。 - * - * @param userId ブロックの動作を行ったユーザーid - * @param target ブロック対象のユーザーid - */ - suspend fun block(userId: Long, target: Long) - - /** - * ブロックを解除します - * 実装はリモートユーザーへのブロック解除の場合Undo Blockアクティビティを配送するべきです - * - * @param userId ブロック解除の動作を行ったユーザーid - * @param target ブロック解除の対象のユーザーid - */ - suspend fun unblock(userId: Long, target: Long) -} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/block/BlockServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/block/BlockServiceImpl.kt deleted file mode 100644 index 761308e4..00000000 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/block/BlockServiceImpl.kt +++ /dev/null @@ -1,91 +0,0 @@ -package dev.usbharu.hideout.core.service.block - -import dev.usbharu.hideout.activitypub.domain.model.Follow -import dev.usbharu.hideout.activitypub.domain.model.Reject -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException -import dev.usbharu.hideout.core.domain.model.block.Block -import dev.usbharu.hideout.core.domain.model.block.BlockRepository -import dev.usbharu.hideout.core.domain.model.user.UserRepository -import dev.usbharu.hideout.core.external.job.DeliverBlockJob -import dev.usbharu.hideout.core.external.job.DeliverBlockJobParam -import dev.usbharu.hideout.core.query.FollowerQueryService -import dev.usbharu.hideout.core.service.job.JobQueueParentService -import dev.usbharu.hideout.core.service.user.UserService -import org.slf4j.LoggerFactory -import org.springframework.stereotype.Service - -@Service -class BlockServiceImpl( - private val transaction: Transaction, - private val blockRepository: BlockRepository, - private val followerQueryService: FollowerQueryService, - private val userService: UserService, - private val jobQueueParentService: JobQueueParentService, - private val deliverBlockJob: DeliverBlockJob, - private val userRepository: UserRepository, - private val applicationConfig: ApplicationConfig -) : - BlockService { - override suspend fun block(userId: Long, target: Long) { - logger.debug("Block userId: {} → target: {}", userId, target) - - val user = userRepository.findById(userId) ?: throw IllegalStateException("Block user was not found.") - - val targetEntity = userRepository.findById(target) ?: throw IllegalStateException("Block use was not found.") - - if (user.domain != applicationConfig.url.host && targetEntity.domain != applicationConfig.url.host) { - logger.warn("Invalid Block activity. Both user and target are remote users.") - return - } - - blockRepository.save(Block(userId, target)) - if (followerQueryService.alreadyFollow(userId, target)) { - logger.debug("Unfollow (Block) userId: {} → target: {}", userId, target) - userService.unfollow(userId, target) - } - - - if (user.domain == applicationConfig.url.host) { - return - } - - if (targetEntity.domain == applicationConfig.url.host) { - return - } - - val blockJobParam = DeliverBlockJobParam( - user.id, - dev.usbharu.hideout.activitypub.domain.model.Block( - user.url, - "${applicationConfig.url}/block/${user.id}/${targetEntity.id}", - targetEntity.url - ), - Reject( - user.url, - "${applicationConfig.url}/reject/${user.id}/${targetEntity.id}", - Follow( - apObject = user.url, - actor = targetEntity.url - ) - ), - targetEntity.inbox - ) - jobQueueParentService.scheduleTypeSafe(deliverBlockJob, blockJobParam) - } - - override suspend fun unblock(userId: Long, target: Long) = transaction.transaction { - logger.debug("Unblock userId: {} → target: {}", userId, target) - try { - val block = blockRepository.findByUserIdAndTarget(userId, target) - blockRepository.delete(block) - } catch (e: FailedToGetResourcesException) { - logger.warn("FAILED Unblock userId: {} target: {}", userId, target, e) - } - } - - companion object { - private val logger = LoggerFactory.getLogger(BlockServiceImpl::class.java) - } -} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserService.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserService.kt index fc34c36c..a9c0de2d 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserService.kt @@ -11,23 +11,4 @@ interface UserService { suspend fun createLocalUser(user: UserCreateDto): User suspend fun createRemoteUser(user: RemoteUserCreateDto): User - - /** - * フォローリクエストを送信する - * - * @param id - * @param followerId - * @return リクエストが成功したか - */ - suspend fun followRequest(id: Long, followerId: Long): Boolean - - /** - * フォローする - * - * @param id - * @param followerId - */ - suspend fun follow(id: Long, followerId: Long) - - suspend fun unfollow(id: Long, followerId: Long): Boolean } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt index e70ec782..b8a459c9 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt @@ -2,12 +2,10 @@ package dev.usbharu.hideout.core.service.user import dev.usbharu.hideout.activitypub.service.activity.follow.APSendFollowService import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.core.domain.exception.UserNotFoundException import dev.usbharu.hideout.core.domain.model.user.User import dev.usbharu.hideout.core.domain.model.user.UserRepository import dev.usbharu.hideout.core.query.FollowerQueryService import dev.usbharu.hideout.core.query.UserQueryService -import dev.usbharu.hideout.core.service.follow.SendFollowDto import dev.usbharu.hideout.core.service.instance.InstanceService import org.jetbrains.exposed.exceptions.ExposedSQLException import org.slf4j.LoggerFactory @@ -96,38 +94,6 @@ class UserServiceImpl( } } - // TODO APのフォロー処理を作る - override suspend fun followRequest(id: Long, followerId: Long): Boolean { - val user = userRepository.findById(id) ?: throw UserNotFoundException("$id was not found.") - val follower = userRepository.findById(followerId) ?: throw UserNotFoundException("$followerId was not found.") - return if (user.domain == applicationConfig.url.host) { - follow(id, followerId) - true - } else { - if (userRepository.findFollowRequestsById(id, followerId)) { - // do-nothing - } else { - apSendFollowService.sendFollow(SendFollowDto(follower, user)) - } - false - } - } - - override suspend fun follow(id: Long, followerId: Long) { - logger.debug("START Follow id: {} → target: {}", followerId, id) - followerQueryService.appendFollower(id, followerId) - if (userRepository.findFollowRequestsById(id, followerId)) { - logger.debug("Follow request is accepted! ") - userRepository.deleteFollowRequest(id, followerId) - } - logger.debug("SUCCESS Follow id: {} → target: {}", followerId, id) - } - - override suspend fun unfollow(id: Long, followerId: Long): Boolean { - followerQueryService.removeFollower(id, followerId) - return false - } - companion object { private val logger = LoggerFactory.getLogger(UserServiceImpl::class.java) } diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt index 0f22e2db..0ebed08e 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt @@ -28,7 +28,7 @@ class MastodonAccountApiController( ): ResponseEntity { val principal = SecurityContextHolder.getContext().getAuthentication().principal as Jwt - return ResponseEntity.ok(accountApiService.follow2(principal.getClaim("uid").toLong(), id.toLong())) + return ResponseEntity.ok(accountApiService.follow(principal.getClaim("uid").toLong(), id.toLong())) } override suspend fun apiV1AccountsIdGet(id: String): ResponseEntity = diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt index e78b8b78..6a3d47f9 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt @@ -29,7 +29,7 @@ interface AccountApiService { suspend fun verifyCredentials(userid: Long): CredentialAccount suspend fun registerAccount(userCreateDto: UserCreateDto): Unit - suspend fun follow2(loginUser: Long, followTargetUserId: Long): Relationship + suspend fun follow(loginUser: Long, followTargetUserId: Long): Relationship suspend fun account(id: Long): Account suspend fun relationships(userid: Long, id: List, withSuspended: Boolean): List @@ -100,7 +100,7 @@ class AccountApiServiceImpl( userService.createLocalUser(UserCreateDto(userCreateDto.name, userCreateDto.name, "", userCreateDto.password)) } - override suspend fun follow2(loginUser: Long, followTargetUserId: Long): Relationship { + override suspend fun follow(loginUser: Long, followTargetUserId: Long): Relationship { relationshipService.followRequest(loginUser, followTargetUserId) return fetchRelationship(loginUser, followTargetUserId) From e41763b96d2bdbb50c2e7226c3095eafff930cc6 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 10 Dec 2023 15:43:12 +0900 Subject: [PATCH 23/43] =?UTF-8?q?test:=20=E3=83=86=E3=82=B9=E3=83=88?= =?UTF-8?q?=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../account/AccountApiServiceImplTest.kt | 94 +++++++++---------- 1 file changed, 43 insertions(+), 51 deletions(-) diff --git a/src/test/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiServiceImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiServiceImplTest.kt index 5cc6a833..a34ed5dc 100644 --- a/src/test/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiServiceImplTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiServiceImplTest.kt @@ -1,8 +1,10 @@ package dev.usbharu.hideout.mastodon.service.account import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository import dev.usbharu.hideout.core.domain.model.user.UserRepository import dev.usbharu.hideout.core.query.FollowerQueryService +import dev.usbharu.hideout.core.service.relationship.RelationshipService import dev.usbharu.hideout.core.service.user.UserService import dev.usbharu.hideout.domain.mastodon.model.generated.Account import dev.usbharu.hideout.domain.mastodon.model.generated.Relationship @@ -40,6 +42,12 @@ class AccountApiServiceImplTest { @Spy private val transaction: Transaction = TestTransaction + @Mock + private lateinit var relationshipService: RelationshipService + + @Mock + private lateinit var relationshipRepository: RelationshipRepository + @InjectMocks private lateinit var accountApiServiceImpl: AccountApiServiceImpl @@ -157,9 +165,6 @@ class AccountApiServiceImplTest { ) ).doReturn(statusList) - whenever(followerQueryService.alreadyFollow(eq(userId), eq(loginUser))).doReturn(false) - - val accountsStatuses = accountApiServiceImpl.accountsStatuses( userid = userId, maxId = null, @@ -197,7 +202,17 @@ class AccountApiServiceImplTest { ) ).doReturn(statusList) - whenever(followerQueryService.alreadyFollow(eq(userId), eq(loginUser))).doReturn(true) + whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(loginUser), eq(userId))).doReturn( + dev.usbharu.hideout.core.domain.model.relationship.Relationship( + userId = loginUser, + targetUserId = userId, + following = true, + blocking = false, + muting = false, + followRequest = false, + ignoreFollowRequestFromTarget = false + ) + ) val accountsStatuses = accountApiServiceImpl.accountsStatuses( @@ -217,51 +232,34 @@ class AccountApiServiceImplTest { assertThat(accountsStatuses).hasSize(1) } - @Test - fun `follow 既にフォローしている場合は何もしない`() = runTest { - val userId = 1234L - val followeeId = 1L - - whenever(followerQueryService.alreadyFollow(eq(followeeId), eq(userId))).doReturn(true) - - whenever(followerQueryService.alreadyFollow(eq(userId), eq(followeeId))).doReturn(true) - - whenever(userRepository.findFollowRequestsById(eq(followeeId), eq(userId))).doReturn(false) - - val follow = accountApiServiceImpl.follow(userId, followeeId) - - val expected = Relationship( - id = followeeId.toString(), - following = true, - showingReblogs = true, - notifying = false, - followedBy = true, - blocking = false, - blockedBy = false, - muting = false, - mutingNotifications = false, - requested = false, - domainBlocking = false, - endorsed = false, - note = "" - ) - assertThat(follow).isEqualTo(expected) - - verify(userService, never()).followRequest(any(), any()) - } - @Test fun `follow 未フォローの場合フォローリクエストが発生する`() = runTest { val userId = 1234L val followeeId = 1L - whenever(followerQueryService.alreadyFollow(eq(followeeId), eq(userId))).doReturn(false) + whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(followeeId), eq(userId))).doReturn( + dev.usbharu.hideout.core.domain.model.relationship.Relationship( + userId = followeeId, + targetUserId = userId, + following = true, + blocking = false, + muting = false, + followRequest = false, + ignoreFollowRequestFromTarget = false + ) + ) + whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(userId), eq(followeeId))).doReturn( + dev.usbharu.hideout.core.domain.model.relationship.Relationship( + userId = userId, + targetUserId = followeeId, + following = true, + blocking = false, + muting = false, + followRequest = false, + ignoreFollowRequestFromTarget = false + ) + ) - whenever(userService.followRequest(eq(followeeId), eq(userId))).doReturn(true) - - whenever(followerQueryService.alreadyFollow(eq(userId), eq(followeeId))).doReturn(true) - - whenever(userRepository.findFollowRequestsById(eq(followeeId), eq(userId))).doReturn(false) val follow = accountApiServiceImpl.follow(userId, followeeId) @@ -282,14 +280,11 @@ class AccountApiServiceImplTest { ) assertThat(follow).isEqualTo(expected) - verify(userService, times(1)).followRequest(eq(followeeId), eq(userId)) + verify(relationshipService, times(1)).followRequest(eq(userId), eq(followeeId)) } @Test fun `relationships idが長すぎたら省略する`() = runTest { - whenever(followerQueryService.alreadyFollow(any(), any())).doReturn(true) - - whenever(userRepository.findFollowRequestsById(any(), any())).doReturn(true) val relationships = accountApiServiceImpl.relationships( userid = 1234L, @@ -297,7 +292,7 @@ class AccountApiServiceImplTest { withSuspended = false ) - assertThat(relationships).hasSizeLessThanOrEqualTo(20) + assertThat(relationships).hasSize(20) } @Test @@ -315,9 +310,6 @@ class AccountApiServiceImplTest { @Test fun `relationships idに指定されたアカウントの関係を取得する`() = runTest { - whenever(followerQueryService.alreadyFollow(any(), any())).doReturn(true) - - whenever(userRepository.findFollowRequestsById(any(), any())).doReturn(true) val relationships = accountApiServiceImpl.relationships( userid = 1234L, From 8027f937e94e66db3dac15683393103c85e01407 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 10 Dec 2023 15:55:09 +0900 Subject: [PATCH 24/43] =?UTF-8?q?refactor:=20=E4=B8=8D=E8=A6=81=E3=81=AA?= =?UTF-8?q?=E3=83=A1=E3=82=BD=E3=83=83=E3=83=89=E3=82=92=E5=89=8A=E9=99=A4?= =?UTF-8?q?=E3=80=81FollowQueryService=E3=81=AE=E5=AE=9F=E4=BD=93=E3=82=92?= =?UTF-8?q?RelationshipQueryService=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../activity/accept/ApAcceptProcessor.kt | 2 - .../core/domain/model/user/UserRepository.kt | 4 - .../exposedquery/FollowerQueryServiceImpl.kt | 243 +----------------- .../exposedrepository/UserRepositoryImpl.kt | 28 -- .../core/query/FollowerQueryService.kt | 6 +- .../core/query/RelationshipQueryService.kt | 8 + .../account/AccountApiServiceImplTest.kt | 1 - 7 files changed, 21 insertions(+), 271 deletions(-) create mode 100644 src/main/kotlin/dev/usbharu/hideout/core/query/RelationshipQueryService.kt diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessor.kt index bf5c0f8a..b1838ed4 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessor.kt @@ -9,14 +9,12 @@ import dev.usbharu.hideout.activitypub.service.common.ActivityType import dev.usbharu.hideout.application.external.Transaction import dev.usbharu.hideout.core.query.UserQueryService import dev.usbharu.hideout.core.service.relationship.RelationshipService -import dev.usbharu.hideout.core.service.user.UserService import org.springframework.stereotype.Service @Service class ApAcceptProcessor( transaction: Transaction, private val userQueryService: UserQueryService, - private val userService: UserService, private val relationshipService: RelationshipService ) : AbstractActivityPubProcessor(transaction) { diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/user/UserRepository.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/user/UserRepository.kt index bbc0d346..f37d45fe 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/user/UserRepository.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/user/UserRepository.kt @@ -10,9 +10,5 @@ interface UserRepository { suspend fun delete(id: Long) - suspend fun deleteFollowRequest(id: Long, follower: Long) - - suspend fun findFollowRequestsById(id: Long, follower: Long): Boolean - suspend fun nextId(): Long } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/FollowerQueryServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/FollowerQueryServiceImpl.kt index 4c62003e..f8c6bb6e 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/FollowerQueryServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/FollowerQueryServiceImpl.kt @@ -1,242 +1,23 @@ package dev.usbharu.hideout.core.infrastructure.exposedquery +import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository import dev.usbharu.hideout.core.domain.model.user.User -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Users -import dev.usbharu.hideout.core.infrastructure.exposedrepository.UsersFollowers import dev.usbharu.hideout.core.query.FollowerQueryService -import org.jetbrains.exposed.sql.* -import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq +import dev.usbharu.hideout.core.query.RelationshipQueryService +import dev.usbharu.hideout.core.query.UserQueryService import org.springframework.stereotype.Repository -import java.time.Instant @Repository -class FollowerQueryServiceImpl(private val userBuilder: User.UserBuilder) : FollowerQueryService { +class FollowerQueryServiceImpl( + private val relationshipQueryService: RelationshipQueryService, + private val userQueryService: UserQueryService, + private val relationshipRepository: RelationshipRepository +) : FollowerQueryService { override suspend fun findFollowersById(id: Long): List { - val followers = Users.alias("FOLLOWERS") - return Users.innerJoin( - otherTable = UsersFollowers, - onColumn = { Users.id }, - otherColumn = { userId } - ) - .innerJoin( - otherTable = followers, - onColumn = { UsersFollowers.followerId }, - otherColumn = { followers[Users.id] } - ) - .slice( - followers[Users.id], - followers[Users.name], - followers[Users.domain], - followers[Users.screenName], - followers[Users.description], - followers[Users.password], - followers[Users.inbox], - followers[Users.outbox], - followers[Users.url], - followers[Users.publicKey], - followers[Users.privateKey], - followers[Users.createdAt], - followers[Users.keyId], - followers[Users.following], - followers[Users.followers], - followers[Users.instance] - ) - .select { Users.id eq id } - .map { - userBuilder.of( - id = it[followers[Users.id]], - name = it[followers[Users.name]], - domain = it[followers[Users.domain]], - screenName = it[followers[Users.screenName]], - description = it[followers[Users.description]], - password = it[followers[Users.password]], - inbox = it[followers[Users.inbox]], - outbox = it[followers[Users.outbox]], - url = it[followers[Users.url]], - publicKey = it[followers[Users.publicKey]], - privateKey = it[followers[Users.privateKey]], - createdAt = Instant.ofEpochMilli(it[followers[Users.createdAt]]), - keyId = it[followers[Users.keyId]], - followers = it[followers[Users.followers]], - following = it[followers[Users.following]], - instance = it[followers[Users.instance]] - ) - } + return userQueryService.findByIds( + relationshipQueryService.findByTargetIdAndFollowing(id, true).map { it.userId }) } - override suspend fun findFollowersByNameAndDomain(name: String, domain: String): List { - val followers = Users.alias("FOLLOWERS") - return Users.innerJoin( - otherTable = UsersFollowers, - onColumn = { id }, - otherColumn = { userId } - ) - .innerJoin( - otherTable = followers, - onColumn = { UsersFollowers.followerId }, - otherColumn = { followers[Users.id] } - ) - .slice( - followers[Users.id], - followers[Users.name], - followers[Users.domain], - followers[Users.screenName], - followers[Users.description], - followers[Users.password], - followers[Users.inbox], - followers[Users.outbox], - followers[Users.url], - followers[Users.publicKey], - followers[Users.privateKey], - followers[Users.createdAt], - followers[Users.keyId], - followers[Users.following], - followers[Users.followers], - followers[Users.instance] - ) - .select { Users.name eq name and (Users.domain eq domain) } - .map { - userBuilder.of( - id = it[followers[Users.id]], - name = it[followers[Users.name]], - domain = it[followers[Users.domain]], - screenName = it[followers[Users.screenName]], - description = it[followers[Users.description]], - password = it[followers[Users.password]], - inbox = it[followers[Users.inbox]], - outbox = it[followers[Users.outbox]], - url = it[followers[Users.url]], - publicKey = it[followers[Users.publicKey]], - privateKey = it[followers[Users.privateKey]], - createdAt = Instant.ofEpochMilli(it[followers[Users.createdAt]]), - keyId = it[followers[Users.keyId]], - followers = it[followers[Users.followers]], - following = it[followers[Users.following]], - instance = it[followers[Users.instance]] - ) - } - } - - override suspend fun findFollowingById(id: Long): List { - val followers = Users.alias("FOLLOWERS") - return Users.innerJoin( - otherTable = UsersFollowers, - onColumn = { Users.id }, - otherColumn = { userId } - ) - .innerJoin( - otherTable = followers, - onColumn = { UsersFollowers.followerId }, - otherColumn = { followers[Users.id] } - ) - .slice( - followers[Users.id], - followers[Users.name], - followers[Users.domain], - followers[Users.screenName], - followers[Users.description], - followers[Users.password], - followers[Users.inbox], - followers[Users.outbox], - followers[Users.url], - followers[Users.publicKey], - followers[Users.privateKey], - followers[Users.createdAt], - followers[Users.keyId], - followers[Users.following], - followers[Users.followers], - followers[Users.instance] - ) - .select { followers[Users.id] eq id } - .map { - userBuilder.of( - id = it[followers[Users.id]], - name = it[followers[Users.name]], - domain = it[followers[Users.domain]], - screenName = it[followers[Users.screenName]], - description = it[followers[Users.description]], - password = it[followers[Users.password]], - inbox = it[followers[Users.inbox]], - outbox = it[followers[Users.outbox]], - url = it[followers[Users.url]], - publicKey = it[followers[Users.publicKey]], - privateKey = it[followers[Users.privateKey]], - createdAt = Instant.ofEpochMilli(it[followers[Users.createdAt]]), - keyId = it[followers[Users.keyId]], - followers = it[followers[Users.followers]], - following = it[followers[Users.following]], - instance = it[followers[Users.instance]] - ) - } - } - - override suspend fun findFollowingByNameAndDomain(name: String, domain: String): List { - val followers = Users.alias("FOLLOWERS") - return Users.innerJoin( - otherTable = UsersFollowers, - onColumn = { id }, - otherColumn = { userId } - ) - .innerJoin( - otherTable = followers, - onColumn = { UsersFollowers.followerId }, - otherColumn = { followers[Users.id] } - ) - .slice( - followers[Users.id], - followers[Users.name], - followers[Users.domain], - followers[Users.screenName], - followers[Users.description], - followers[Users.password], - followers[Users.inbox], - followers[Users.outbox], - followers[Users.url], - followers[Users.publicKey], - followers[Users.privateKey], - followers[Users.createdAt], - followers[Users.keyId], - followers[Users.following], - followers[Users.followers], - followers[Users.instance] - ) - .select { followers[Users.name] eq name and (followers[Users.domain] eq domain) } - .map { - userBuilder.of( - id = it[followers[Users.id]], - name = it[followers[Users.name]], - domain = it[followers[Users.domain]], - screenName = it[followers[Users.screenName]], - description = it[followers[Users.description]], - password = it[followers[Users.password]], - inbox = it[followers[Users.inbox]], - outbox = it[followers[Users.outbox]], - url = it[followers[Users.url]], - publicKey = it[followers[Users.publicKey]], - privateKey = it[followers[Users.privateKey]], - createdAt = Instant.ofEpochMilli(it[followers[Users.createdAt]]), - keyId = it[followers[Users.keyId]], - followers = it[followers[Users.followers]], - following = it[followers[Users.following]], - instance = it[followers[Users.instance]] - ) - } - } - - override suspend fun appendFollower(user: Long, follower: Long) { - UsersFollowers.insert { - it[userId] = user - it[followerId] = follower - } - } - - override suspend fun removeFollower(user: Long, follower: Long) { - UsersFollowers.deleteWhere { userId eq user and (followerId eq follower) } - } - - override suspend fun alreadyFollow(userId: Long, followerId: Long): Boolean { - return UsersFollowers.select { UsersFollowers.userId eq userId or (UsersFollowers.followerId eq followerId) } - .empty() - .not() - } + override suspend fun alreadyFollow(userId: Long, followerId: Long): Boolean = + relationshipRepository.findByUserIdAndTargetUserId(followerId, userId)?.following ?: false } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/UserRepositoryImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/UserRepositoryImpl.kt index ecd910a6..b4adbaad 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/UserRepositoryImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/UserRepositoryImpl.kt @@ -4,7 +4,6 @@ import dev.usbharu.hideout.application.infrastructure.exposed.ResultRowMapper import dev.usbharu.hideout.application.service.id.IdGenerateService import dev.usbharu.hideout.core.domain.model.user.User import dev.usbharu.hideout.core.domain.model.user.UserRepository -import org.jetbrains.exposed.dao.id.LongIdTable import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.springframework.stereotype.Repository @@ -62,15 +61,6 @@ class UserRepositoryImpl( override suspend fun findById(id: Long): User? = Users.select { Users.id eq id }.singleOrNull()?.let(userResultRowMapper::map) - override suspend fun deleteFollowRequest(id: Long, follower: Long) { - FollowRequests.deleteWhere { userId.eq(id) and followerId.eq(follower) } - } - - override suspend fun findFollowRequestsById(id: Long, follower: Long): Boolean { - return FollowRequests.select { (FollowRequests.userId eq id) and (FollowRequests.followerId eq follower) } - .singleOrNull() != null - } - override suspend fun delete(id: Long) { Users.deleteWhere { Users.id.eq(id) } } @@ -108,21 +98,3 @@ object Users : Table("users") { uniqueIndex(name, domain) } } - -object UsersFollowers : LongIdTable("users_followers") { - val userId: Column = long("user_id").references(Users.id).index() - val followerId: Column = long("follower_id").references(Users.id) - - init { - uniqueIndex(userId, followerId) - } -} - -object FollowRequests : LongIdTable("follow_requests") { - val userId: Column = long("user_id").references(Users.id) - val followerId: Column = long("follower_id").references(Users.id) - - init { - uniqueIndex(userId, followerId) - } -} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/query/FollowerQueryService.kt b/src/main/kotlin/dev/usbharu/hideout/core/query/FollowerQueryService.kt index e5935576..8d3d5bb4 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/query/FollowerQueryService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/query/FollowerQueryService.kt @@ -2,12 +2,8 @@ package dev.usbharu.hideout.core.query import dev.usbharu.hideout.core.domain.model.user.User +@Deprecated("Use RelationshipQueryService") interface FollowerQueryService { suspend fun findFollowersById(id: Long): List - suspend fun findFollowersByNameAndDomain(name: String, domain: String): List - suspend fun findFollowingById(id: Long): List - suspend fun findFollowingByNameAndDomain(name: String, domain: String): List - suspend fun appendFollower(user: Long, follower: Long) - suspend fun removeFollower(user: Long, follower: Long) suspend fun alreadyFollow(userId: Long, followerId: Long): Boolean } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/query/RelationshipQueryService.kt b/src/main/kotlin/dev/usbharu/hideout/core/query/RelationshipQueryService.kt new file mode 100644 index 00000000..5f051397 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/core/query/RelationshipQueryService.kt @@ -0,0 +1,8 @@ +package dev.usbharu.hideout.core.query + +import dev.usbharu.hideout.core.domain.model.relationship.Relationship + +interface RelationshipQueryService { + + suspend fun findByTargetIdAndFollowing(targetId: Long, following: Boolean): List +} diff --git a/src/test/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiServiceImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiServiceImplTest.kt index a34ed5dc..19834065 100644 --- a/src/test/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiServiceImplTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiServiceImplTest.kt @@ -305,7 +305,6 @@ class AccountApiServiceImplTest { assertThat(relationships).hasSize(0) verify(followerQueryService, never()).alreadyFollow(any(), any()) - verify(userRepository, never()).findFollowRequestsById(any(), any()) } @Test From d7637a4fcb242594ae7ea91eef0276957f929349 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 10 Dec 2023 16:13:51 +0900 Subject: [PATCH 25/43] =?UTF-8?q?feat:=20RelationshipRepository=E3=81=A8Re?= =?UTF-8?q?lationshipQueryService=E3=81=AE=E5=AE=9F=E8=A3=85=E3=82=92?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RelationshipRepositoryImpl.kt | 85 +++++++++++++++++++ .../RelationshipQueryServiceImpl.kt | 16 ++++ 2 files changed, 101 insertions(+) create mode 100644 src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepositoryImpl.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/RelationshipQueryServiceImpl.kt diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepositoryImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepositoryImpl.kt new file mode 100644 index 00000000..bb0513e6 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepositoryImpl.kt @@ -0,0 +1,85 @@ +package dev.usbharu.hideout.core.domain.model.relationship + +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Users +import org.jetbrains.exposed.dao.id.LongIdTable +import org.jetbrains.exposed.sql.* +import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq +import org.springframework.stereotype.Service + +@Service +class RelationshipRepositoryImpl : RelationshipRepository { + override suspend fun save(relationship: Relationship): Relationship { + val singleOrNull = + Relationships + .select { + (Relationships.userId eq relationship.userId) + .and(Relationships.targetUserId eq relationship.targetUserId) + } + .singleOrNull() + + if (singleOrNull == null) { + Relationships.insert { + it[userId] = relationship.userId + it[targetUserId] = relationship.targetUserId + it[following] = relationship.following + it[blocking] = relationship.blocking + it[muting] = relationship.blocking + it[followRequest] = relationship.followRequest + it[ignoreFollowRequestFromTarget] = relationship.ignoreFollowRequestFromTarget + } + } else { + Relationships + .update({ + (Relationships.userId eq relationship.userId) + .and(Relationships.targetUserId eq relationship.targetUserId) + }) { + it[following] = relationship.following + it[blocking] = relationship.blocking + it[muting] = relationship.blocking + it[followRequest] = relationship.followRequest + it[ignoreFollowRequestFromTarget] = relationship.ignoreFollowRequestFromTarget + } + } + return relationship + } + + override suspend fun delete(relationship: Relationship) { + Relationships.deleteWhere { + (Relationships.userId eq relationship.userId) + .and(Relationships.targetUserId eq relationship.targetUserId) + } + } + + override suspend fun findByUserIdAndTargetUserId(userId: Long, targetUserId: Long): Relationship? { + return Relationships.select { + (Relationships.userId eq userId) + .and(Relationships.targetUserId eq targetUserId) + }.singleOrNull() + ?.toRelationships() + } + +} + +fun ResultRow.toRelationships(): Relationship = Relationship( + this[Relationships.userId], + this[Relationships.targetUserId], + this[Relationships.following], + this[Relationships.blocking], + this[Relationships.muting], + this[Relationships.followRequest], + this[Relationships.ignoreFollowRequestFromTarget] +) + +object Relationships : LongIdTable("relationships") { + val userId = long("user_id").references(Users.id) + val targetUserId = long("target_user_id").references(Users.id) + val following = bool("following") + val blocking = bool("blocking") + val muting = bool("muting") + val followRequest = bool("follow_request") + val ignoreFollowRequestFromTarget = bool("ignore_follow_request") + + init { + uniqueIndex(userId, targetUserId) + } +} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/RelationshipQueryServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/RelationshipQueryServiceImpl.kt new file mode 100644 index 00000000..f1a122c9 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/RelationshipQueryServiceImpl.kt @@ -0,0 +1,16 @@ +package dev.usbharu.hideout.core.infrastructure.exposedquery + +import dev.usbharu.hideout.core.domain.model.relationship.Relationship +import dev.usbharu.hideout.core.domain.model.relationship.Relationships +import dev.usbharu.hideout.core.domain.model.relationship.toRelationships +import dev.usbharu.hideout.core.query.RelationshipQueryService +import org.jetbrains.exposed.sql.and +import org.jetbrains.exposed.sql.select +import org.springframework.stereotype.Service + +@Service +class RelationshipQueryServiceImpl : RelationshipQueryService { + override suspend fun findByTargetIdAndFollowing(targetId: Long, following: Boolean): List = + Relationships.select { Relationships.targetUserId eq targetId and (Relationships.following eq following) } + .map { it.toRelationships() } +} From 76cd8614d0b6db902bd360b255435e8b65dc0b4a Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 10 Dec 2023 16:20:29 +0900 Subject: [PATCH 26/43] =?UTF-8?q?refactor:=20=E3=83=86=E3=83=BC=E3=83=96?= =?UTF-8?q?=E3=83=AB=E5=AE=9A=E7=BE=A9=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/core/domain/model/block/Block.kt | 12 ----- .../domain/model/block/BlockRepository.kt | 31 ----------- .../exposedrepository/BlockRepositoryImpl.kt | 53 ------------------- .../resources/db/migration/V1__Init_DB.sql | 33 ++++++------ .../resources/db/migration/V2__Add_Block.sql | 8 --- 5 files changed, 17 insertions(+), 120 deletions(-) delete mode 100644 src/main/kotlin/dev/usbharu/hideout/core/domain/model/block/Block.kt delete mode 100644 src/main/kotlin/dev/usbharu/hideout/core/domain/model/block/BlockRepository.kt delete mode 100644 src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/BlockRepositoryImpl.kt delete mode 100644 src/main/resources/db/migration/V2__Add_Block.sql diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/block/Block.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/block/Block.kt deleted file mode 100644 index 7435183e..00000000 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/block/Block.kt +++ /dev/null @@ -1,12 +0,0 @@ -package dev.usbharu.hideout.core.domain.model.block - -/** - * ブロック関係を表します - * - * @property userId ブロックの動作を行ったユーザーid - * @property target ブロックの対象のユーザーid - */ -data class Block( - val userId: Long, - val target: Long -) diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/block/BlockRepository.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/block/BlockRepository.kt deleted file mode 100644 index 3504ffea..00000000 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/block/BlockRepository.kt +++ /dev/null @@ -1,31 +0,0 @@ -package dev.usbharu.hideout.core.domain.model.block - -/** - * ブロックの状態を永続化します - * - */ -interface BlockRepository { - /** - * ブロックの状態を永続化します - * - * @param block 永続化するブロック - * @return 永続化されたブロック - */ - suspend fun save(block: Block): Block - - /** - * ブロックの状態を削除します - * - * @param block 削除する永続化されたブロック - */ - suspend fun delete(block: Block) - - /** - * - * - * @param userId - * @param target - * @return - */ - suspend fun findByUserIdAndTarget(userId: Long, target: Long): Block -} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/BlockRepositoryImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/BlockRepositoryImpl.kt deleted file mode 100644 index c3d60ea9..00000000 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/BlockRepositoryImpl.kt +++ /dev/null @@ -1,53 +0,0 @@ -package dev.usbharu.hideout.core.infrastructure.exposedrepository - -import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException -import dev.usbharu.hideout.core.domain.model.block.Block -import dev.usbharu.hideout.core.domain.model.block.BlockRepository -import dev.usbharu.hideout.util.singleOr -import org.jetbrains.exposed.dao.id.LongIdTable -import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq -import org.jetbrains.exposed.sql.and -import org.jetbrains.exposed.sql.deleteWhere -import org.jetbrains.exposed.sql.insert -import org.jetbrains.exposed.sql.select -import org.springframework.stereotype.Repository - -@Repository -class BlockRepositoryImpl : BlockRepository { - override suspend fun save(block: Block): Block { - Blocks.insert { - it[userId] = block.userId - it[target] = block.target - } - return block - } - - override suspend fun delete(block: Block) { - Blocks.deleteWhere { Blocks.userId eq block.userId and (Blocks.target eq block.target) } - } - - override suspend fun findByUserIdAndTarget(userId: Long, target: Long): Block { - val singleOr = Blocks - .select { Blocks.userId eq userId and (Blocks.target eq target) } - .singleOr { - FailedToGetResourcesException( - "userId: $userId target: $target is duplicate or not exist.", - it - ) - } - - return Block( - singleOr[Blocks.userId], - singleOr[Blocks.target] - ) - } -} - -object Blocks : LongIdTable("blocks") { - val userId = long("user_id").references(Users.id).index() - val target = long("target").references(Users.id) - - init { - uniqueIndex(userId, target) - } -} diff --git a/src/main/resources/db/migration/V1__Init_DB.sql b/src/main/resources/db/migration/V1__Init_DB.sql index 1440fb61..234078e7 100644 --- a/src/main/resources/db/migration/V1__Init_DB.sql +++ b/src/main/resources/db/migration/V1__Init_DB.sql @@ -34,14 +34,7 @@ create table if not exists users unique ("name", "domain"), constraint fk_users_instance__id foreign key ("instance") references instance (id) on delete restrict on update restrict ); -create table if not exists follow_requests -( - id bigserial primary key, - user_id bigint not null, - follower_id bigint not null, - constraint fk_follow_requests_user_id__id foreign key (user_id) references users (id) on delete restrict on update restrict, - constraint fk_follow_requests_follower_id__id foreign key (follower_id) references users (id) on delete restrict on update restrict -); + create table if not exists media ( id bigint primary key, @@ -119,14 +112,7 @@ create table if not exists timelines is_pure_repost boolean not null, media_ids varchar(255) not null ); -create table if not exists users_followers -( - id bigserial primary key, - user_id bigint not null, - follower_id bigint not null, - constraint fk_users_followers_user_id__id foreign key (user_id) references users (id) on delete restrict on update restrict, - constraint fk_users_followers_follower_id__id foreign key (follower_id) references users (id) on delete restrict on update restrict -); + create table if not exists application_authorization ( id varchar(255) primary key, @@ -186,4 +172,19 @@ create table if not exists registered_client scopes varchar(1000) not null, client_settings varchar(2000) not null, token_settings varchar(2000) not null +); + +create table if not exists relationships +( + id bigserial primary key, + user_id bigint not null, + target_user_id bigint not null, + following boolean not null, + blocking boolean not null, + muting boolean not null, + follow_request boolean not null, + ignore_follow_request boolean not null, + constraint fk_relationships_user_id__id foreign key (user_id) references users (id) on delete restrict on update restrict, + constraint fk_relationships_target_user_id__id foreign key (target_user_id) references users (id) on delete restrict on update restrict, + unique (user_id, target_user_id) ) diff --git a/src/main/resources/db/migration/V2__Add_Block.sql b/src/main/resources/db/migration/V2__Add_Block.sql deleted file mode 100644 index d82d705e..00000000 --- a/src/main/resources/db/migration/V2__Add_Block.sql +++ /dev/null @@ -1,8 +0,0 @@ -create table if not exists blocks -( - id bigserial primary key, - user_id bigint not null, - target bigint not null, - constraint fk_blocks_user_id__id foreign key (user_id) references users (id) on delete restrict on update restrict, - constraint fk_blocks_target_id__id foreign key (target) references users (id) on delete restrict on update restrict -); From 2b765a1fa2581d9f880d030fd1f81ffed18c6331 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 10 Dec 2023 16:47:18 +0900 Subject: [PATCH 27/43] =?UTF-8?q?wip=20=E6=9C=AA=E5=AE=9F=E8=A3=85?= =?UTF-8?q?=E3=81=AE=E3=82=82=E3=81=AE=E3=82=92=E4=BB=AE=E3=81=A7=E5=AE=9F?= =?UTF-8?q?=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../activity/reject/ApSendRejectServiceImpl.kt | 11 +++++++++++ .../activity/undo/APSendUndoServiceImpl.kt | 15 +++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/ApSendRejectServiceImpl.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APSendUndoServiceImpl.kt diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/ApSendRejectServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/ApSendRejectServiceImpl.kt new file mode 100644 index 00000000..383d1e38 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/ApSendRejectServiceImpl.kt @@ -0,0 +1,11 @@ +package dev.usbharu.hideout.activitypub.service.activity.reject + +import dev.usbharu.hideout.core.domain.model.user.User +import org.springframework.stereotype.Service + +@Service +class ApSendRejectServiceImpl : ApSendRejectService { + override suspend fun sendReject(user: User, target: User) { + TODO("Not yet implemented") + } +} diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APSendUndoServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APSendUndoServiceImpl.kt new file mode 100644 index 00000000..bd48ba1b --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APSendUndoServiceImpl.kt @@ -0,0 +1,15 @@ +package dev.usbharu.hideout.activitypub.service.activity.undo + +import dev.usbharu.hideout.core.domain.model.user.User +import org.springframework.stereotype.Service + +@Service +class APSendUndoServiceImpl : APSendUndoService { + override suspend fun sendUndoFollow(user: User, target: User) { + TODO("Not yet implemented") + } + + override suspend fun sendUndoBlock(user: User, target: User) { + TODO("Not yet implemented") + } +} From e0e8a4cc0bdf29c2d6fd200cec47e55fae0e699b Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 10 Dec 2023 16:47:36 +0900 Subject: [PATCH 28/43] =?UTF-8?q?test:=20=E3=83=86=E3=82=B9=E3=83=88?= =?UTF-8?q?=E7=94=A8=E3=81=AESQL=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...pSignature認証でフォロワーがfollowers投稿を取得できる.sql | 5 +++-- ...httpSignature認証でフォロワーがpublic投稿を取得できる.sql | 5 +++-- ...tpSignature認証でフォロワーがunlisted投稿を取得できる.sql | 5 +++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/intTest/resources/sql/note/httpSignature認証でフォロワーがfollowers投稿を取得できる.sql b/src/intTest/resources/sql/note/httpSignature認証でフォロワーがfollowers投稿を取得できる.sql index a01f34bb..d3076eb1 100644 --- a/src/intTest/resources/sql/note/httpSignature認証でフォロワーがfollowers投稿を取得できる.sql +++ b/src/intTest/resources/sql/note/httpSignature認証でフォロワーがfollowers投稿を取得できる.sql @@ -21,8 +21,9 @@ VALUES (9, 'test-user9', 'follower.example.com', 'Im test-user9.', 'THis account 'https://follower.example.com/users/test-user9/following', 'https://follower.example.com/users/test-user9/followers', null); -insert into USERS_FOLLOWERS (USER_ID, FOLLOWER_ID) -VALUES (8, 9); +insert into relationships (user_id, target_user_id, following, blocking, muting, follow_request, + ignore_follow_request) +VALUES (9, 8, true, false, false, false, false); insert into POSTS (ID, USER_ID, OVERVIEW, TEXT, CREATED_AT, VISIBILITY, URL, REPLY_ID, REPOST_ID, SENSITIVE, AP_ID) VALUES (1239, 8, null, 'test post', 12345680, 2, 'https://example.com/users/test-user8/posts/1239', null, null, false, diff --git a/src/intTest/resources/sql/note/httpSignature認証でフォロワーがpublic投稿を取得できる.sql b/src/intTest/resources/sql/note/httpSignature認証でフォロワーがpublic投稿を取得できる.sql index 6c78ae20..82d37cc0 100644 --- a/src/intTest/resources/sql/note/httpSignature認証でフォロワーがpublic投稿を取得できる.sql +++ b/src/intTest/resources/sql/note/httpSignature認証でフォロワーがpublic投稿を取得できる.sql @@ -21,8 +21,9 @@ VALUES (5, 'test-user5', 'follower.example.com', 'Im test user5.', 'THis account 'https://follower.example.com/users/test-user5/following', 'https://follower.example.com/users/test-user5/followers', null); -insert into USERS_FOLLOWERS (USER_ID, FOLLOWER_ID) -VALUES (4, 5); +insert into relationships (user_id, target_user_id, following, blocking, muting, follow_request, + ignore_follow_request) +VALUES (5, 4, true, false, false, false, false); insert into POSTS (ID, "USER_ID", OVERVIEW, TEXT, "CREATED_AT", VISIBILITY, URL, "REPOST_ID", "REPLY_ID", SENSITIVE, AP_ID) diff --git a/src/intTest/resources/sql/note/httpSignature認証でフォロワーがunlisted投稿を取得できる.sql b/src/intTest/resources/sql/note/httpSignature認証でフォロワーがunlisted投稿を取得できる.sql index 94de8f2e..cce85d78 100644 --- a/src/intTest/resources/sql/note/httpSignature認証でフォロワーがunlisted投稿を取得できる.sql +++ b/src/intTest/resources/sql/note/httpSignature認証でフォロワーがunlisted投稿を取得できる.sql @@ -21,8 +21,9 @@ VALUES (7, 'test-user7', 'follower.example.com', 'Im test-user7.', 'THis account 'https://follower.example.com/users/test-user7/following', 'https://follower.example.com/users/test-user7/followers', null); -insert into USERS_FOLLOWERS (USER_ID, FOLLOWER_ID) -VALUES (6, 7); +insert into relationships (user_id, target_user_id, following, blocking, muting, follow_request, + ignore_follow_request) +VALUES (7, 6, true, false, false, false, false); insert into POSTS (ID, "USER_ID", OVERVIEW, TEXT, "CREATED_AT", VISIBILITY, URL, "REPOST_ID", "REPLY_ID", SENSITIVE, AP_ID) From 8fba1571e14725477c64a112cf03a969b7640e5d Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 10 Dec 2023 17:43:46 +0900 Subject: [PATCH 29/43] =?UTF-8?q?feat:=20=E4=BB=AE=E3=81=AE=E5=AE=9F?= =?UTF-8?q?=E8=A3=85=E3=82=92=E3=81=A1=E3=82=83=E3=82=93=E3=81=A8=E5=AE=9F?= =?UTF-8?q?=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../activitypub/domain/model/Accept.kt | 16 ++----- .../activity/accept/ApSendAcceptService.kt | 28 +++++++++-- .../activity/reject/ApSendRejectService.kt | 2 +- .../reject/ApSendRejectServiceImpl.kt | 27 +++++++++-- .../activity/undo/APSendUndoServiceImpl.kt | 47 +++++++++++++++++-- .../core/external/job/DeliverAcceptJob.kt | 36 ++++++++++++++ .../core/external/job/DeliverRejectJob.kt | 36 ++++++++++++++ .../core/external/job/DeliverUndoJob.kt | 38 +++++++++++++++ .../relationship/RelationshipServiceImpl.kt | 4 +- .../hideout/ap/ContextSerializerTest.kt | 1 - .../RelationshipServiceImplTest.kt | 14 +++--- 11 files changed, 217 insertions(+), 32 deletions(-) create mode 100644 src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverAcceptJob.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverRejectJob.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverUndoJob.kt diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Accept.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Accept.kt index 2cd730db..13f6a31d 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Accept.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Accept.kt @@ -8,7 +8,6 @@ import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer open class Accept @JsonCreator constructor( type: List = emptyList(), - override val name: String, @JsonDeserialize(using = ObjectDeserializer::class) @JsonProperty("object") val apObject: Object, @@ -16,9 +15,7 @@ open class Accept @JsonCreator constructor( ) : Object( type = add(type, "Accept") ), - HasActor, - HasName { - + HasActor { override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false @@ -26,7 +23,6 @@ open class Accept @JsonCreator constructor( other as Accept - if (name != other.name) return false if (apObject != other.apObject) return false if (actor != other.actor) return false @@ -35,7 +31,6 @@ open class Accept @JsonCreator constructor( override fun hashCode(): Int { var result = super.hashCode() - result = 31 * result + name.hashCode() result = 31 * result + apObject.hashCode() result = 31 * result + actor.hashCode() return result @@ -43,10 +38,9 @@ open class Accept @JsonCreator constructor( override fun toString(): String { return "Accept(" + - "name='$name', " + - "apObject=$apObject, " + - "actor='$actor'" + - ")" + - " ${super.toString()}" + "apObject=$apObject, " + + "actor='$actor'" + + ")" + + " ${super.toString()}" } } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApSendAcceptService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApSendAcceptService.kt index 59f4f4ec..573e7104 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApSendAcceptService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApSendAcceptService.kt @@ -1,16 +1,36 @@ package dev.usbharu.hideout.activitypub.service.activity.accept +import dev.usbharu.hideout.activitypub.domain.model.Accept +import dev.usbharu.hideout.activitypub.domain.model.Follow import dev.usbharu.hideout.core.domain.model.user.User +import dev.usbharu.hideout.core.external.job.DeliverAcceptJob +import dev.usbharu.hideout.core.external.job.DeliverAcceptJobParam +import dev.usbharu.hideout.core.service.job.JobQueueParentService import org.springframework.stereotype.Service interface ApSendAcceptService { - suspend fun sendAccept(user: User, target: User) + suspend fun sendAcceptFollow(user: User, target: User) } @Service -class ApSendAcceptServiceImpl : ApSendAcceptService { - override suspend fun sendAccept(user: User, target: User) { - TODO("Not yet implemented") +class ApSendAcceptServiceImpl( + private val jobQueueParentService: JobQueueParentService, + private val deliverAcceptJob: DeliverAcceptJob +) : ApSendAcceptService { + override suspend fun sendAcceptFollow(user: User, target: User) { + val deliverAcceptJobParam = DeliverAcceptJobParam( + Accept( + apObject = Follow( + apObject = target.url, + actor = user.url + ), + actor = user.url + ), + target.inbox, + user.id + ) + + jobQueueParentService.scheduleTypeSafe(deliverAcceptJob, deliverAcceptJobParam) } } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/ApSendRejectService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/ApSendRejectService.kt index 29c66e28..ecb675c2 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/ApSendRejectService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/ApSendRejectService.kt @@ -3,5 +3,5 @@ package dev.usbharu.hideout.activitypub.service.activity.reject import dev.usbharu.hideout.core.domain.model.user.User interface ApSendRejectService { - suspend fun sendReject(user: User, target: User) + suspend fun sendRejectFollow(user: User, target: User) } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/ApSendRejectServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/ApSendRejectServiceImpl.kt index 383d1e38..ded3c2a5 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/ApSendRejectServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/ApSendRejectServiceImpl.kt @@ -1,11 +1,32 @@ package dev.usbharu.hideout.activitypub.service.activity.reject +import dev.usbharu.hideout.activitypub.domain.model.Follow +import dev.usbharu.hideout.activitypub.domain.model.Reject +import dev.usbharu.hideout.application.config.ApplicationConfig import dev.usbharu.hideout.core.domain.model.user.User +import dev.usbharu.hideout.core.external.job.DeliverRejectJob +import dev.usbharu.hideout.core.external.job.DeliverRejectJobParam +import dev.usbharu.hideout.core.service.job.JobQueueParentService import org.springframework.stereotype.Service @Service -class ApSendRejectServiceImpl : ApSendRejectService { - override suspend fun sendReject(user: User, target: User) { - TODO("Not yet implemented") +class ApSendRejectServiceImpl( + private val applicationConfig: ApplicationConfig, + private val jobQueueParentService: JobQueueParentService, + private val deliverRejectJob: DeliverRejectJob +) : ApSendRejectService { + override suspend fun sendRejectFollow(user: User, target: User) { + + val deliverRejectJobParam = DeliverRejectJobParam( + Reject( + user.url, + "${applicationConfig.url}/reject/${user.id}/${target.id}", + Follow(apObject = user.url, actor = target.url) + ), + target.inbox, + user.id + ) + + jobQueueParentService.scheduleTypeSafe(deliverRejectJob, deliverRejectJobParam) } } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APSendUndoServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APSendUndoServiceImpl.kt index bd48ba1b..3945cc5a 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APSendUndoServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APSendUndoServiceImpl.kt @@ -1,15 +1,56 @@ package dev.usbharu.hideout.activitypub.service.activity.undo +import dev.usbharu.hideout.activitypub.domain.model.Block +import dev.usbharu.hideout.activitypub.domain.model.Follow +import dev.usbharu.hideout.activitypub.domain.model.Undo +import dev.usbharu.hideout.application.config.ApplicationConfig import dev.usbharu.hideout.core.domain.model.user.User +import dev.usbharu.hideout.core.external.job.DeliverUndoJob +import dev.usbharu.hideout.core.external.job.DeliverUndoJobParam +import dev.usbharu.hideout.core.service.job.JobQueueParentService import org.springframework.stereotype.Service +import java.time.Instant @Service -class APSendUndoServiceImpl : APSendUndoService { +class APSendUndoServiceImpl( + private val jobQueueParentService: JobQueueParentService, + private val deliverUndoJob: DeliverUndoJob, + private val applicationConfig: ApplicationConfig +) : APSendUndoService { override suspend fun sendUndoFollow(user: User, target: User) { - TODO("Not yet implemented") + val deliverUndoJobParam = DeliverUndoJobParam( + Undo( + actor = user.url, + id = "${applicationConfig.url}/undo/follow/${user.id}/${target.url}", + `object` = Follow( + apObject = user.url, + actor = target.url + ), + published = Instant.now().toString() + ), + target.inbox, + user.id + ) + + jobQueueParentService.scheduleTypeSafe(deliverUndoJob, deliverUndoJobParam) } override suspend fun sendUndoBlock(user: User, target: User) { - TODO("Not yet implemented") + val deliverUndoJobParam = DeliverUndoJobParam( + Undo( + actor = user.url, + id = "${applicationConfig.url}/undo/block/${user.id}/${target.url}", + `object` = Block( + apObject = user.url, + actor = target.url, + id = "${applicationConfig.url}/block/${user.id}/${target.id}" + ), + published = Instant.now().toString() + ), + target.inbox, + user.id + ) + + jobQueueParentService.scheduleTypeSafe(deliverUndoJob, deliverUndoJobParam) } } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverAcceptJob.kt b/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverAcceptJob.kt new file mode 100644 index 00000000..ad002175 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverAcceptJob.kt @@ -0,0 +1,36 @@ +package dev.usbharu.hideout.core.external.job + +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.module.kotlin.readValue +import dev.usbharu.hideout.activitypub.domain.model.Accept +import kjob.core.dsl.ScheduleContext +import kjob.core.job.JobProps +import org.springframework.stereotype.Component + +data class DeliverAcceptJobParam( + val accept: Accept, + val inbox: String, + val signer: Long +) + +@Component +class DeliverAcceptJob(private val objectMapper: ObjectMapper) : HideoutJob() { + + val accept = string("accept") + val inbox = string("inbox") + val signer = long("signer") + + override fun convert(value: DeliverAcceptJobParam): ScheduleContext.(DeliverAcceptJob) -> Unit = { + props[accept] = objectMapper.writeValueAsString(value.accept) + props[inbox] = value.inbox + props[signer] = value.signer + } + + override fun convert(props: JobProps): DeliverAcceptJobParam { + return DeliverAcceptJobParam( + objectMapper.readValue(props[accept]), + props[inbox], + props[signer] + ) + } +} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverRejectJob.kt b/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverRejectJob.kt new file mode 100644 index 00000000..9f923534 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverRejectJob.kt @@ -0,0 +1,36 @@ +package dev.usbharu.hideout.core.external.job + +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.module.kotlin.readValue +import dev.usbharu.hideout.activitypub.domain.model.Reject +import kjob.core.dsl.ScheduleContext +import kjob.core.job.JobProps +import org.springframework.beans.factory.annotation.Qualifier +import org.springframework.stereotype.Component + +data class DeliverRejectJobParam( + val reject: Reject, + val inbox: String, + val signer: Long +) + +@Component +class DeliverRejectJob(@Qualifier("activitypub") private val objectMapper: ObjectMapper) : + HideoutJob() { + val reject = string("reject") + val inbox = string("inbox") + val signer = long("signer") + + override fun convert(value: DeliverRejectJobParam): ScheduleContext.(DeliverRejectJob) -> Unit = + { + props[reject] = objectMapper.writeValueAsString(value.reject) + props[inbox] = value.inbox + props[signer] = value.signer + } + + override fun convert(props: JobProps): DeliverRejectJobParam = DeliverRejectJobParam( + objectMapper.readValue(props[reject]), + props[inbox], + props[signer] + ) +} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverUndoJob.kt b/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverUndoJob.kt new file mode 100644 index 00000000..b8ad9088 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverUndoJob.kt @@ -0,0 +1,38 @@ +package dev.usbharu.hideout.core.external.job + +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.module.kotlin.readValue +import dev.usbharu.hideout.activitypub.domain.model.Undo +import kjob.core.dsl.ScheduleContext +import kjob.core.job.JobProps +import org.springframework.beans.factory.annotation.Qualifier +import org.springframework.stereotype.Component + +data class DeliverUndoJobParam( + val undo: Undo, + val inbox: String, + val signer: Long +) + +@Component +class DeliverUndoJob(@Qualifier("activitypub") private val objectMapper: ObjectMapper) : + HideoutJob() { + + val undo = string("undo") + val inbox = string("inbox") + val signer = long("signer") + + override fun convert(value: DeliverUndoJobParam): ScheduleContext.(DeliverUndoJob) -> Unit = { + props[undo] = objectMapper.writeValueAsString(value.undo) + props[inbox] = value.inbox + props[signer] = value.signer + } + + override fun convert(props: JobProps): DeliverUndoJobParam { + return DeliverUndoJobParam( + objectMapper.readValue(props[undo]), + props[inbox], + props[signer] + ) + } +} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImpl.kt index 9e3b98ce..aad49d6a 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImpl.kt @@ -146,7 +146,7 @@ class RelationshipServiceImpl( if (remoteUser != null) { val user = userQueryService.findById(userId) - apSendAcceptService.sendAccept(user, remoteUser) + apSendAcceptService.sendAcceptFollow(user, remoteUser) } } @@ -171,7 +171,7 @@ class RelationshipServiceImpl( if (remoteUser != null) { val user = userQueryService.findById(userId) - apSendRejectService.sendReject(user, remoteUser) + apSendRejectService.sendRejectFollow(user, remoteUser) } } diff --git a/src/test/kotlin/dev/usbharu/hideout/ap/ContextSerializerTest.kt b/src/test/kotlin/dev/usbharu/hideout/ap/ContextSerializerTest.kt index a141c11b..1403b190 100644 --- a/src/test/kotlin/dev/usbharu/hideout/ap/ContextSerializerTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/ap/ContextSerializerTest.kt @@ -10,7 +10,6 @@ class ContextSerializerTest { @Test fun serialize() { val accept = Accept( - name = "aaa", actor = "bbb", apObject = Follow( apObject = "ddd", diff --git a/src/test/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImplTest.kt index 5f1d6141..cda067c5 100644 --- a/src/test/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImplTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImplTest.kt @@ -172,7 +172,7 @@ class RelationshipServiceImplTest { ) ) - verify(apSendAcceptService, times(1)).sendAccept(eq(localUser), eq(remoteUser)) + verify(apSendAcceptService, times(1)).sendAcceptFollow(eq(localUser), eq(remoteUser)) verify(apSendFollowService, never()).sendFollow(any()) } @@ -284,7 +284,7 @@ class RelationshipServiceImplTest { ) ) - verify(apSendAcceptService, never()).sendAccept(any(), any()) + verify(apSendAcceptService, never()).sendAcceptFollow(any(), any()) } @Test @@ -322,14 +322,14 @@ class RelationshipServiceImplTest { ) ) - verify(apSendAcceptService, times(1)).sendAccept(eq(localUser), eq(remoteUser)) + verify(apSendAcceptService, times(1)).sendAcceptFollow(eq(localUser), eq(remoteUser)) } @Test fun `acceptFollowRequest Relationshipが存在しないときは何もしない`() = runTest { relationshipServiceImpl.acceptFollowRequest(1234, 5678, false) - verify(apSendAcceptService, never()).sendAccept(any(), any()) + verify(apSendAcceptService, never()).sendAcceptFollow(any(), any()) } @Test @@ -342,7 +342,7 @@ class RelationshipServiceImplTest { relationshipServiceImpl.acceptFollowRequest(1234, 5678, false) - verify(apSendAcceptService, never()).sendAccept(any(), any()) + verify(apSendAcceptService, never()).sendAcceptFollow(any(), any()) } @Test @@ -440,7 +440,7 @@ class RelationshipServiceImplTest { ) ) - verify(apSendRejectService, never()).sendReject(any(), any()) + verify(apSendRejectService, never()).sendRejectFollow(any(), any()) } @Test @@ -479,7 +479,7 @@ class RelationshipServiceImplTest { ) ) - verify(apSendRejectService, times(1)).sendReject(eq(localUser), eq(remoteUser)) + verify(apSendRejectService, times(1)).sendRejectFollow(eq(localUser), eq(remoteUser)) } @Test From 8c1f5c7752ea828b1a42bf921a321fbd68fc7758 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 10 Dec 2023 17:44:40 +0900 Subject: [PATCH 30/43] =?UTF-8?q?refactor:=20JSON=E3=83=9E=E3=83=83?= =?UTF-8?q?=E3=83=94=E3=83=B3=E3=82=B0=E7=94=A8=E3=81=AEPOJO=E3=81=AE?= =?UTF-8?q?=E5=A4=89=E6=95=B0=E5=90=8D=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dev/usbharu/hideout/activitypub/domain/model/Undo.kt | 9 +++++---- .../activity/like/ApRemoveReactionJobProcessor.kt | 2 +- .../service/activity/undo/APSendUndoServiceImpl.kt | 4 ++-- .../activitypub/service/activity/undo/APUndoProcessor.kt | 4 ++-- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Undo.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Undo.kt index 01dbc17c..b1399777 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Undo.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Undo.kt @@ -1,5 +1,6 @@ package dev.usbharu.hideout.activitypub.domain.model +import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.databind.annotation.JsonDeserialize import dev.usbharu.hideout.activitypub.domain.model.objects.Object import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer @@ -9,7 +10,7 @@ open class Undo( override val actor: String, override val id: String, @JsonDeserialize(using = ObjectDeserializer::class) - @Suppress("VariableNaming") val `object`: Object, + @JsonProperty("object") val apObject: Object, val published: String ) : Object(add(type, "Undo")), HasId, HasActor { @@ -20,7 +21,7 @@ open class Undo( other as Undo - if (`object` != other.`object`) return false + if (apObject != other.apObject) return false if (published != other.published) return false if (actor != other.actor) return false if (id != other.id) return false @@ -30,7 +31,7 @@ open class Undo( override fun hashCode(): Int { var result = super.hashCode() - result = 31 * result + `object`.hashCode() + result = 31 * result + apObject.hashCode() result = 31 * result + published.hashCode() result = 31 * result + actor.hashCode() result = 31 * result + id.hashCode() @@ -38,5 +39,5 @@ open class Undo( } override fun toString(): String = - "Undo(`object`=$`object`, published=$published, actor='$actor', id='$id') ${super.toString()}" + "Undo(`object`=$apObject, published=$published, actor='$actor', id='$id') ${super.toString()}" } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApRemoveReactionJobProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApRemoveReactionJobProcessor.kt index 285670b5..0409b9a0 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApRemoveReactionJobProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/ApRemoveReactionJobProcessor.kt @@ -31,7 +31,7 @@ class ApRemoveReactionJobProcessor( param.inbox, Undo( actor = param.actor, - `object` = like, + apObject = like, id = "${applicationConfig.url}/undo/like/${param.id}", published = Instant.now().toString() ), diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APSendUndoServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APSendUndoServiceImpl.kt index 3945cc5a..e1d4c6fd 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APSendUndoServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APSendUndoServiceImpl.kt @@ -22,7 +22,7 @@ class APSendUndoServiceImpl( Undo( actor = user.url, id = "${applicationConfig.url}/undo/follow/${user.id}/${target.url}", - `object` = Follow( + apObject = Follow( apObject = user.url, actor = target.url ), @@ -40,7 +40,7 @@ class APSendUndoServiceImpl( Undo( actor = user.url, id = "${applicationConfig.url}/undo/block/${user.id}/${target.url}", - `object` = Block( + apObject = Block( apObject = user.url, actor = target.url, id = "${applicationConfig.url}/block/${user.id}/${target.id}" diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt index 6f44abc6..f6a3ff0e 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt @@ -28,13 +28,13 @@ class APUndoProcessor( } val type = - undo.`object`.type.orEmpty() + undo.apObject.type.orEmpty() .firstOrNull { it == "Block" || it == "Follow" || it == "Like" || it == "Announce" || it == "Accept" } ?: return when (type) { "Follow" -> { - val follow = undo.`object` as Follow + val follow = undo.apObject as Follow apUserService.fetchPerson(undo.actor, follow.apObject) val follower = userQueryService.findByUrl(undo.actor) From e850ba8e47d330c486c097d169aa99a61147f0d3 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 10 Dec 2023 17:58:55 +0900 Subject: [PATCH 31/43] =?UTF-8?q?feat:=20JobProcessor=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../accept/APDeliverAcceptJobProcessor.kt | 22 +++++++++++++++++++ .../reject/APDeliverRejectJobProcessor.kt | 22 +++++++++++++++++++ .../undo/APDeliverUndoJobProcessor.kt | 21 ++++++++++++++++++ 3 files changed, 65 insertions(+) create mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/APDeliverAcceptJobProcessor.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/APDeliverRejectJobProcessor.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APDeliverUndoJobProcessor.kt diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/APDeliverAcceptJobProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/APDeliverAcceptJobProcessor.kt new file mode 100644 index 00000000..e9d240a9 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/APDeliverAcceptJobProcessor.kt @@ -0,0 +1,22 @@ +package dev.usbharu.hideout.activitypub.service.activity.accept + +import dev.usbharu.hideout.activitypub.service.common.APRequestService +import dev.usbharu.hideout.core.external.job.DeliverAcceptJob +import dev.usbharu.hideout.core.external.job.DeliverAcceptJobParam +import dev.usbharu.hideout.core.query.UserQueryService +import dev.usbharu.hideout.core.service.job.JobProcessor +import org.springframework.stereotype.Service + +@Service +class APDeliverAcceptJobProcessor( + private val apRequestService: APRequestService, + private val userQueryService: UserQueryService, + private val deliverAcceptJob: DeliverAcceptJob +) : + JobProcessor { + override suspend fun process(param: DeliverAcceptJobParam) { + apRequestService.apPost(param.inbox, param.accept, userQueryService.findById(param.signer)) + } + + override fun job(): DeliverAcceptJob = deliverAcceptJob +} diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/APDeliverRejectJobProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/APDeliverRejectJobProcessor.kt new file mode 100644 index 00000000..8e271288 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/APDeliverRejectJobProcessor.kt @@ -0,0 +1,22 @@ +package dev.usbharu.hideout.activitypub.service.activity.reject + +import dev.usbharu.hideout.activitypub.service.common.APRequestService +import dev.usbharu.hideout.core.external.job.DeliverRejectJob +import dev.usbharu.hideout.core.external.job.DeliverRejectJobParam +import dev.usbharu.hideout.core.query.UserQueryService +import dev.usbharu.hideout.core.service.job.JobProcessor +import org.springframework.stereotype.Component + +@Component +class APDeliverRejectJobProcessor( + private val apRequestService: APRequestService, + private val userQueryService: UserQueryService, + private val deliverRejectJob: DeliverRejectJob +) : + JobProcessor { + override suspend fun process(param: DeliverRejectJobParam) { + apRequestService.apPost(param.inbox, param.reject, userQueryService.findById(param.signer)) + } + + override fun job(): DeliverRejectJob = deliverRejectJob +} diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APDeliverUndoJobProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APDeliverUndoJobProcessor.kt new file mode 100644 index 00000000..800f6ca5 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APDeliverUndoJobProcessor.kt @@ -0,0 +1,21 @@ +package dev.usbharu.hideout.activitypub.service.activity.undo + +import dev.usbharu.hideout.activitypub.service.common.APRequestService +import dev.usbharu.hideout.core.external.job.DeliverUndoJob +import dev.usbharu.hideout.core.external.job.DeliverUndoJobParam +import dev.usbharu.hideout.core.query.UserQueryService +import dev.usbharu.hideout.core.service.job.JobProcessor +import org.springframework.stereotype.Service + +@Service +class APDeliverUndoJobProcessor( + private val deliverUndoJob: DeliverUndoJob, + private val apRequestService: APRequestService, + private val userQueryService: UserQueryService +) : JobProcessor { + override suspend fun process(param: DeliverUndoJobParam) { + apRequestService.apPost(param.inbox, param.undo, userQueryService.findById(param.signer)) + } + + override fun job(): DeliverUndoJob = deliverUndoJob +} From edae03cd58c4eb5fa385192c9d2708d11c0c6e8c Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Mon, 11 Dec 2023 01:22:17 +0900 Subject: [PATCH 32/43] =?UTF-8?q?fix:=20=E3=83=95=E3=82=A9=E3=83=AD?= =?UTF-8?q?=E3=83=BC=E6=89=BF=E8=AA=8D=E3=81=AB=E5=A4=B1=E6=95=97=E3=81=99?= =?UTF-8?q?=E3=82=8B=E3=81=AE=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../accept/APDeliverAcceptJobProcessor.kt | 6 ++- .../activity/accept/ApSendAcceptService.kt | 4 +- .../reject/APDeliverRejectJobProcessor.kt | 6 ++- .../undo/APDeliverUndoJobProcessor.kt | 6 ++- .../service/activity/undo/APUndoProcessor.kt | 11 +++++ .../core/external/job/DeliverAcceptJob.kt | 3 +- .../core/external/job/DeliverRejectJob.kt | 2 +- .../core/external/job/DeliverUndoJob.kt | 2 +- .../hideout/core/external/job/HideoutJob.kt | 2 +- .../kjobexposed/KJobJobQueueWorkerService.kt | 3 ++ .../relationship/RelationshipService.kt | 9 ++++ .../relationship/RelationshipServiceImpl.kt | 18 ++++++-- src/main/resources/logback.xml | 4 +- .../RelationshipServiceImplTest.kt | 42 +++++++++---------- 14 files changed, 80 insertions(+), 38 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/APDeliverAcceptJobProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/APDeliverAcceptJobProcessor.kt index e9d240a9..343dcb50 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/APDeliverAcceptJobProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/APDeliverAcceptJobProcessor.kt @@ -1,6 +1,7 @@ package dev.usbharu.hideout.activitypub.service.activity.accept import dev.usbharu.hideout.activitypub.service.common.APRequestService +import dev.usbharu.hideout.application.external.Transaction import dev.usbharu.hideout.core.external.job.DeliverAcceptJob import dev.usbharu.hideout.core.external.job.DeliverAcceptJobParam import dev.usbharu.hideout.core.query.UserQueryService @@ -11,10 +12,11 @@ import org.springframework.stereotype.Service class APDeliverAcceptJobProcessor( private val apRequestService: APRequestService, private val userQueryService: UserQueryService, - private val deliverAcceptJob: DeliverAcceptJob + private val deliverAcceptJob: DeliverAcceptJob, + private val transaction: Transaction ) : JobProcessor { - override suspend fun process(param: DeliverAcceptJobParam) { + override suspend fun process(param: DeliverAcceptJobParam): Unit = transaction.transaction { apRequestService.apPost(param.inbox, param.accept, userQueryService.findById(param.signer)) } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApSendAcceptService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApSendAcceptService.kt index 573e7104..ab364c06 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApSendAcceptService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApSendAcceptService.kt @@ -21,8 +21,8 @@ class ApSendAcceptServiceImpl( val deliverAcceptJobParam = DeliverAcceptJobParam( Accept( apObject = Follow( - apObject = target.url, - actor = user.url + apObject = user.url, + actor = target.url ), actor = user.url ), diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/APDeliverRejectJobProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/APDeliverRejectJobProcessor.kt index 8e271288..575a34de 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/APDeliverRejectJobProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/APDeliverRejectJobProcessor.kt @@ -1,6 +1,7 @@ package dev.usbharu.hideout.activitypub.service.activity.reject import dev.usbharu.hideout.activitypub.service.common.APRequestService +import dev.usbharu.hideout.application.external.Transaction import dev.usbharu.hideout.core.external.job.DeliverRejectJob import dev.usbharu.hideout.core.external.job.DeliverRejectJobParam import dev.usbharu.hideout.core.query.UserQueryService @@ -11,10 +12,11 @@ import org.springframework.stereotype.Component class APDeliverRejectJobProcessor( private val apRequestService: APRequestService, private val userQueryService: UserQueryService, - private val deliverRejectJob: DeliverRejectJob + private val deliverRejectJob: DeliverRejectJob, + private val transaction: Transaction ) : JobProcessor { - override suspend fun process(param: DeliverRejectJobParam) { + override suspend fun process(param: DeliverRejectJobParam): Unit = transaction.transaction { apRequestService.apPost(param.inbox, param.reject, userQueryService.findById(param.signer)) } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APDeliverUndoJobProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APDeliverUndoJobProcessor.kt index 800f6ca5..d5f1fec2 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APDeliverUndoJobProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APDeliverUndoJobProcessor.kt @@ -1,6 +1,7 @@ package dev.usbharu.hideout.activitypub.service.activity.undo import dev.usbharu.hideout.activitypub.service.common.APRequestService +import dev.usbharu.hideout.application.external.Transaction import dev.usbharu.hideout.core.external.job.DeliverUndoJob import dev.usbharu.hideout.core.external.job.DeliverUndoJobParam import dev.usbharu.hideout.core.query.UserQueryService @@ -11,9 +12,10 @@ import org.springframework.stereotype.Service class APDeliverUndoJobProcessor( private val deliverUndoJob: DeliverUndoJob, private val apRequestService: APRequestService, - private val userQueryService: UserQueryService + private val userQueryService: UserQueryService, + private val transaction: Transaction ) : JobProcessor { - override suspend fun process(param: DeliverUndoJobParam) { + override suspend fun process(param: DeliverUndoJobParam): Unit = transaction.transaction { apRequestService.apPost(param.inbox, param.undo, userQueryService.findById(param.signer)) } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt index f6a3ff0e..7e5b8583 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt @@ -1,5 +1,6 @@ package dev.usbharu.hideout.activitypub.service.activity.undo +import dev.usbharu.hideout.activitypub.domain.model.Block import dev.usbharu.hideout.activitypub.domain.model.Follow import dev.usbharu.hideout.activitypub.domain.model.Undo import dev.usbharu.hideout.activitypub.service.common.AbstractActivityPubProcessor @@ -44,6 +45,16 @@ class APUndoProcessor( return } + "Block" -> { + val block = undo.apObject as Block + + val blocker = apUserService.fetchPersonWithEntity(undo.actor, block.apObject).second + val target = userQueryService.findByUrl(block.apObject) + + relationshipService.unblock(blocker.id, target.id) + return + } + else -> {} } TODO() diff --git a/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverAcceptJob.kt b/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverAcceptJob.kt index ad002175..0de9fafb 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverAcceptJob.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverAcceptJob.kt @@ -14,7 +14,8 @@ data class DeliverAcceptJobParam( ) @Component -class DeliverAcceptJob(private val objectMapper: ObjectMapper) : HideoutJob() { +class DeliverAcceptJob(private val objectMapper: ObjectMapper) : + HideoutJob("DeliverAcceptJob") { val accept = string("accept") val inbox = string("inbox") diff --git a/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverRejectJob.kt b/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverRejectJob.kt index 9f923534..c52dc5ab 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverRejectJob.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverRejectJob.kt @@ -16,7 +16,7 @@ data class DeliverRejectJobParam( @Component class DeliverRejectJob(@Qualifier("activitypub") private val objectMapper: ObjectMapper) : - HideoutJob() { + HideoutJob("DeliverRejectJob") { val reject = string("reject") val inbox = string("inbox") val signer = long("signer") diff --git a/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverUndoJob.kt b/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverUndoJob.kt index b8ad9088..50efd716 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverUndoJob.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverUndoJob.kt @@ -16,7 +16,7 @@ data class DeliverUndoJobParam( @Component class DeliverUndoJob(@Qualifier("activitypub") private val objectMapper: ObjectMapper) : - HideoutJob() { + HideoutJob("DeliverUndoJob") { val undo = string("undo") val inbox = string("inbox") diff --git a/src/main/kotlin/dev/usbharu/hideout/core/external/job/HideoutJob.kt b/src/main/kotlin/dev/usbharu/hideout/core/external/job/HideoutJob.kt index d201fc9a..3efdf09d 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/external/job/HideoutJob.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/external/job/HideoutJob.kt @@ -7,7 +7,7 @@ import kjob.core.dsl.ScheduleContext import kjob.core.job.JobProps import org.springframework.stereotype.Component -abstract class HideoutJob>(name: String = "") : Job(name) { +abstract class HideoutJob>(name: String) : Job(name) { abstract fun convert(value: @UnsafeVariance T): ScheduleContext<@UnsafeVariance R>.(@UnsafeVariance R) -> Unit fun convertUnsafe(props: JobProps<*>): T = convert(props as JobProps) abstract fun convert(props: JobProps<@UnsafeVariance R>): T diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobexposed/KJobJobQueueWorkerService.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobexposed/KJobJobQueueWorkerService.kt index ff8e07e8..9bd19351 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobexposed/KJobJobQueueWorkerService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobexposed/KJobJobQueueWorkerService.kt @@ -40,6 +40,9 @@ class KJobJobQueueWorkerService(private val jobQueueProcessorList: List - %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level [%X{x-request-id}] %logger{36} - %msg%n + %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level [%X{x-request-id}] [%X{x-job-id}] %logger{36} - + %msg%n + diff --git a/src/test/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImplTest.kt index cda067c5..0389b895 100644 --- a/src/test/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImplTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImplTest.kt @@ -140,10 +140,10 @@ class RelationshipServiceImplTest { @Test fun `followRequest 既にフォローしている場合は念の為フォロー承認を自動で行う`() = runTest { - val localUser = UserBuilder.localUserOf(domain = "example.com") - whenever(userQueryService.findById(eq(1234))).doReturn(localUser) val remoteUser = UserBuilder.remoteUserOf(domain = "remote.example.com") - whenever(userQueryService.findById(eq(5678))).doReturn(remoteUser) + whenever(userQueryService.findById(eq(1234))).doReturn(remoteUser) + val localUser = UserBuilder.localUserOf(domain = "example.com") + whenever(userQueryService.findById(eq(5678))).doReturn(localUser) whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn( Relationship( userId = 1234, @@ -258,10 +258,10 @@ class RelationshipServiceImplTest { fun `acceptFollowRequest ローカルユーザーの場合永続化される`() = runTest { whenever(userQueryService.findById(eq(5678))).doReturn(UserBuilder.localUserOf(domain = "example.com")) - whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn( + whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(5678), eq(1234))).doReturn( Relationship( - userId = 1234, - targetUserId = 5678, + userId = 5678, + targetUserId = 1234, following = false, blocking = false, muting = false, @@ -274,8 +274,8 @@ class RelationshipServiceImplTest { verify(relationshipRepository, times(1)).save( Relationship( - userId = 1234, - targetUserId = 5678, + userId = 5678, + targetUserId = 1234, following = true, blocking = false, muting = false, @@ -294,10 +294,10 @@ class RelationshipServiceImplTest { val remoteUser = UserBuilder.remoteUserOf(domain = "remote.example.com") whenever(userQueryService.findById(eq(5678))).doReturn(remoteUser) - whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn( + whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(5678), eq(1234))).doReturn( Relationship( - userId = 1234, - targetUserId = 5678, + userId = 5678, + targetUserId = 1234, following = false, blocking = false, muting = false, @@ -311,8 +311,8 @@ class RelationshipServiceImplTest { verify(relationshipRepository, times(1)).save( eq( Relationship( - userId = 1234, - targetUserId = 5678, + userId = 5678, + targetUserId = 1234, following = true, blocking = false, muting = false, @@ -334,9 +334,9 @@ class RelationshipServiceImplTest { @Test fun `acceptFollowRequest フォローリクエストが存在せずforceがfalseのとき何もしない`() = runTest { - whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn( + whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(5678), eq(1234))).doReturn( Relationship( - 1234, 5678, false, false, false, false, false + 5678, 1234, false, false, false, false, false ) ) @@ -349,9 +349,9 @@ class RelationshipServiceImplTest { fun `acceptFollowRequest フォローリクエストが存在せずforceがtrueのときフォローを承認する`() = runTest { whenever(userQueryService.findById(eq(5678))).doReturn(UserBuilder.remoteUserOf(domain = "remote.example.com")) - whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn( + whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(5678), eq(1234))).doReturn( Relationship( - 1234, 5678, false, false, false, false, false + 5678, 1234, false, false, false, false, false ) ) @@ -360,8 +360,8 @@ class RelationshipServiceImplTest { verify(relationshipRepository, times(1)).save( eq( Relationship( - userId = 1234, - targetUserId = 5678, + userId = 5678, + targetUserId = 1234, following = true, blocking = false, muting = false, @@ -374,9 +374,9 @@ class RelationshipServiceImplTest { @Test fun `acceptFollowRequest ブロックしている場合は何もしない`() = runTest { - whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn( + whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(5678), eq(1234))).doReturn( Relationship( - 1234, 5678, false, true, false, true, false + 5678, 1234, false, true, false, true, false ) ) From d952aac688aed6a615b012d59f90760474c1ee8e Mon Sep 17 00:00:00 2001 From: usbharu Date: Mon, 11 Dec 2023 01:34:21 +0900 Subject: [PATCH 33/43] Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .../hideout/activitypub/domain/model/Accept.kt | 8 ++++---- .../hideout/activitypub/domain/model/Block.kt | 12 +++++------- .../hideout/activitypub/domain/model/Create.kt | 16 ++++++++-------- .../hideout/activitypub/domain/model/Reject.kt | 12 +++++------- .../activity/accept/ApSendAcceptService.kt | 1 - .../activity/block/APDeliverBlockJobProcessor.kt | 1 - .../activity/block/BlockActivityPubProcessor.kt | 1 - .../activity/reject/ApSendRejectServiceImpl.kt | 1 - .../relationship/RelationshipRepositoryImpl.kt | 1 - .../hideout/core/external/job/DeliverBlockJob.kt | 2 -- .../exposedquery/FollowerQueryServiceImpl.kt | 3 ++- .../service/relationship/RelationshipService.kt | 1 - .../relationship/RelationshipServiceImpl.kt | 11 +++++++---- 13 files changed, 31 insertions(+), 39 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Accept.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Accept.kt index 13f6a31d..67a8ed0f 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Accept.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Accept.kt @@ -38,9 +38,9 @@ open class Accept @JsonCreator constructor( override fun toString(): String { return "Accept(" + - "apObject=$apObject, " + - "actor='$actor'" + - ")" + - " ${super.toString()}" + "apObject=$apObject, " + + "actor='$actor'" + + ")" + + " ${super.toString()}" } } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Block.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Block.kt index b1bde6d5..88a7b319 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Block.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Block.kt @@ -34,12 +34,10 @@ open class Block( override fun toString(): String { return "Block(" + - "actor='$actor', " + - "id='$id', " + - "apObject='$apObject'" + - ")" + - " ${super.toString()}" + "actor='$actor', " + + "id='$id', " + + "apObject='$apObject'" + + ")" + + " ${super.toString()}" } - - } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Create.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Create.kt index 3a27e800..2cbc9a3c 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Create.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Create.kt @@ -51,13 +51,13 @@ open class Create( override fun toString(): String { return "Create(" + - "name=$name, " + - "apObject=$apObject, " + - "actor='$actor', " + - "id='$id', " + - "to=$to, " + - "cc=$cc" + - ")" + - " ${super.toString()}" + "name=$name, " + + "apObject=$apObject, " + + "actor='$actor', " + + "id='$id', " + + "to=$to, " + + "cc=$cc" + + ")" + + " ${super.toString()}" } } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Reject.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Reject.kt index fd0e980e..82cb5b8a 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Reject.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Reject.kt @@ -34,12 +34,10 @@ open class Reject( override fun toString(): String { return "Reject(" + - "actor='$actor', " + - "id='$id', " + - "apObject=$apObject" + - ")" + - " ${super.toString()}" + "actor='$actor', " + + "id='$id', " + + "apObject=$apObject" + + ")" + + " ${super.toString()}" } - - } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApSendAcceptService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApSendAcceptService.kt index ab364c06..35270235 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApSendAcceptService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApSendAcceptService.kt @@ -32,5 +32,4 @@ class ApSendAcceptServiceImpl( jobQueueParentService.scheduleTypeSafe(deliverAcceptJob, deliverAcceptJobParam) } - } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/block/APDeliverBlockJobProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/block/APDeliverBlockJobProcessor.kt index 37236c0f..a97a0f19 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/block/APDeliverBlockJobProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/block/APDeliverBlockJobProcessor.kt @@ -19,7 +19,6 @@ class APDeliverBlockJobProcessor( private val deliverBlockJob: DeliverBlockJob ) : JobProcessor { override suspend fun process(param: DeliverBlockJobParam): Unit = transaction.transaction { - val signer = userRepository.findById(param.signer) apRequestService.apPost( param.inbox, diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/block/BlockActivityPubProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/block/BlockActivityPubProcessor.kt index fa67be3d..075bf893 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/block/BlockActivityPubProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/block/BlockActivityPubProcessor.kt @@ -9,7 +9,6 @@ import dev.usbharu.hideout.core.query.UserQueryService import dev.usbharu.hideout.core.service.relationship.RelationshipService import org.springframework.stereotype.Service - /** * ブロックアクティビティを処理します */ diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/ApSendRejectServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/ApSendRejectServiceImpl.kt index ded3c2a5..fb7610bb 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/ApSendRejectServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/ApSendRejectServiceImpl.kt @@ -16,7 +16,6 @@ class ApSendRejectServiceImpl( private val deliverRejectJob: DeliverRejectJob ) : ApSendRejectService { override suspend fun sendRejectFollow(user: User, target: User) { - val deliverRejectJobParam = DeliverRejectJobParam( Reject( user.url, diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepositoryImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepositoryImpl.kt index bb0513e6..8507cbc1 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepositoryImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepositoryImpl.kt @@ -57,7 +57,6 @@ class RelationshipRepositoryImpl : RelationshipRepository { }.singleOrNull() ?.toRelationships() } - } fun ResultRow.toRelationships(): Relationship = Relationship( diff --git a/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverBlockJob.kt b/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverBlockJob.kt index 3e8688b9..988b5b1a 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverBlockJob.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverBlockJob.kt @@ -49,6 +49,4 @@ class DeliverBlockJob(@Qualifier("activitypub") private val objectMapper: Object objectMapper.readValue(props[reject]), props[inbox] ) - - } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/FollowerQueryServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/FollowerQueryServiceImpl.kt index f8c6bb6e..df5acb77 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/FollowerQueryServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/FollowerQueryServiceImpl.kt @@ -15,7 +15,8 @@ class FollowerQueryServiceImpl( ) : FollowerQueryService { override suspend fun findFollowersById(id: Long): List { return userQueryService.findByIds( - relationshipQueryService.findByTargetIdAndFollowing(id, true).map { it.userId }) + relationshipQueryService.findByTargetIdAndFollowing(id, true).map { it.userId } + ) } override suspend fun alreadyFollow(userId: Long, followerId: Long): Boolean = diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipService.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipService.kt index 64dcb536..9032bd2e 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipService.kt @@ -19,5 +19,4 @@ interface RelationshipService { suspend fun unblock(userId: Long, targetId: Long) suspend fun mute(userId: Long, targetId: Long) suspend fun unmute(userId: Long, targetId: Long) - } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImpl.kt index 39987141..bea66075 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImpl.kt @@ -79,7 +79,7 @@ class RelationshipServiceImpl( val user = userQueryService.findById(userId) apSendFollowService.sendFollow(SendFollowDto(user, remoteUser)) } else { - //TODO: フォロー許可制ユーザーを実装したら消す + // TODO: フォロー許可制ユーザーを実装したら消す acceptFollowRequest(targetId, userId) } @@ -135,12 +135,16 @@ class RelationshipServiceImpl( if (relationship.blocking) { logger.warn("FAILED Blocking user userId: {} targetId: {}", userId, targetId) - throw IllegalStateException("Cannot accept a follow request from a blocked user. userId: $userId targetId: $targetId") + throw IllegalStateException( + "Cannot accept a follow request from a blocked user. userId: $userId targetId: $targetId" + ) } if (inverseRelationship.blocking) { logger.warn("FAILED BLocked by user userId: {} targetId: {}", userId, targetId) - throw IllegalStateException("Cannot accept a follow request from a blocking user. userId: $userId targetId: $targetId") + throw IllegalStateException( + "Cannot accept a follow request from a blocking user. userId: $userId targetId: $targetId" + ) } val copy = relationship.copy(followRequest = false, following = true, blocking = false) @@ -237,7 +241,6 @@ class RelationshipServiceImpl( val copy = relationship.copy(blocking = false) relationshipRepository.save(copy) - val remoteUser = isRemoteUser(targetId) if (remoteUser != null) { val user = userQueryService.findById(userId) From 9c8f90bb05d8227ad2064a39e7e28a8aa43f7ef7 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Mon, 11 Dec 2023 01:47:23 +0900 Subject: [PATCH 34/43] =?UTF-8?q?refactor:=20=E4=B8=8D=E8=A6=81=E3=81=AA?= =?UTF-8?q?=E4=BE=9D=E5=AD=98=E3=82=92=E5=89=8A=E9=99=A4=20fix=20lint?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/activity/undo/APUndoProcessor.kt | 7 +----- .../RelationshipRepositoryImpl.kt | 14 +++++------ .../kjobexposed/KJobJobQueueWorkerService.kt | 1 + .../media/LocalFileSystemMediaDataStore.kt | 1 + .../core/service/user/UserServiceImpl.kt | 4 ---- .../account/MastodonAccountApiController.kt | 22 +++++++++--------- .../service/account/AccountApiService.kt | 23 ++++++++++--------- .../mastodon/service/app/AppApiService.kt | 12 +++++----- .../core/service/user/UserServiceTest.kt | 4 +--- 9 files changed, 40 insertions(+), 48 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt index 7e5b8583..21734428 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt @@ -10,7 +10,6 @@ import dev.usbharu.hideout.activitypub.service.objects.user.APUserService import dev.usbharu.hideout.application.external.Transaction import dev.usbharu.hideout.core.query.UserQueryService import dev.usbharu.hideout.core.service.relationship.RelationshipService -import dev.usbharu.hideout.core.service.user.UserService import org.springframework.stereotype.Service @Service @@ -18,18 +17,14 @@ class APUndoProcessor( transaction: Transaction, private val apUserService: APUserService, private val userQueryService: UserQueryService, - private val userService: UserService, private val relationshipService: RelationshipService ) : AbstractActivityPubProcessor(transaction) { override suspend fun internalProcess(activity: ActivityPubProcessContext) { val undo = activity.activity - if (undo.actor == null) { - return - } val type = - undo.apObject.type.orEmpty() + undo.apObject.type .firstOrNull { it == "Block" || it == "Follow" || it == "Like" || it == "Announce" || it == "Accept" } ?: return diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepositoryImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepositoryImpl.kt index 8507cbc1..a8bfd6de 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepositoryImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepositoryImpl.kt @@ -60,13 +60,13 @@ class RelationshipRepositoryImpl : RelationshipRepository { } fun ResultRow.toRelationships(): Relationship = Relationship( - this[Relationships.userId], - this[Relationships.targetUserId], - this[Relationships.following], - this[Relationships.blocking], - this[Relationships.muting], - this[Relationships.followRequest], - this[Relationships.ignoreFollowRequestFromTarget] + userId = this[Relationships.userId], + targetUserId = this[Relationships.targetUserId], + following = this[Relationships.following], + blocking = this[Relationships.blocking], + muting = this[Relationships.muting], + followRequest = this[Relationships.followRequest], + ignoreFollowRequestFromTarget = this[Relationships.ignoreFollowRequestFromTarget] ) object Relationships : LongIdTable("relationships") { diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobexposed/KJobJobQueueWorkerService.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobexposed/KJobJobQueueWorkerService.kt index 9bd19351..28b437c6 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobexposed/KJobJobQueueWorkerService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/kjobexposed/KJobJobQueueWorkerService.kt @@ -36,6 +36,7 @@ class KJobJobQueueWorkerService(private val jobQueueProcessorList: List("uid").toLong() val statusFlow = accountApiService.accountsStatuses( - id.toLong(), - maxId?.toLongOrNull(), - sinceId?.toLongOrNull(), - minId?.toLongOrNull(), - limit, - onlyMedia, - excludeReplies, - excludeReblogs, - pinned, - tagged, - userid + userid = id.toLong(), + maxId = maxId?.toLongOrNull(), + sinceId = sinceId?.toLongOrNull(), + minId = minId?.toLongOrNull(), + limit = limit, + onlyMedia = onlyMedia, + excludeReplies = excludeReplies, + excludeReblogs = excludeReblogs, + pinned = pinned, + tagged = tagged, + loginUser = userid ).asFlow() ResponseEntity.ok(statusFlow) } diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt index 6a3d47f9..3a0f22bc 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt @@ -13,6 +13,7 @@ import kotlin.math.min @Service interface AccountApiService { + @Suppress("LongParameterList") suspend fun accountsStatuses( userid: Long, maxId: Long?, @@ -76,17 +77,17 @@ class AccountApiServiceImpl( return transaction.transaction { statusQueryService.accountsStatus( - userid, - maxId, - sinceId, - minId, - limit, - onlyMedia, - excludeReplies, - excludeReblogs, - pinned, - tagged, - canViewFollowers + accountId = userid, + maxId = maxId, + sinceId = sinceId, + minId = minId, + limit = limit, + onlyMedia = onlyMedia, + excludeReplies = excludeReplies, + excludeReblogs = excludeReblogs, + pinned = pinned, + tagged = tagged, + includeFollowers = canViewFollowers ) } } diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/app/AppApiService.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/app/AppApiService.kt index 7324dd33..c2139a27 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/app/AppApiService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/app/AppApiService.kt @@ -55,12 +55,12 @@ class AppApiServiceImpl( registeredClientRepository.save(registeredClient) Application( - appsRequest.clientName, - "invalid-vapid-key", - appsRequest.website, - id, - clientSecret, - appsRequest.redirectUris + name = appsRequest.clientName, + vapidKey = "invalid-vapid-key", + website = appsRequest.website, + clientId = id, + clientSecret = clientSecret, + redirectUri = appsRequest.redirectUris ) } } diff --git a/src/test/kotlin/dev/usbharu/hideout/core/service/user/UserServiceTest.kt b/src/test/kotlin/dev/usbharu/hideout/core/service/user/UserServiceTest.kt index 65a88e8b..c524a0f5 100644 --- a/src/test/kotlin/dev/usbharu/hideout/core/service/user/UserServiceTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/core/service/user/UserServiceTest.kt @@ -37,8 +37,6 @@ class UserServiceTest { userRepository, userAuthService, mock(), - mock(), - mock(), userBuilder, testApplicationConfig, mock() @@ -68,7 +66,7 @@ class UserServiceTest { onBlocking { nextId() } doReturn 113345L } val userService = - UserServiceImpl(userRepository, mock(), mock(), mock(), mock(), userBuilder, testApplicationConfig, mock()) + UserServiceImpl(userRepository, mock(), mock(), userBuilder, testApplicationConfig, mock()) val user = RemoteUserCreateDto( name = "test", domain = "remote.example.com", From d4c8592a1058e3cb10daf17398e97c0ce17cd35c Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Mon, 11 Dec 2023 11:28:43 +0900 Subject: [PATCH 35/43] =?UTF-8?q?feat:=20Mastodon=20API=E3=81=8B=E3=82=89?= =?UTF-8?q?=E3=83=95=E3=82=A9=E3=83=AD=E3=83=BC=E8=A7=A3=E9=99=A4=E3=83=BB?= =?UTF-8?q?=E3=83=96=E3=83=AD=E3=83=83=E3=82=AF=E8=A7=A3=E9=99=A4=E3=81=8C?= =?UTF-8?q?=E3=81=A7=E3=81=8D=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 --- .../application/config/SecurityConfig.kt | 2 + .../relationship/RelationshipServiceImpl.kt | 2 +- .../account/MastodonAccountApiController.kt | 20 +++++++++ .../service/account/AccountApiService.kt | 18 +++++++- src/main/resources/openapi/mastodon.yaml | 43 +++++++++++++++++++ 5 files changed, 82 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt b/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt index e42ad9b9..dadee6f4 100644 --- a/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt +++ b/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt @@ -197,7 +197,9 @@ class SecurityConfig { authorize(GET, "/api/v1/accounts/*", permitAll) authorize(GET, "/api/v1/accounts/*/statuses", permitAll) authorize(POST, "/api/v1/accounts/*/follow", hasAnyScope("write", "write:follows")) + authorize(POST, "/api/v1/accounts/*/unfollow", hasAnyScope("write", "write:follows")) authorize(POST, "/api/v1/accounts/*/block", hasAnyScope("write", "write:blocks")) + authorize(POST, "/api/v1/accounts/*/unblock", hasAnyScope("write", "write:blocks")) authorize(POST, "/api/v1/media", hasAnyScope("write", "write:media")) authorize(POST, "/api/v1/statuses", hasAnyScope("write", "write:statuses")) diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImpl.kt index bea66075..08e0ac9f 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImpl.kt @@ -167,7 +167,7 @@ class RelationshipServiceImpl( return } - if (relationship.followRequest.not()) { + if (relationship.followRequest.not() && relationship.following.not()) { logger.warn("FAILED Follow Request Not Found. (Follow Request) userId: {} targetId: {}", userId, targetId) return } diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt index e032ac01..8311596f 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt @@ -113,4 +113,24 @@ class MastodonAccountApiController( return ResponseEntity.ok(block) } + + override suspend fun apiV1AccountsIdUnblockPost(id: String): ResponseEntity { + val principal = SecurityContextHolder.getContext().getAuthentication().principal as Jwt + + val userid = principal.getClaim("uid").toLong() + + val unblock = accountApiService.unblock(userid, id.toLong()) + + return ResponseEntity.ok(unblock) + } + + override suspend fun apiV1AccountsIdUnfollowPost(id: String): ResponseEntity { + val principal = SecurityContextHolder.getContext().getAuthentication().principal as Jwt + + val userid = principal.getClaim("uid").toLong() + + val unfollow = accountApiService.unfollow(userid, id.toLong()) + + return ResponseEntity.ok(unfollow) + } } diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt index 3a0f22bc..955603cb 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt @@ -42,6 +42,8 @@ interface AccountApiService { * @return ブロック後のブロック対象ユーザーとの[Relationship] */ suspend fun block(userid: Long, target: Long): Relationship + suspend fun unblock(userid: Long, target: Long): Relationship + suspend fun unfollow(userid: Long, target: Long): Relationship } @Service @@ -101,10 +103,10 @@ class AccountApiServiceImpl( userService.createLocalUser(UserCreateDto(userCreateDto.name, userCreateDto.name, "", userCreateDto.password)) } - override suspend fun follow(loginUser: Long, followTargetUserId: Long): Relationship { + override suspend fun follow(loginUser: Long, followTargetUserId: Long): Relationship = transaction.transaction { relationshipService.followRequest(loginUser, followTargetUserId) - return fetchRelationship(loginUser, followTargetUserId) + return@transaction fetchRelationship(loginUser, followTargetUserId) } override suspend fun account(id: Long): Account = transaction.transaction { @@ -132,6 +134,18 @@ class AccountApiServiceImpl( fetchRelationship(userid, target) } + override suspend fun unblock(userid: Long, target: Long): Relationship = transaction.transaction { + relationshipService.unblock(userid, target) + + return@transaction fetchRelationship(userid, target) + } + + override suspend fun unfollow(userid: Long, target: Long): Relationship = transaction.transaction { + relationshipService.unfollow(userid, target) + + return@transaction fetchRelationship(userid, target) + } + private fun from(account: Account): CredentialAccount { return CredentialAccount( id = account.id, diff --git a/src/main/resources/openapi/mastodon.yaml b/src/main/resources/openapi/mastodon.yaml index 592726ae..fa3ea430 100644 --- a/src/main/resources/openapi/mastodon.yaml +++ b/src/main/resources/openapi/mastodon.yaml @@ -308,6 +308,49 @@ paths: schema: $ref: "#/components/schemas/Relationship" + /api/v1/accounts/{id}/unfollow: + post: + tags: + - account + security: + - OAuth2: + - "write:follows" + parameters: + - in: path + name: id + required: true + schema: + type: string + responses: + 200: + description: 成功 + content: + application/json: + schema: + $ref: "#/components/schemas/Relationship" + + /api/v1/accounts/{id}/unblock: + post: + tags: + - account + security: + - OAuth2: + - "write:blocks" + parameters: + - in: path + name: id + required: true + schema: + type: string + responses: + 200: + description: 成功 + content: + application/json: + schema: + $ref: "#/components/schemas/Relationship" + + /api/v1/accounts/{id}/statuses: get: tags: From ec57f145e4ee8d6e84874f0613a10514ae8c392c Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Mon, 11 Dec 2023 11:29:15 +0900 Subject: [PATCH 36/43] =?UTF-8?q?fix:=20=E3=83=96=E3=83=AD=E3=83=83?= =?UTF-8?q?=E3=82=AF=E3=81=A8=E3=83=95=E3=82=A9=E3=83=AD=E3=83=BC=E6=89=BF?= =?UTF-8?q?=E8=AA=8D=E3=81=8C=E5=87=BA=E6=9D=A5=E3=81=AA=E3=81=8B=E3=81=A3?= =?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 --- .../service/activity/accept/ApAcceptProcessor.kt | 4 ++-- .../hideout/core/external/job/DeliverBlockJob.kt | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessor.kt index b1838ed4..24a54ef5 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessor.kt @@ -35,8 +35,8 @@ class ApAcceptProcessor( val user = userQueryService.findByUrl(userUrl) val follower = userQueryService.findByUrl(followerUrl) - relationshipService.acceptFollowRequest(follower.id, user.id) - logger.debug("SUCCESS Follow from ${follower.url} to ${user.url}.") + relationshipService.acceptFollowRequest(user.id, follower.id) + logger.debug("SUCCESS Follow from ${user.url} to ${follower.url}.") } override fun isSupported(activityType: ActivityType): Boolean = activityType == ActivityType.Accept diff --git a/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverBlockJob.kt b/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverBlockJob.kt index 988b5b1a..482760e1 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverBlockJob.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverBlockJob.kt @@ -39,14 +39,14 @@ class DeliverBlockJob(@Qualifier("activitypub") private val objectMapper: Object override fun convert(value: DeliverBlockJobParam): ScheduleContext.(DeliverBlockJob) -> Unit = { props[block] = objectMapper.writeValueAsString(value.block) props[reject] = objectMapper.writeValueAsString(value.reject) - props[reject] = value.inbox + props[inbox] = value.inbox props[signer] = value.signer } override fun convert(props: JobProps): DeliverBlockJobParam = DeliverBlockJobParam( - props[signer], - objectMapper.readValue(props[block]), - objectMapper.readValue(props[reject]), - props[inbox] + signer = props[signer], + block = objectMapper.readValue(props[block]), + reject = objectMapper.readValue(props[reject]), + inbox = props[inbox] ) } From 04a0975f272ba0ae92707e39f749afc2ee06830e Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Mon, 11 Dec 2023 11:41:56 +0900 Subject: [PATCH 37/43] =?UTF-8?q?fix:=20=E3=83=96=E3=83=AD=E3=83=83?= =?UTF-8?q?=E3=82=AF=E3=81=95=E3=82=8C=E3=81=9F=E3=81=A8=E3=81=8D=E3=83=96?= =?UTF-8?q?=E3=83=AD=E3=83=83=E3=82=AF=E3=81=95=E3=82=8C=E3=81=9Factor?= =?UTF-8?q?=E3=81=8B=E3=82=89=E3=83=96=E3=83=AD=E3=83=83=E3=82=AF=E3=81=97?= =?UTF-8?q?=E3=81=9Factor=E3=81=B8=E3=81=AE=E3=83=95=E3=82=A9=E3=83=AD?= =?UTF-8?q?=E3=83=BC=E3=82=92=E8=A7=A3=E9=99=A4=E3=81=99=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/model/relationship/RelationshipRepositoryImpl.kt | 4 ++-- .../core/service/relationship/RelationshipServiceImpl.kt | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepositoryImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepositoryImpl.kt index a8bfd6de..c7e6986d 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepositoryImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepositoryImpl.kt @@ -23,7 +23,7 @@ class RelationshipRepositoryImpl : RelationshipRepository { it[targetUserId] = relationship.targetUserId it[following] = relationship.following it[blocking] = relationship.blocking - it[muting] = relationship.blocking + it[muting] = relationship.muting it[followRequest] = relationship.followRequest it[ignoreFollowRequestFromTarget] = relationship.ignoreFollowRequestFromTarget } @@ -35,7 +35,7 @@ class RelationshipRepositoryImpl : RelationshipRepository { }) { it[following] = relationship.following it[blocking] = relationship.blocking - it[muting] = relationship.blocking + it[muting] = relationship.muting it[followRequest] = relationship.followRequest it[ignoreFollowRequestFromTarget] = relationship.ignoreFollowRequestFromTarget } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImpl.kt index 08e0ac9f..c056a381 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImpl.kt @@ -98,7 +98,13 @@ class RelationshipServiceImpl( ignoreFollowRequestFromTarget = false ) + val inverseRelationship = relationshipRepository.findByUserIdAndTargetUserId(targetId, userId) + ?.copy(followRequest = false, following = false) + relationshipRepository.save(relationship) + if (inverseRelationship != null) { + relationshipRepository.save(inverseRelationship) + } val remoteUser = isRemoteUser(targetId) From a2dc70fb418f8dc445a333d8f11383caedf7b4f1 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Mon, 11 Dec 2023 12:18:09 +0900 Subject: [PATCH 38/43] =?UTF-8?q?feat:=20=E3=83=95=E3=82=A9=E3=83=AD?= =?UTF-8?q?=E3=83=AF=E3=83=BC=E3=82=92=E8=A7=A3=E9=99=A4=E3=81=AB=E5=AF=BE?= =?UTF-8?q?=E5=BF=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../activity/reject/ApRejectProcessor.kt | 51 +++++++++++++++++++ .../relationship/RelationshipServiceImpl.kt | 2 +- .../account/MastodonAccountApiController.kt | 10 ++++ .../service/account/AccountApiService.kt | 7 +++ src/main/resources/openapi/mastodon.yaml | 21 +++++++- 5 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/ApRejectProcessor.kt diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/ApRejectProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/ApRejectProcessor.kt new file mode 100644 index 00000000..53e4c4bb --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/ApRejectProcessor.kt @@ -0,0 +1,51 @@ +package dev.usbharu.hideout.activitypub.service.activity.reject + +import dev.usbharu.hideout.activitypub.domain.model.Follow +import dev.usbharu.hideout.activitypub.domain.model.Reject +import dev.usbharu.hideout.activitypub.service.common.AbstractActivityPubProcessor +import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessContext +import dev.usbharu.hideout.activitypub.service.common.ActivityType +import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.query.UserQueryService +import dev.usbharu.hideout.core.service.relationship.RelationshipService +import org.springframework.stereotype.Service + + +@Service +class ApRejectProcessor( + private val relationshipService: RelationshipService, + private val userQueryService: UserQueryService, + transaction: Transaction +) : + AbstractActivityPubProcessor(transaction) { + override suspend fun internalProcess(activity: ActivityPubProcessContext) { + + val activityType = activity.activity.apObject.type.firstOrNull { it == "Follow" } + + if (activityType == null) { + logger.warn("FAILED Process Reject Activity type: {}", activity.activity.apObject.type) + return + } + when (activityType) { + "Follow" -> { + val user = userQueryService.findByUrl(activity.activity.actor) + + activity.activity.apObject as Follow + + val actor = activity.activity.apObject.actor + + val target = userQueryService.findByUrl(actor) + + logger.debug("REJECT Follow user {} target {}", user.url, target.url) + + relationshipService.rejectFollowRequest(user.id, target.id) + } + + else -> {} + } + } + + override fun isSupported(activityType: ActivityType): Boolean = activityType == ActivityType.Reject + + override fun type(): Class = Reject::class.java +} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImpl.kt index c056a381..a94332f9 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImpl.kt @@ -166,7 +166,7 @@ class RelationshipServiceImpl( } override suspend fun rejectFollowRequest(userId: Long, targetId: Long) { - val relationship = relationshipRepository.findByUserIdAndTargetUserId(userId, targetId) + val relationship = relationshipRepository.findByUserIdAndTargetUserId(targetId, userId) if (relationship == null) { logger.warn("FAILED Follow Request Not Found. (Relationship) userId: {} targetId: {}", userId, targetId) diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt index 8311596f..f9af1c7d 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt @@ -133,4 +133,14 @@ class MastodonAccountApiController( return ResponseEntity.ok(unfollow) } + + override suspend fun apiV1AccountsIdRemoveFromFollowersPost(id: String): ResponseEntity { + val principal = SecurityContextHolder.getContext().getAuthentication().principal as Jwt + + val userid = principal.getClaim("uid").toLong() + + val removeFromFollowers = accountApiService.removeFromFollowers(userid, id.toLong()) + + return ResponseEntity.ok(removeFromFollowers) + } } diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt index 955603cb..f639afc8 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt @@ -44,6 +44,7 @@ interface AccountApiService { suspend fun block(userid: Long, target: Long): Relationship suspend fun unblock(userid: Long, target: Long): Relationship suspend fun unfollow(userid: Long, target: Long): Relationship + suspend fun removeFromFollowers(userid: Long, target: Long): Relationship } @Service @@ -146,6 +147,12 @@ class AccountApiServiceImpl( return@transaction fetchRelationship(userid, target) } + override suspend fun removeFromFollowers(userid: Long, target: Long): Relationship = transaction.transaction { + relationshipService.rejectFollowRequest(userid, target) + + return@transaction fetchRelationship(userid, target) + } + private fun from(account: Account): CredentialAccount { return CredentialAccount( id = account.id, diff --git a/src/main/resources/openapi/mastodon.yaml b/src/main/resources/openapi/mastodon.yaml index fa3ea430..a43da7de 100644 --- a/src/main/resources/openapi/mastodon.yaml +++ b/src/main/resources/openapi/mastodon.yaml @@ -349,7 +349,26 @@ paths: application/json: schema: $ref: "#/components/schemas/Relationship" - + /api/v1/accounts/{id}/remove_from_followers: + post: + tags: + - account + security: + - OAuth2: + - "write:follows" + parameters: + - in: path + name: id + required: true + schema: + type: string + responses: + 200: + description: 成功 + content: + application/json: + schema: + $ref: "#/components/schemas/Relationship" /api/v1/accounts/{id}/statuses: get: From 79a6bd20a4f85957d6e7fa5ab7981e9202b985ad Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Mon, 11 Dec 2023 12:29:08 +0900 Subject: [PATCH 39/43] =?UTF-8?q?feat:=20=E3=83=95=E3=82=A9=E3=83=AD?= =?UTF-8?q?=E3=83=BC=E6=89=BF=E8=AA=8D=E3=82=92=E5=8F=96=E3=82=8A=E6=B6=88?= =?UTF-8?q?=E3=81=97=E3=81=AE=E6=96=B9=E6=B3=95=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/activity/undo/APUndoProcessor.kt | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt index 21734428..010766cb 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt @@ -1,8 +1,10 @@ package dev.usbharu.hideout.activitypub.service.activity.undo +import dev.usbharu.hideout.activitypub.domain.model.Accept import dev.usbharu.hideout.activitypub.domain.model.Block import dev.usbharu.hideout.activitypub.domain.model.Follow import dev.usbharu.hideout.activitypub.domain.model.Undo +import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectValue import dev.usbharu.hideout.activitypub.service.common.AbstractActivityPubProcessor import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessContext import dev.usbharu.hideout.activitypub.service.common.ActivityType @@ -50,6 +52,25 @@ class APUndoProcessor( return } + "Accept" -> { + val accept = undo.apObject as Accept + + + val acceptObject = if (accept.apObject is ObjectValue) { + accept.apObject.`object` + } else if (accept.apObject is Follow) { + accept.apObject.apObject + } else { + logger.warn("FAILED Unsupported type. Undo Accept {}", accept.apObject.type) + return + } + + val accepter = apUserService.fetchPersonWithEntity(undo.actor, acceptObject).second + val target = userQueryService.findByUrl(acceptObject) + + relationshipService.rejectFollowRequest(accepter.id, target.id) + } + else -> {} } TODO() From ef653be4f8bd788bf7736c6a229598d1847d7637 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Mon, 11 Dec 2023 12:43:27 +0900 Subject: [PATCH 40/43] =?UTF-8?q?test:=20rejectFollowRequest=E3=81=AE?= =?UTF-8?q?=E3=83=86=E3=82=B9=E3=83=88=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RelationshipServiceImplTest.kt | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/test/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImplTest.kt index 0389b895..be2fca26 100644 --- a/src/test/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImplTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImplTest.kt @@ -412,10 +412,10 @@ class RelationshipServiceImplTest { fun `rejectFollowRequest ローカルユーザーの場合永続化される`() = runTest { whenever(userQueryService.findById(eq(5678))).doReturn(UserBuilder.localUserOf(domain = "example.com")) - whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn( + whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(5678), eq(1234))).doReturn( Relationship( - userId = 1234, - targetUserId = 5678, + userId = 5678, + targetUserId = 1234, following = false, blocking = false, muting = false, @@ -429,8 +429,8 @@ class RelationshipServiceImplTest { verify(relationshipRepository, times(1)).save( eq( Relationship( - userId = 1234, - targetUserId = 5678, + userId = 5678, + targetUserId = 1234, following = false, blocking = false, muting = false, @@ -451,10 +451,10 @@ class RelationshipServiceImplTest { val remoteUser = UserBuilder.remoteUserOf(domain = "remote.example.com") whenever(userQueryService.findById(eq(5678))).doReturn(remoteUser) - whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn( + whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(5678), eq(1234))).doReturn( Relationship( - userId = 1234, - targetUserId = 5678, + userId = 5678, + targetUserId = 1234, following = false, blocking = false, muting = false, @@ -468,8 +468,8 @@ class RelationshipServiceImplTest { verify(relationshipRepository, times(1)).save( eq( Relationship( - userId = 1234, - targetUserId = 5678, + userId = 5678, + targetUserId = 1234, following = false, blocking = false, muting = false, @@ -492,10 +492,10 @@ class RelationshipServiceImplTest { @Test fun `rejectFollowRequest フォローリクエストが存在しない場合何もしない`() = runTest { - whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn( + whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(5678), eq(1234))).doReturn( Relationship( - userId = 1234, - targetUserId = 5678, + userId = 5678, + targetUserId = 1234, following = false, blocking = false, muting = false, From b623a03ad96910e5f6829225f3ea95a90ffebcf2 Mon Sep 17 00:00:00 2001 From: usbharu Date: Mon, 11 Dec 2023 13:22:43 +0900 Subject: [PATCH 41/43] Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .../activitypub/service/activity/reject/ApRejectProcessor.kt | 2 -- .../activitypub/service/activity/undo/APUndoProcessor.kt | 1 - 2 files changed, 3 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/ApRejectProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/ApRejectProcessor.kt index 53e4c4bb..d86a583a 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/ApRejectProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/ApRejectProcessor.kt @@ -10,7 +10,6 @@ import dev.usbharu.hideout.core.query.UserQueryService import dev.usbharu.hideout.core.service.relationship.RelationshipService import org.springframework.stereotype.Service - @Service class ApRejectProcessor( private val relationshipService: RelationshipService, @@ -19,7 +18,6 @@ class ApRejectProcessor( ) : AbstractActivityPubProcessor(transaction) { override suspend fun internalProcess(activity: ActivityPubProcessContext) { - val activityType = activity.activity.apObject.type.firstOrNull { it == "Follow" } if (activityType == null) { diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt index 010766cb..f476ce36 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt @@ -55,7 +55,6 @@ class APUndoProcessor( "Accept" -> { val accept = undo.apObject as Accept - val acceptObject = if (accept.apObject is ObjectValue) { accept.apObject.`object` } else if (accept.apObject is Follow) { From 87a11c5d187390a4e21a55b9c0534f1859d783bf Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Mon, 11 Dec 2023 14:17:12 +0900 Subject: [PATCH 42/43] =?UTF-8?q?test:=20APAcceptProcessor=E3=81=A8APDeliv?= =?UTF-8?q?erAcceptJobProcessor=E3=81=AE=E3=83=86=E3=82=B9=E3=83=88?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../accept/APDeliverAcceptJobProcessorTest.kt | 70 ++++++++++ .../activity/accept/ApAcceptProcessorTest.kt | 130 ++++++++++++++++++ 2 files changed, 200 insertions(+) create mode 100644 src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/APDeliverAcceptJobProcessorTest.kt create mode 100644 src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessorTest.kt diff --git a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/APDeliverAcceptJobProcessorTest.kt b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/APDeliverAcceptJobProcessorTest.kt new file mode 100644 index 00000000..4eeded48 --- /dev/null +++ b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/APDeliverAcceptJobProcessorTest.kt @@ -0,0 +1,70 @@ +package dev.usbharu.hideout.activitypub.service.activity.accept + +import dev.usbharu.hideout.activitypub.domain.model.Accept +import dev.usbharu.hideout.activitypub.domain.model.Follow +import dev.usbharu.hideout.activitypub.service.common.APRequestService +import dev.usbharu.hideout.core.external.job.DeliverAcceptJob +import dev.usbharu.hideout.core.external.job.DeliverAcceptJobParam +import dev.usbharu.hideout.core.query.UserQueryService +import kotlinx.coroutines.test.runTest +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.mockito.InjectMocks +import org.mockito.Mock +import org.mockito.Spy +import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.kotlin.* +import utils.TestTransaction +import utils.UserBuilder + +@ExtendWith(MockitoExtension::class) +class APDeliverAcceptJobProcessorTest { + + @Mock + private lateinit var apRequestService: APRequestService + + @Mock + private lateinit var userQueryService: UserQueryService + + @Mock + private lateinit var deliverAcceptJob: DeliverAcceptJob + + @Spy + private val transaction = TestTransaction + + @InjectMocks + private lateinit var apDeliverAcceptJobProcessor: APDeliverAcceptJobProcessor + + @Test + fun `process apPostが発行される`() = runTest { + val user = UserBuilder.localUserOf() + + whenever(userQueryService.findById(eq(1))).doReturn(user) + + val accept = Accept( + apObject = Follow( + apObject = "https://example.com", + actor = "https://remote.example.com" + ), + actor = "https://example.com" + ) + val param = DeliverAcceptJobParam( + accept = accept, + "https://remote.example.com", + 1 + ) + + apDeliverAcceptJobProcessor.process(param) + + verify(apRequestService, times(1)).apPost(eq("https://remote.example.com"), eq(accept), eq(user)) + } + + @Test + fun `job DeliverAcceptJobが返ってくる`() { + val actual = apDeliverAcceptJobProcessor.job() + + assertThat(actual).isEqualTo(deliverAcceptJob) + + } +} diff --git a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessorTest.kt b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessorTest.kt new file mode 100644 index 00000000..b9015e1d --- /dev/null +++ b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessorTest.kt @@ -0,0 +1,130 @@ +package dev.usbharu.hideout.activitypub.service.activity.accept + +import dev.usbharu.hideout.activitypub.domain.exception.IllegalActivityPubObjectException +import dev.usbharu.hideout.activitypub.domain.model.Accept +import dev.usbharu.hideout.activitypub.domain.model.Follow +import dev.usbharu.hideout.activitypub.domain.model.Like +import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessContext +import dev.usbharu.hideout.activitypub.service.common.ActivityType +import dev.usbharu.hideout.application.config.ActivityPubConfig +import dev.usbharu.hideout.core.query.UserQueryService +import dev.usbharu.hideout.core.service.relationship.RelationshipService +import dev.usbharu.httpsignature.common.HttpHeaders +import dev.usbharu.httpsignature.common.HttpMethod +import dev.usbharu.httpsignature.common.HttpRequest +import kotlinx.coroutines.test.runTest +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.DynamicTest +import org.junit.jupiter.api.DynamicTest.dynamicTest +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestFactory +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.api.extension.ExtendWith +import org.mockito.InjectMocks +import org.mockito.Mock +import org.mockito.Spy +import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.kotlin.* +import utils.TestTransaction +import utils.UserBuilder +import java.net.URL + + +@ExtendWith(MockitoExtension::class) +class ApAcceptProcessorTest { + + @Mock + private lateinit var userQueryService: UserQueryService + + @Mock + private lateinit var relationshipService: RelationshipService + + @Spy + private val transaction = TestTransaction + + @InjectMocks + private lateinit var apAcceptProcessor: ApAcceptProcessor + + @Test + fun `internalProcess objectがFollowの場合フォローを承認する`() = runTest { + + val json = """""" + val objectMapper = ActivityPubConfig().objectMapper() + val jsonNode = objectMapper.readTree(json) + + val accept = Accept( + apObject = Follow( + apObject = "https://example.com", + actor = "https://remote.example.com" + ), + actor = "https://example.com" + ) + val activity = ActivityPubProcessContext( + accept, jsonNode, HttpRequest( + URL("https://example.com"), + HttpHeaders(emptyMap()), HttpMethod.POST + ), null, true + ) + + val user = UserBuilder.localUserOf() + whenever(userQueryService.findByUrl(eq("https://example.com"))).doReturn(user) + val remoteUser = UserBuilder.remoteUserOf() + whenever(userQueryService.findByUrl(eq("https://remote.example.com"))).doReturn(remoteUser) + + apAcceptProcessor.internalProcess(activity) + + verify(relationshipService, times(1)).acceptFollowRequest(eq(user.id), eq(remoteUser.id), eq(false)) + } + + @Test + fun `internalProcess objectがFollow以外の場合IllegalActivityPubObjecExceptionが発生する`() = runTest { + val json = """""" + val objectMapper = ActivityPubConfig().objectMapper() + val jsonNode = objectMapper.readTree(json) + + val accept = Accept( + apObject = Like( + apObject = "https://example.com", + actor = "https://remote.example.com", + content = "", + id = "" + ), + actor = "https://example.com" + ) + val activity = ActivityPubProcessContext( + accept, jsonNode, HttpRequest( + URL("https://example.com"), + HttpHeaders(emptyMap()), HttpMethod.POST + ), null, true + ) + + assertThrows { + apAcceptProcessor.internalProcess(activity) + } + } + + @Test + fun `isSupproted Acceptにはtrue`() { + val actual = apAcceptProcessor.isSupported(ActivityType.Accept) + assertThat(actual).isTrue() + } + + @TestFactory + fun `isSupported Accept以外にはfalse`(): List { + return ActivityType + .values() + .filterNot { it == ActivityType.Accept } + .map { + dynamicTest("isSupported $it にはfalse") { + + val actual = apAcceptProcessor.isSupported(it) + assertThat(actual).isFalse() + } + } + } + + @Test + fun `type Acceptのclassjavaが返ってくる`() { + assertThat(apAcceptProcessor.type()).isEqualTo(Accept::class.java) + } +} From a458dbd432b22316e0043534e26a8bc2128ec186 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Mon, 11 Dec 2023 14:35:35 +0900 Subject: [PATCH 43/43] =?UTF-8?q?test:=20APDeliverBlockJobProcessor?= =?UTF-8?q?=E3=81=A8APSendAcceptServiceImpl=E3=81=AE=E3=83=86=E3=82=B9?= =?UTF-8?q?=E3=83=88=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../accept/ApSendAcceptServiceImplTest.kt | 45 +++++++++++ .../block/APDeliverBlockJobProcessorTest.kt | 78 +++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApSendAcceptServiceImplTest.kt create mode 100644 src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/block/APDeliverBlockJobProcessorTest.kt diff --git a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApSendAcceptServiceImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApSendAcceptServiceImplTest.kt new file mode 100644 index 00000000..3d06c4d3 --- /dev/null +++ b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApSendAcceptServiceImplTest.kt @@ -0,0 +1,45 @@ +package dev.usbharu.hideout.activitypub.service.activity.accept + +import dev.usbharu.hideout.activitypub.domain.model.Accept +import dev.usbharu.hideout.activitypub.domain.model.Follow +import dev.usbharu.hideout.core.external.job.DeliverAcceptJob +import dev.usbharu.hideout.core.external.job.DeliverAcceptJobParam +import dev.usbharu.hideout.core.service.job.JobQueueParentService +import kotlinx.coroutines.test.runTest +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.mockito.InjectMocks +import org.mockito.Mock +import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.kotlin.eq +import org.mockito.kotlin.times +import org.mockito.kotlin.verify +import utils.UserBuilder + +@ExtendWith(MockitoExtension::class) +class ApSendAcceptServiceImplTest { + + @Mock + private lateinit var jobQueueParentService: JobQueueParentService + + @Mock + private lateinit var deliverAcceptJob: DeliverAcceptJob + + @InjectMocks + private lateinit var apSendAcceptServiceImpl: ApSendAcceptServiceImpl + + @Test + fun `sendAccept DeliverAcceptJobが発行される`() = runTest { + val user = UserBuilder.localUserOf() + val remoteUser = UserBuilder.remoteUserOf() + + apSendAcceptServiceImpl.sendAcceptFollow(user, remoteUser) + + val deliverAcceptJobParam = DeliverAcceptJobParam( + Accept(apObject = Follow(apObject = user.url, actor = remoteUser.url), actor = user.url), + remoteUser.inbox, + user.id + ) + verify(jobQueueParentService, times(1)).scheduleTypeSafe(eq(deliverAcceptJob), eq(deliverAcceptJobParam)) + } +} diff --git a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/block/APDeliverBlockJobProcessorTest.kt b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/block/APDeliverBlockJobProcessorTest.kt new file mode 100644 index 00000000..55e52ff9 --- /dev/null +++ b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/block/APDeliverBlockJobProcessorTest.kt @@ -0,0 +1,78 @@ +package dev.usbharu.hideout.activitypub.service.activity.block + +import dev.usbharu.hideout.activitypub.domain.model.Block +import dev.usbharu.hideout.activitypub.domain.model.Follow +import dev.usbharu.hideout.activitypub.domain.model.Reject +import dev.usbharu.hideout.activitypub.service.common.APRequestService +import dev.usbharu.hideout.core.domain.model.user.UserRepository +import dev.usbharu.hideout.core.external.job.DeliverBlockJob +import dev.usbharu.hideout.core.external.job.DeliverBlockJobParam +import kotlinx.coroutines.test.runTest +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.mockito.InjectMocks +import org.mockito.Mock +import org.mockito.Spy +import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.kotlin.* +import utils.TestTransaction +import utils.UserBuilder + +@ExtendWith(MockitoExtension::class) +class APDeliverBlockJobProcessorTest { + + @Mock + private lateinit var apRequestService: APRequestService + + @Mock + private lateinit var userRepository: UserRepository + + @Spy + private val transaction = TestTransaction + + @Mock + private lateinit var deliverBlockJob: DeliverBlockJob + + @InjectMocks + private lateinit var apDeliverBlockJobProcessor: APDeliverBlockJobProcessor + + @Test + fun `process rejectとblockがapPostされる`() = runTest { + val user = UserBuilder.localUserOf() + whenever(userRepository.findById(eq(user.id))).doReturn(user) + + + val block = Block( + actor = user.url, + "https://example.com/block", + apObject = "https://remote.example.com" + ) + val reject = Reject( + actor = user.url, + "https://example.com/reject/follow", + apObject = Follow( + apObject = user.url, + actor = "https://remote.example.com" + ) + ) + val param = DeliverBlockJobParam( + user.id, + block, + reject, + "https://remote.example.com" + ) + + + apDeliverBlockJobProcessor.process(param) + + verify(apRequestService, times(1)).apPost(eq("https://remote.example.com"), eq(block), eq(user)) + verify(apRequestService, times(1)).apPost(eq("https://remote.example.com"), eq(reject), eq(user)) + } + + @Test + fun `job deliverBlockJobが返ってくる`() { + val actual = apDeliverBlockJobProcessor.job() + assertThat(actual).isEqualTo(deliverBlockJob) + } +}