This commit is contained in:
usbharu 2024-05-14 10:47:51 +09:00
parent ae43b5e793
commit 8f553d3ecd
10 changed files with 81 additions and 33 deletions

View File

@ -31,18 +31,18 @@ import com.fasterxml.jackson.databind.annotation.JsonSerialize
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
open class JsonLd { open class JsonLd {
@JsonProperty("@context") @JsonProperty("@context")
@JsonDeserialize(contentUsing = ContextDeserializer::class) @JsonDeserialize(contentUsing = StringOrObjectDeserializer::class)
@JsonSerialize(include = JsonSerialize.Inclusion.NON_EMPTY, using = ContextSerializer::class) @JsonSerialize(include = JsonSerialize.Inclusion.NON_EMPTY, contentUsing = StringORObjectSerializer::class)
@JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonInclude(JsonInclude.Include.NON_EMPTY)
var context: List<StringOrObject> = emptyList() var context: List<StringOrObject> = emptyList()
set(value) { set(value) {
field = value.filter { it.isEmpty() } field = value.filterNot { it.isEmpty() }
} }
@JsonCreator @JsonCreator
constructor(context: List<StringOrObject?>?) { constructor(context: List<StringOrObject?>?) {
if (context != null) { if (context != null) {
this.context = context.filterNotNull().filter { it.isEmpty() } this.context = context.filterNotNull().filterNot { it.isEmpty() }
} else { } else {
this.context = emptyList() this.context = emptyList()
} }
@ -89,17 +89,13 @@ class ContextSerializer : JsonSerializer<List<StringOrObject>>() {
return return
} }
if (value.size == 1) { if (value.size == 1) {
gen?.writeString(value[0]) serializers.findValueSerializer(StringOrObject::class.java).serialize(value[0], gen, serializers)
} else { } else {
gen?.writeStartArray() gen?.writeStartArray()
value.forEach { value.forEach {
gen?.writeString(it) serializers.findValueSerializer(StringOrObject::class.java).serialize(it, gen, serializers)
} }
gen?.writeEndArray() gen?.writeEndArray()
} }
} }
override fun serialize(value: List<StringOrObject>?, gen: JsonGenerator?, serializers: SerializerProvider?) {
}
} }

View File

@ -50,14 +50,19 @@ open class StringOrObject {
class StringOrObjectDeserializer : JsonDeserializer<StringOrObject>() { class StringOrObjectDeserializer : JsonDeserializer<StringOrObject>() {
override fun deserialize(p: JsonParser?, ctxt: DeserializationContext?): StringOrObject { override fun deserialize(p: JsonParser?, ctxt: DeserializationContext): StringOrObject {
val readTree: JsonNode = p?.codec?.readTree(p) ?: return StringOrObject("") val readTree: JsonNode = p?.codec?.readTree(p) ?: return StringOrObject("")
return if (readTree.isValueNode) { return if (readTree.isValueNode) {
StringOrObject(readTree.textValue()) StringOrObject(readTree.textValue())
} else { } else {
val map: Map<String, String> = ctxt.readTreeAsValue<Map<String, String>>(
readTree,
ctxt.typeFactory.constructType(object : TypeReference<Map<String, String>>() {})
)
println(readTree.toPrettyString())
val map = p.readValueAs<Map<String, String>>(object : TypeReference<Map<String, String>>() {}) // val map = p.codec.readValue<Map<String, String>>(p,object : TypeReference<Map<String, String>>() {})
println(map)
StringOrObject(map) StringOrObject(map)
} }
} }
@ -72,8 +77,10 @@ class StringORObjectSerializer : JsonSerializer<StringOrObject>() {
} }
if (value.contextString != null) { if (value.contextString != null) {
gen?.writeString(value.contextString) gen?.writeString(value.contextString)
println("serialize string")
} else { } else {
serializers.defaultSerializeValue(value.contextObject, gen) serializers.defaultSerializeValue(value.contextObject, gen)
println("serialize string")
} }
} }
} }

View File

@ -17,6 +17,7 @@
package dev.usbharu.hideout.activitypub.interfaces.api.actor package dev.usbharu.hideout.activitypub.interfaces.api.actor
import dev.usbharu.hideout.activitypub.domain.model.Person import dev.usbharu.hideout.activitypub.domain.model.Person
import dev.usbharu.hideout.activitypub.domain.model.StringOrObject
import dev.usbharu.hideout.activitypub.service.objects.user.APUserService import dev.usbharu.hideout.activitypub.service.objects.user.APUserService
import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException
import org.springframework.http.HttpStatus import org.springframework.http.HttpStatus
@ -31,7 +32,7 @@ class UserAPControllerImpl(private val apUserService: APUserService) : UserAPCon
} catch (_: UserNotFoundException) { } catch (_: UserNotFoundException) {
return ResponseEntity.notFound().build() return ResponseEntity.notFound().build()
} }
person.context += listOf("https://www.w3.org/ns/activitystreams") person.context += listOf(StringOrObject("https://www.w3.org/ns/activitystreams"))
return ResponseEntity(person, HttpStatus.OK) return ResponseEntity(person, HttpStatus.OK)
} }
} }

