diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/Constant.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/Constant.kt new file mode 100644 index 00000000..451e24d9 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/Constant.kt @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.activitypub.domain + +import dev.usbharu.hideout.activitypub.domain.model.StringOrObject + +object Constant { + val context = listOf( + StringOrObject("https://www.w3.org/ns/activitystreams"), + StringOrObject("https://w3id.org/security/v1"), + StringOrObject( + mapOf( + "manuallyApprovesFollowers" to "as:manuallyApprovesFollowers", + "sensitive" to "as:sensitive", + "Hashtag" to "as:Hashtag", + "quoteUrl" to "as:quoteUrl", + "toot" to "http://joinmastodon.org/ns#", + "Emoji" to "toot:Emoji", + "featured" to "toot:featured", + "discoverable" to "toot:discoverable", + "schema" to "http://schema.org#", + "PropertyValue" to "schema:PropertyValue", + "value" to "schema:value", + ) + ) + ) +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/JsonLd.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/JsonLd.kt index 0e1a3d79..88a8e48d 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/JsonLd.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/JsonLd.kt @@ -31,18 +31,18 @@ import com.fasterxml.jackson.databind.annotation.JsonSerialize @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) open class JsonLd { @JsonProperty("@context") - @JsonDeserialize(contentUsing = ContextDeserializer::class) + @JsonDeserialize(contentUsing = StringOrObjectDeserializer::class) @JsonSerialize(include = JsonSerialize.Inclusion.NON_EMPTY, using = ContextSerializer::class) @JsonInclude(JsonInclude.Include.NON_EMPTY) - var context: List = emptyList() + var context: List = emptyList() set(value) { - field = value.filterNotNull().filter { it.isNotBlank() } + field = value.filterNot { it.isEmpty() } } @JsonCreator - constructor(context: List?) { + constructor(context: List?) { if (context != null) { - this.context = context.filterNotNull().filter { it.isNotBlank() } + this.context = context.filterNotNull().filterNot { it.isEmpty() } } else { this.context = emptyList() } @@ -76,24 +76,24 @@ class ContextDeserializer : JsonDeserializer() { } } -class ContextSerializer : JsonSerializer>() { +class ContextSerializer : JsonSerializer>() { @Deprecated("Deprecated in Java") - override fun isEmpty(value: List?): Boolean = value.isNullOrEmpty() + override fun isEmpty(value: List?): Boolean = value.isNullOrEmpty() - override fun isEmpty(provider: SerializerProvider?, value: List?): Boolean = value.isNullOrEmpty() + override fun isEmpty(provider: SerializerProvider?, value: List?): Boolean = value.isNullOrEmpty() - override fun serialize(value: List?, gen: JsonGenerator?, serializers: SerializerProvider) { + override fun serialize(value: List?, gen: JsonGenerator?, serializers: SerializerProvider) { if (value.isNullOrEmpty()) { serializers.defaultSerializeNull(gen) return } if (value.size == 1) { - gen?.writeString(value[0]) + serializers.findValueSerializer(StringOrObject::class.java).serialize(value[0], gen, serializers) } else { gen?.writeStartArray() value.forEach { - gen?.writeString(it) + serializers.findValueSerializer(StringOrObject::class.java).serialize(it, gen, serializers) } gen?.writeEndArray() } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/StringOrObject.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/StringOrObject.kt new file mode 100644 index 00000000..b419bf79 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/StringOrObject.kt @@ -0,0 +1,82 @@ +package dev.usbharu.hideout.activitypub.domain.model + +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.core.type.TypeReference +import com.fasterxml.jackson.databind.* + +open class StringOrObject { + var contextString: String? = null + var contextObject: Map? = null + + @JsonCreator + protected constructor() + + constructor(string: String) : this() { + contextString = string + } + + constructor(contextObject: Map) : this() { + this.contextObject = contextObject + } + + fun isEmpty(): Boolean = contextString.isNullOrEmpty() and contextObject.isNullOrEmpty() + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as StringOrObject + + if (contextString != other.contextString) return false + if (contextObject != other.contextObject) return false + + return true + } + + override fun hashCode(): Int { + var result = contextString?.hashCode() ?: 0 + result = 31 * result + (contextObject?.hashCode() ?: 0) + return result + } + + override fun toString(): String { + return "StringOrObject(contextString=$contextString, contextObject=$contextObject)" + } + + +} + + +class StringOrObjectDeserializer : JsonDeserializer() { + override fun deserialize(p: JsonParser?, ctxt: DeserializationContext): StringOrObject { + val readTree: JsonNode = p?.codec?.readTree(p) ?: return StringOrObject("") + return if (readTree.isValueNode) { + StringOrObject(readTree.textValue()) + } else if (readTree.isObject) { + val map: Map = ctxt.readTreeAsValue( + readTree, + ctxt.typeFactory.constructType(object : TypeReference>() {}) + ) + StringOrObject(map) + } else { + StringOrObject("") + } + } + +} + +class StringORObjectSerializer : JsonSerializer() { + override fun serialize(value: StringOrObject?, gen: JsonGenerator?, serializers: SerializerProvider) { + if (value == null) { + serializers.defaultSerializeNull(gen) + return + } + if (value.contextString != null) { + gen?.writeString(value.contextString) + } else { + serializers.defaultSerializeValue(value.contextObject, gen) + } + } +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/actor/UserAPControllerImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/actor/UserAPControllerImpl.kt index c1e8a9b9..73b9b8a5 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/actor/UserAPControllerImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/actor/UserAPControllerImpl.kt @@ -16,6 +16,7 @@ package dev.usbharu.hideout.activitypub.interfaces.api.actor +import dev.usbharu.hideout.activitypub.domain.Constant import dev.usbharu.hideout.activitypub.domain.model.Person import dev.usbharu.hideout.activitypub.service.objects.user.APUserService import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException @@ -31,7 +32,7 @@ class UserAPControllerImpl(private val apUserService: APUserService) : UserAPCon } catch (_: UserNotFoundException) { return ResponseEntity.notFound().build() } - person.context += listOf("https://www.w3.org/ns/activitystreams") + person.context += Constant.context return ResponseEntity(person, HttpStatus.OK) } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APRequestServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APRequestServiceImpl.kt index 9f19ebde..4b463532 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APRequestServiceImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APRequestServiceImpl.kt @@ -17,6 +17,8 @@ package dev.usbharu.hideout.activitypub.service.common import com.fasterxml.jackson.databind.ObjectMapper +import dev.usbharu.hideout.activitypub.domain.Constant +import dev.usbharu.hideout.activitypub.domain.model.StringOrObject import dev.usbharu.hideout.activitypub.domain.model.objects.Object import dev.usbharu.hideout.core.domain.model.actor.Actor import dev.usbharu.hideout.util.Base64Util @@ -74,7 +76,7 @@ class APRequestServiceImpl( date: String, u: URL, signer: Actor, - url: String + url: String, ): HttpResponse { val headers = headers { append("Accept", Activity) @@ -117,7 +119,7 @@ class APRequestServiceImpl( url: String, body: T?, signer: Actor?, - responseClass: Class + responseClass: Class, ): R { val bodyAsText = apPost(url, body, signer) return objectMapper.readValue(bodyAsText, responseClass) @@ -167,7 +169,7 @@ class APRequestServiceImpl( url: String, date: String?, digest: String, - requestBody: String? + requestBody: String?, ) = httpClient.post(url) { accept(Activity) header("Date", date) @@ -183,7 +185,7 @@ class APRequestServiceImpl( u: URL, digest: String, signer: Actor, - requestBody: String? + requestBody: String?, ): HttpResponse { val headers = headers { append("Accept", Activity) @@ -218,10 +220,10 @@ class APRequestServiceImpl( } private fun addContextIfNotNull(body: T?) = if (body != null) { - val mutableListOf = mutableListOf() - mutableListOf.add("https://www.w3.org/ns/activitystreams") - mutableListOf.addAll(body.context) - body.context = mutableListOf + val context = mutableListOf() + context.addAll(Constant.context) + context.addAll(body.context) + body.context = context objectMapper.writeValueAsString(body) } else { null diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/ActivityPubConfig.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/ActivityPubConfig.kt index f46a67ce..e77dd1d7 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/ActivityPubConfig.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/ActivityPubConfig.kt @@ -22,7 +22,10 @@ import com.fasterxml.jackson.annotation.Nulls import com.fasterxml.jackson.core.JsonParser import com.fasterxml.jackson.databind.DeserializationFeature import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.databind.module.SimpleModule import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import dev.usbharu.hideout.activitypub.domain.model.StringORObjectSerializer +import dev.usbharu.hideout.activitypub.domain.model.StringOrObject import dev.usbharu.hideout.core.infrastructure.httpsignature.HttpRequestMixIn import dev.usbharu.httpsignature.common.HttpRequest import dev.usbharu.httpsignature.sign.HttpSignatureSigner @@ -39,7 +42,11 @@ class ActivityPubConfig { @Bean @Qualifier("activitypub") fun objectMapper(): ObjectMapper { + + val module = SimpleModule().addSerializer(StringOrObject::class.java, StringORObjectSerializer()) + val objectMapper = jacksonObjectMapper() + .registerModules(module) .enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY) .setSerializationInclusion(JsonInclude.Include.NON_EMPTY) .setDefaultSetterInfo(JsonSetter.Value.forContentNulls(Nulls.SKIP)) @@ -49,6 +56,7 @@ class ActivityPubConfig { .configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true) .configure(JsonParser.Feature.ALLOW_TRAILING_COMMA, true) .addMixIn(HttpRequest::class.java, HttpRequestMixIn::class.java) + return objectMapper } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt index b4f6fb05..889133e7 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt @@ -17,11 +17,14 @@ package dev.usbharu.hideout.application.config import com.fasterxml.jackson.annotation.JsonInclude +import com.fasterxml.jackson.databind.module.SimpleModule import com.nimbusds.jose.jwk.JWKSet import com.nimbusds.jose.jwk.RSAKey import com.nimbusds.jose.jwk.source.ImmutableJWKSet import com.nimbusds.jose.jwk.source.JWKSource import com.nimbusds.jose.proc.SecurityContext +import dev.usbharu.hideout.activitypub.domain.model.StringORObjectSerializer +import dev.usbharu.hideout.activitypub.domain.model.StringOrObject import dev.usbharu.hideout.application.external.Transaction import dev.usbharu.hideout.application.infrastructure.springframework.RoleHierarchyAuthorizationManagerFactory import dev.usbharu.hideout.core.domain.model.actor.ActorRepository @@ -295,13 +298,16 @@ class SecurityConfig { @Primary fun jackson2ObjectMapperBuilderCustomizer(): Jackson2ObjectMapperBuilderCustomizer { return Jackson2ObjectMapperBuilderCustomizer { - it.serializationInclusion(JsonInclude.Include.ALWAYS).serializers() + it.serializationInclusion(JsonInclude.Include.ALWAYS) + .modulesToInstall(SimpleModule().addSerializer(StringOrObject::class.java, StringORObjectSerializer())) + .serializers() } } @Bean fun mappingJackson2HttpMessageConverter(): MappingJackson2HttpMessageConverter { val builder = Jackson2ObjectMapperBuilder().serializationInclusion(JsonInclude.Include.NON_NULL) + builder.modulesToInstall(SimpleModule().addSerializer(StringOrObject::class.java, StringORObjectSerializer())) return MappingJackson2HttpMessageConverter(builder.build()) } diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/DeleteSerializeTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/DeleteSerializeTest.kt index d301f079..6f964232 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/DeleteSerializeTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/DeleteSerializeTest.kt @@ -17,6 +17,7 @@ package dev.usbharu.hideout.activitypub.domain.model import com.fasterxml.jackson.module.kotlin.readValue +import dev.usbharu.hideout.activitypub.domain.Constant import dev.usbharu.hideout.application.config.ActivityPubConfig import org.intellij.lang.annotations.Language import org.junit.jupiter.api.Assertions.assertEquals @@ -37,14 +38,7 @@ class DeleteSerializeTest { "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", - "isCat" : "misskey:isCat", - "vcard" : "http://www.w3.org/2006/vcard/ns#" + "value" : "schema:value" } ], "type" : "Delete", "actor" : "https://misskey.usbharu.dev/users/97ws8y3rj6", @@ -69,7 +63,7 @@ class DeleteSerializeTest { ), published = "2023-11-02T15:30:34.160Z", ) - expected.context = listOf("https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1", "") + expected.context = Constant.context assertEquals(expected, readValue) } diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/JsonLdSerializeTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/JsonLdSerializeTest.kt index 445b68ba..9c9530cc 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/JsonLdSerializeTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/JsonLdSerializeTest.kt @@ -31,7 +31,7 @@ class JsonLdSerializeTest { val readValue = objectMapper.readValue(json) - assertEquals(JsonLd(listOf("https://example.com")), readValue) + assertEquals(JsonLd(listOf(StringOrObject("https://example.com"))), readValue) } @Test @@ -43,7 +43,14 @@ class JsonLdSerializeTest { val readValue = objectMapper.readValue(json) - assertEquals(JsonLd(listOf("https://example.com", "https://www.w3.org/ns/activitystreams")), readValue) + assertEquals( + JsonLd( + listOf( + StringOrObject("https://example.com"), + StringOrObject("https://www.w3.org/ns/activitystreams") + ) + ), readValue + ) } @Test @@ -67,7 +74,14 @@ class JsonLdSerializeTest { val readValue = objectMapper.readValue(json) - assertEquals(JsonLd(listOf("https://example.com", "https://www.w3.org/ns/activitystreams")), readValue) + assertEquals( + JsonLd( + listOf( + StringOrObject("https://example.com"), + StringOrObject("https://www.w3.org/ns/activitystreams") + ) + ), readValue + ) } @Test @@ -79,7 +93,7 @@ class JsonLdSerializeTest { val readValue = objectMapper.readValue(json) - assertEquals(JsonLd(emptyList()), readValue) + assertEquals(JsonLd(listOf(StringOrObject(mapOf("hoge" to "fuga")))), readValue) } @Test @@ -91,7 +105,15 @@ class JsonLdSerializeTest { val readValue = objectMapper.readValue(json) - assertEquals(JsonLd(listOf("https://example.com", "https://www.w3.org/ns/activitystreams")), readValue) + assertEquals( + JsonLd( + listOf( + StringOrObject("https://example.com"), + StringOrObject(mapOf("hoge" to "fuga")), + StringOrObject("https://www.w3.org/ns/activitystreams") + ) + ), readValue + ) } @Test @@ -130,7 +152,7 @@ class JsonLdSerializeTest { @Test fun contextが文字列のとき文字列としてシリアライズされる() { - val jsonLd = JsonLd(listOf("https://example.com")) + val jsonLd = JsonLd(listOf(StringOrObject("https://example.com"))) val objectMapper = ActivityPubConfig().objectMapper() @@ -141,7 +163,12 @@ class JsonLdSerializeTest { @Test fun contextが文字列の配列のとき配列としてシリアライズされる() { - val jsonLd = JsonLd(listOf("https://example.com", "https://www.w3.org/ns/activitystreams")) + val jsonLd = JsonLd( + listOf( + StringOrObject("https://example.com"), + StringOrObject("https://www.w3.org/ns/activitystreams") + ) + ) val objectMapper = ActivityPubConfig().objectMapper() @@ -149,4 +176,74 @@ class JsonLdSerializeTest { assertEquals("""{"@context":["https://example.com","https://www.w3.org/ns/activitystreams"]}""", actual) } + + @Test + fun contextがオブジェクトのときシリアライズできる() { + val jsonLd = JsonLd( + listOf( + StringOrObject(mapOf("hoge" to "fuga")) + ) + ) + + val objectMapper = ActivityPubConfig().objectMapper() + + val actual = objectMapper.writeValueAsString(jsonLd) + + assertEquals("""{"@context":{"hoge":"fuga"}}""", actual) + + } + + @Test + fun contextが複数のオブジェクトのときシリアライズできる() { + val jsonLd = JsonLd( + listOf( + StringOrObject(mapOf("hoge" to "fuga")), + StringOrObject(mapOf("foo" to "bar")) + ) + ) + + val objectMapper = ActivityPubConfig().objectMapper() + + val actual = objectMapper.writeValueAsString(jsonLd) + + assertEquals("""{"@context":[{"hoge":"fuga"},{"foo":"bar"}]}""", actual) + } + + @Test + fun contextが複数のオブジェクトのときデシリアライズできる() { + //language=JSON + val json = """{"@context":["https://example.com",{"hoge": "fuga"},{"foo": "bar"}]}""" + + val objectMapper = ActivityPubConfig().objectMapper() + + val readValue = objectMapper.readValue(json) + + assertEquals( + JsonLd( + listOf( + StringOrObject("https://example.com"), + StringOrObject(mapOf("hoge" to "fuga")), + StringOrObject(mapOf("foo" to "bar")) + ) + ), readValue + ) + } + + @Test + fun contextがオブジェクトのときデシリアライズできる() { + //language=JSON + val json = """{"@context":{"hoge": "fuga"}}""" + + val objectMapper = ActivityPubConfig().objectMapper() + + val readValue = objectMapper.readValue(json) + + assertEquals( + JsonLd( + listOf( + StringOrObject(mapOf("hoge" to "fuga")) + ) + ), readValue + ) + } } diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/NoteSerializeTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/NoteSerializeTest.kt index 7546c606..8c4b3f9d 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/NoteSerializeTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/NoteSerializeTest.kt @@ -17,6 +17,7 @@ package dev.usbharu.hideout.activitypub.domain.model import com.fasterxml.jackson.module.kotlin.readValue +import dev.usbharu.hideout.activitypub.domain.Constant import dev.usbharu.hideout.activitypub.service.objects.note.APNoteServiceImpl.Companion.public import dev.usbharu.hideout.application.config.ActivityPubConfig import org.assertj.core.api.Assertions.assertThat @@ -108,15 +109,7 @@ class NoteSerializeTest { "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#" + "value": "schema:value" } ], "id": "https://misskey.usbharu.dev/notes/9nj1omt1rn", @@ -182,10 +175,7 @@ class NoteSerializeTest { ) ) - expected.context = listOf( - "https://www.w3.org/ns/activitystreams", - "https://w3id.org/security/v1" - ) + expected.context = Constant.context val note = objectMapper.readValue(json) diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/RejectTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/RejectTest.kt index 94c006ba..65e26aac 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/RejectTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/RejectTest.kt @@ -17,6 +17,7 @@ package dev.usbharu.hideout.activitypub.domain.model import com.fasterxml.jackson.module.kotlin.readValue +import dev.usbharu.hideout.activitypub.domain.Constant import dev.usbharu.hideout.application.config.ActivityPubConfig import org.assertj.core.api.Assertions.assertThat import org.intellij.lang.annotations.Language @@ -38,15 +39,7 @@ class RejectTest { "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#" + "value" : "schema:value" } ], "type" : "Reject", "actor" : "https://misskey.usbharu.dev/users/97ws8y3rj6", @@ -72,7 +65,9 @@ class RejectTest { actor = "https://test-hideout.usbharu.dev/users/test-user2", id = "https://misskey.usbharu.dev/follows/9mxh6mawru/97ws8y3rj6" ) - ).apply { context = listOf("https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1") } + ).apply { + context = Constant.context + } assertThat(reject).isEqualTo(expected) } @@ -88,7 +83,12 @@ class RejectTest { 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") } + ).apply { + context = listOf( + StringOrObject("https://www.w3.org/ns/activitystreams"), + StringOrObject("https://w3id.org/security/v1") + ) + } val objectMapper = ActivityPubConfig().objectMapper() diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/actor/ActorAPControllerImplTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/actor/ActorAPControllerImplTest.kt index 656cdf6d..76239bdd 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/actor/ActorAPControllerImplTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/actor/ActorAPControllerImplTest.kt @@ -33,6 +33,7 @@ import org.mockito.kotlin.doReturn import org.mockito.kotlin.doThrow import org.mockito.kotlin.eq import org.mockito.kotlin.whenever +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter import org.springframework.test.web.servlet.MockMvc import org.springframework.test.web.servlet.get import org.springframework.test.web.servlet.post @@ -51,7 +52,10 @@ class ActorAPControllerImplTest { @BeforeEach fun setUp() { - mockMvc = MockMvcBuilders.standaloneSetup(userAPControllerImpl).build() + mockMvc = MockMvcBuilders + .standaloneSetup(userAPControllerImpl) + .setMessageConverters(MappingJackson2HttpMessageConverter(ActivityPubConfig().objectMapper())) + .build() } @Test @@ -85,6 +89,7 @@ class ActorAPControllerImplTest { mockMvc .get("/users/hoge") .asyncDispatch() + .andDo { print() } .andExpect { status { isOk() } } .andExpect { content { this.json(objectMapper.writeValueAsString(person)) } } } diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/common/APRequestServiceImplTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/common/APRequestServiceImplTest.kt index 84bf3a7e..e8243f53 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/common/APRequestServiceImplTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/common/APRequestServiceImplTest.kt @@ -17,7 +17,10 @@ package dev.usbharu.hideout.activitypub.service.common import com.fasterxml.jackson.module.kotlin.readValue +import dev.usbharu.hideout.activitypub.domain.Constant import dev.usbharu.hideout.activitypub.domain.model.Follow +import dev.usbharu.hideout.activitypub.domain.model.StringOrObject +import dev.usbharu.hideout.application.config.ActivityPubConfig import dev.usbharu.hideout.util.Base64Util import dev.usbharu.httpsignature.common.HttpHeaders import dev.usbharu.httpsignature.common.HttpMethod @@ -35,7 +38,6 @@ import org.mockito.kotlin.any import org.mockito.kotlin.doReturn import org.mockito.kotlin.eq import org.mockito.kotlin.mock -import utils.JsonObjectMapper.objectMapper import utils.UserBuilder import java.net.URL import java.security.MessageDigest @@ -57,7 +59,7 @@ class APRequestServiceImplTest { } respond("""{"type":"Follow","object": "https://example.com","actor": "https://example.com"}""") }), - objectMapper, + ActivityPubConfig().objectMapper(), mock(), dateTimeFormatter ) @@ -82,7 +84,7 @@ class APRequestServiceImplTest { } respond("""{"type":"Follow","object": "https://example.com","actor": "https://example.com"}""") }), - objectMapper, + ActivityPubConfig().objectMapper(), mock(), dateTimeFormatter ) @@ -122,7 +124,7 @@ class APRequestServiceImplTest { } respond("""{"type":"Follow","object": "https://example.com","actor": "https://example.com"}""") }), - objectMapper, + ActivityPubConfig().objectMapper(), httpSignatureSigner, dateTimeFormatter ) @@ -171,12 +173,12 @@ class APRequestServiceImplTest { fun `apPost bodyがnullでないときcontextにactivitystreamのURLを追加する`() = runTest { val dateTimeFormatter = DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US) val apRequestServiceImpl = APRequestServiceImpl(HttpClient(MockEngine { - val readValue = objectMapper.readValue(it.body.toByteArray()) + val readValue = ActivityPubConfig().objectMapper().readValue(it.body.toByteArray()) - assertThat(readValue.context).contains("https://www.w3.org/ns/activitystreams") + assertThat(readValue.context).containsAll(Constant.context) respondOk("{}") - }), objectMapper, mock(), dateTimeFormatter) + }), ActivityPubConfig().objectMapper(), mock(), dateTimeFormatter) val body = Follow( apObject = "https://example.com", @@ -193,7 +195,7 @@ class APRequestServiceImplTest { assertEquals(0, it.body.toByteArray().size) respondOk("{}") - }), objectMapper, mock(), dateTimeFormatter) + }), ActivityPubConfig().objectMapper(), mock(), dateTimeFormatter) apRequestServiceImpl.apPost("https://example.com", null, null) } @@ -203,9 +205,9 @@ class APRequestServiceImplTest { val dateTimeFormatter = DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US) val apRequestServiceImpl = APRequestServiceImpl(HttpClient(MockEngine { val src = it.body.toByteArray() - val readValue = objectMapper.readValue(src) + val readValue = ActivityPubConfig().objectMapper().readValue(src) - assertThat(readValue.context).contains("https://www.w3.org/ns/activitystreams") + assertThat(readValue.context).contains(StringOrObject("https://www.w3.org/ns/activitystreams")) val map = it.headers.toMap() assertThat(map).containsKey("Date") @@ -222,7 +224,7 @@ class APRequestServiceImplTest { assertEquals(digest, it.headers["Digest"].orEmpty().split("256=").last()) respondOk("{}") - }), objectMapper, mock(), dateTimeFormatter) + }), ActivityPubConfig().objectMapper(), mock(), dateTimeFormatter) val body = Follow( apObject = "https://example.com", @@ -236,9 +238,9 @@ class APRequestServiceImplTest { val dateTimeFormatter = DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US) val apRequestServiceImpl = APRequestServiceImpl(HttpClient(MockEngine { val src = it.body.toByteArray() - val readValue = objectMapper.readValue(src) + val readValue = ActivityPubConfig().objectMapper().readValue(src) - assertThat(readValue.context).contains("https://www.w3.org/ns/activitystreams") + assertThat(readValue.context).contains(StringOrObject("https://www.w3.org/ns/activitystreams")) val map = it.headers.toMap() assertThat(map).containsKey("Date") @@ -252,7 +254,7 @@ class APRequestServiceImplTest { assertEquals(digest, it.headers["Digest"].orEmpty().split("256=").last()) respondOk("{}") - }), objectMapper, mock(), dateTimeFormatter) + }), ActivityPubConfig().objectMapper(), mock(), dateTimeFormatter) val body = Follow( apObject = "https://example.com", @@ -277,9 +279,9 @@ class APRequestServiceImplTest { } val apRequestServiceImpl = APRequestServiceImpl(HttpClient(MockEngine { val src = it.body.toByteArray() - val readValue = objectMapper.readValue(src) + val readValue = ActivityPubConfig().objectMapper().readValue(src) - assertThat(readValue.context).contains("https://www.w3.org/ns/activitystreams") + assertThat(readValue.context).contains(StringOrObject("https://www.w3.org/ns/activitystreams")) val map = it.headers.toMap() assertThat(map).containsKey("Date") @@ -293,7 +295,7 @@ class APRequestServiceImplTest { assertEquals(digest, it.headers["Digest"].orEmpty().split("256=").last()) respondOk("{}") - }), objectMapper, httpSignatureSigner, dateTimeFormatter) + }), ActivityPubConfig().objectMapper(), httpSignatureSigner, dateTimeFormatter) val body = Follow( apObject = "https://example.com", @@ -338,12 +340,12 @@ class APRequestServiceImplTest { val dateTimeFormatter = DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US) val apRequestServiceImpl = APRequestServiceImpl(HttpClient(MockEngine { val src = it.body.toByteArray() - val readValue = objectMapper.readValue(src) + val readValue = ActivityPubConfig().objectMapper().readValue(src) - assertThat(readValue.context).contains("https://www.w3.org/ns/activitystreams") + assertThat(readValue.context).contains(StringOrObject("https://www.w3.org/ns/activitystreams")) respondOk(src.decodeToString()) - }), objectMapper, mock(), dateTimeFormatter) + }), ActivityPubConfig().objectMapper(), mock(), dateTimeFormatter) val body = Follow( apObject = "https://example.com",