View File

@ -17,6 +17,7 @@
package dev.usbharu.hideout.activitypub.service.common package dev.usbharu.hideout.activitypub.service.common
import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.ObjectMapper
import dev.usbharu.hideout.activitypub.domain.model.StringOrObject
import dev.usbharu.hideout.activitypub.domain.model.objects.Object import dev.usbharu.hideout.activitypub.domain.model.objects.Object
import dev.usbharu.hideout.core.domain.model.actor.Actor import dev.usbharu.hideout.core.domain.model.actor.Actor
import dev.usbharu.hideout.util.Base64Util import dev.usbharu.hideout.util.Base64Util
@ -218,8 +219,8 @@ class APRequestServiceImpl(
} }
private fun <T : Object> addContextIfNotNull(body: T?) = if (body != null) { private fun <T : Object> addContextIfNotNull(body: T?) = if (body != null) {
val mutableListOf = mutableListOf<String>() val mutableListOf = mutableListOf<StringOrObject>()
mutableListOf.add("https://www.w3.org/ns/activitystreams") mutableListOf.add(StringOrObject("https://www.w3.org/ns/activitystreams"))
mutableListOf.addAll(body.context) mutableListOf.addAll(body.context)
body.context = mutableListOf body.context = mutableListOf
objectMapper.writeValueAsString(body) objectMapper.writeValueAsString(body)

View File

@ -49,6 +49,7 @@ class ActivityPubConfig {
.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true) .configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true)
.configure(JsonParser.Feature.ALLOW_TRAILING_COMMA, true) .configure(JsonParser.Feature.ALLOW_TRAILING_COMMA, true)
.addMixIn(HttpRequest::class.java, HttpRequestMixIn::class.java) .addMixIn(HttpRequest::class.java, HttpRequestMixIn::class.java)
return objectMapper return objectMapper
} }

View File

@ -69,7 +69,11 @@ class DeleteSerializeTest {
), ),
published = "2023-11-02T15:30:34.160Z", published = "2023-11-02T15:30:34.160Z",
) )
expected.context = listOf("https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1", "") expected.context = listOf(
StringOrObject("https://www.w3.org/ns/activitystreams"),
StringOrObject("https://w3id.org/security/v1"),
StringOrObject("")
)
assertEquals(expected, readValue) assertEquals(expected, readValue)
} }

View File

@ -31,7 +31,7 @@ class JsonLdSerializeTest {
val readValue = objectMapper.readValue<JsonLd>(json) val readValue = objectMapper.readValue<JsonLd>(json)
assertEquals(JsonLd(listOf("https://example.com")), readValue) assertEquals(JsonLd(listOf(StringOrObject("https://example.com"))), readValue)
} }
@Test @Test
@ -43,7 +43,14 @@ class JsonLdSerializeTest {
val readValue = objectMapper.readValue<JsonLd>(json) val readValue = objectMapper.readValue<JsonLd>(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 @Test
@ -67,7 +74,14 @@ class JsonLdSerializeTest {
val readValue = objectMapper.readValue<JsonLd>(json) val readValue = objectMapper.readValue<JsonLd>(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 @Test
@ -79,7 +93,7 @@ class JsonLdSerializeTest {
val readValue = objectMapper.readValue<JsonLd>(json) val readValue = objectMapper.readValue<JsonLd>(json)
assertEquals(JsonLd(emptyList()), readValue) assertEquals(JsonLd(listOf(StringOrObject(mapOf("hoge" to "fuga")))), readValue)
} }
@Test @Test
@ -91,7 +105,15 @@ class JsonLdSerializeTest {
val readValue = objectMapper.readValue<JsonLd>(json) val readValue = objectMapper.readValue<JsonLd>(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 @Test
@ -130,7 +152,7 @@ class JsonLdSerializeTest {
@Test @Test
fun contextが文字列のとき文字列としてシリアライズされる() { fun contextが文字列のとき文字列としてシリアライズされる() {
val jsonLd = JsonLd(listOf("https://example.com")) val jsonLd = JsonLd(listOf(StringOrObject("https://example.com")))
val objectMapper = ActivityPubConfig().objectMapper() val objectMapper = ActivityPubConfig().objectMapper()
@ -141,7 +163,12 @@ class JsonLdSerializeTest {
@Test @Test
fun contextが文字列の配列のとき配列としてシリアライズされる() { 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() val objectMapper = ActivityPubConfig().objectMapper()

View File

@ -183,8 +183,8 @@ class NoteSerializeTest {
) )
expected.context = listOf( expected.context = listOf(
"https://www.w3.org/ns/activitystreams", StringOrObject("https://www.w3.org/ns/activitystreams"),
"https://w3id.org/security/v1" StringOrObject("https://w3id.org/security/v1")
) )
val note = objectMapper.readValue<Note>(json) val note = objectMapper.readValue<Note>(json)

View File

@ -72,7 +72,12 @@ class RejectTest {
actor = "https://test-hideout.usbharu.dev/users/test-user2", actor = "https://test-hideout.usbharu.dev/users/test-user2",
id = "https://misskey.usbharu.dev/follows/9mxh6mawru/97ws8y3rj6" id = "https://misskey.usbharu.dev/follows/9mxh6mawru/97ws8y3rj6"
) )
).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")
)
}
assertThat(reject).isEqualTo(expected) assertThat(reject).isEqualTo(expected)
} }
@ -88,7 +93,12 @@ class RejectTest {
apObject = "https://misskey.usbharu.dev/users/97ws8y3rj6", apObject = "https://misskey.usbharu.dev/users/97ws8y3rj6",
actor = "https://test-hideout.usbharu.dev/users/test-user2" 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() val objectMapper = ActivityPubConfig().objectMapper()

View File

@ -18,6 +18,7 @@ package dev.usbharu.hideout.activitypub.service.common
import com.fasterxml.jackson.module.kotlin.readValue import com.fasterxml.jackson.module.kotlin.readValue
import dev.usbharu.hideout.activitypub.domain.model.Follow import dev.usbharu.hideout.activitypub.domain.model.Follow
import dev.usbharu.hideout.activitypub.domain.model.StringOrObject
import dev.usbharu.hideout.util.Base64Util import dev.usbharu.hideout.util.Base64Util
import dev.usbharu.httpsignature.common.HttpHeaders import dev.usbharu.httpsignature.common.HttpHeaders
import dev.usbharu.httpsignature.common.HttpMethod import dev.usbharu.httpsignature.common.HttpMethod
@ -173,7 +174,7 @@ class APRequestServiceImplTest {
val apRequestServiceImpl = APRequestServiceImpl(HttpClient(MockEngine { val apRequestServiceImpl = APRequestServiceImpl(HttpClient(MockEngine {
val readValue = objectMapper.readValue<Follow>(it.body.toByteArray()) val readValue = objectMapper.readValue<Follow>(it.body.toByteArray())
assertThat(readValue.context).contains("https://www.w3.org/ns/activitystreams") assertThat(readValue.context).contains(StringOrObject("https://www.w3.org/ns/activitystreams"))
respondOk("{}") respondOk("{}")
}), objectMapper, mock(), dateTimeFormatter) }), objectMapper, mock(), dateTimeFormatter)
@ -205,7 +206,7 @@ class APRequestServiceImplTest {
val src = it.body.toByteArray() val src = it.body.toByteArray()
val readValue = objectMapper.readValue<Follow>(src) val readValue = objectMapper.readValue<Follow>(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() val map = it.headers.toMap()
assertThat(map).containsKey("Date") assertThat(map).containsKey("Date")
@ -238,7 +239,7 @@ class APRequestServiceImplTest {
val src = it.body.toByteArray() val src = it.body.toByteArray()
val readValue = objectMapper.readValue<Follow>(src) val readValue = objectMapper.readValue<Follow>(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() val map = it.headers.toMap()
assertThat(map).containsKey("Date") assertThat(map).containsKey("Date")
@ -279,7 +280,7 @@ class APRequestServiceImplTest {
val src = it.body.toByteArray() val src = it.body.toByteArray()
val readValue = objectMapper.readValue<Follow>(src) val readValue = objectMapper.readValue<Follow>(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() val map = it.headers.toMap()
assertThat(map).containsKey("Date") assertThat(map).containsKey("Date")
@ -340,7 +341,7 @@ class APRequestServiceImplTest {
val src = it.body.toByteArray() val src = it.body.toByteArray()
val readValue = objectMapper.readValue<Follow>(src) val readValue = objectMapper.readValue<Follow>(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()) respondOk(src.decodeToString())
}), objectMapper, mock(), dateTimeFormatter) }), objectMapper, mock(), dateTimeFormatter)