feat: Nodeinfoのデシリアライズ用クラスを別に準備

This commit is contained in:
usbharu 2023-11-18 12:02:40 +09:00
parent 4837192139
commit ee56adcf27
5 changed files with 55 additions and 67 deletions

View File

@ -3,7 +3,10 @@ package dev.usbharu.hideout.activitypub.service.common
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.user.User import dev.usbharu.hideout.core.domain.model.user.User
import dev.usbharu.hideout.core.domain.model.user.UserRepository import dev.usbharu.hideout.core.domain.model.user.UserRepository
import dev.usbharu.hideout.core.service.resource.CacheManager
import dev.usbharu.hideout.core.service.resource.ResolveResponse
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
import java.io.InputStream
@Service @Service
class APResourceResolveServiceImpl( class APResourceResolveServiceImpl(
@ -25,7 +28,7 @@ class APResourceResolveServiceImpl(
cacheManager.putCache(key) { cacheManager.putCache(key) {
runResolve(url, singerId?.let { userRepository.findById(it) }, clazz) runResolve(url, singerId?.let { userRepository.findById(it) }, clazz)
} }
return cacheManager.getOrWait(key) as T return (cacheManager.getOrWait(key) as APResolveResponse<T>).objects
} }
private suspend fun <T : Object> internalResolve(url: String, singer: User?, clazz: Class<T>): T { private suspend fun <T : Object> internalResolve(url: String, singer: User?, clazz: Class<T>): T {
@ -33,11 +36,12 @@ class APResourceResolveServiceImpl(
cacheManager.putCache(key) { cacheManager.putCache(key) {
runResolve(url, singer, clazz) runResolve(url, singer, clazz)
} }
return cacheManager.getOrWait(key) as T return (cacheManager.getOrWait(key) as APResolveResponse<T>).objects
} }
private suspend fun <T : Object> runResolve(url: String, singer: User?, clazz: Class<T>): Object = private suspend fun <T : Object> runResolve(url: String, singer: User?, clazz: Class<T>): ResolveResponse {
apRequestService.apGet(url, singer, clazz) return APResolveResponse(apRequestService.apGet(url, singer, clazz))
}
private fun genCacheKey(url: String, singerId: Long?): String { private fun genCacheKey(url: String, singerId: Long?): String {
if (singerId != null) { if (singerId != null) {
@ -45,4 +49,30 @@ class APResourceResolveServiceImpl(
} }
return url return url
} }
private class APResolveResponse<T : Object>(val objects: T) : ResolveResponse {
override suspend fun body(): InputStream {
TODO("Not yet implemented")
}
override suspend fun bodyAsText(): String {
TODO("Not yet implemented")
}
override suspend fun bodyAsBytes(): ByteArray {
TODO("Not yet implemented")
}
override suspend fun header(): Map<String, List<String>> {
TODO("Not yet implemented")
}
override suspend fun status(): Int {
TODO("Not yet implemented")
}
override suspend fun statusMessage(): String {
TODO("Not yet implemented")
}
}
} }

View File

@ -1,9 +0,0 @@
package dev.usbharu.hideout.activitypub.service.common
import dev.usbharu.hideout.activitypub.domain.model.objects.Object
interface CacheManager {
suspend fun putCache(key: String, block: suspend () -> Object)
suspend fun getOrWait(key: String): Object
}

View File

@ -1,50 +0,0 @@
package dev.usbharu.hideout.activitypub.service.common
import dev.usbharu.hideout.activitypub.domain.model.objects.Object
import dev.usbharu.hideout.util.LruCache
import kotlinx.coroutines.delay
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import org.springframework.stereotype.Service
import java.time.Instant
@Service
class InMemoryCacheManager : CacheManager {
private val cacheKey = LruCache<String, Long>(15)
private val valueStore = mutableMapOf<String, Object>()
private val keyMutex = Mutex()
override suspend fun putCache(key: String, block: suspend () -> Object) {
val needRunBlock: Boolean
keyMutex.withLock {
cacheKey.filter { Instant.ofEpochMilli(it.value).plusSeconds(300) <= Instant.now() }
val cached = cacheKey.get(key)
if (cached == null) {
needRunBlock = true
cacheKey[key] = Instant.now().toEpochMilli()
valueStore.remove(key)
} else {
needRunBlock = false
}
}
if (needRunBlock) {
val processed = block()
if (cacheKey.containsKey(key)) {
valueStore[key] = processed
}
}
}
override suspend fun getOrWait(key: String): Object {
while (valueStore.contains(key).not()) {
if (cacheKey.containsKey(key).not()) {
throw IllegalStateException("Invalid cache key.")
}
delay(1)
}
return valueStore.getValue(key)
}
}

View File

@ -0,0 +1,16 @@
package dev.usbharu.hideout.core.domain.model.instance
class Nodeinfo {
var links: List<Links> = emptyList()
protected constructor()
}
class Links {
var rel: String? = null
var href: String? = null
protected constructor()
}

View File

@ -1,11 +1,12 @@
package dev.usbharu.hideout.core.service.instance package dev.usbharu.hideout.core.service.instance
import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.ObjectMapper
import dev.usbharu.hideout.activitypub.domain.model.nodeinfo.Nodeinfo
import dev.usbharu.hideout.activitypub.domain.model.nodeinfo.Nodeinfo2_0 import dev.usbharu.hideout.activitypub.domain.model.nodeinfo.Nodeinfo2_0
import dev.usbharu.hideout.core.domain.model.instance.Instance import dev.usbharu.hideout.core.domain.model.instance.Instance
import dev.usbharu.hideout.core.domain.model.instance.InstanceRepository import dev.usbharu.hideout.core.domain.model.instance.InstanceRepository
import dev.usbharu.hideout.core.domain.model.instance.Nodeinfo
import dev.usbharu.hideout.core.service.resource.ResourceResolveService import dev.usbharu.hideout.core.service.resource.ResourceResolveService
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
import java.net.URL import java.net.URL
import java.time.Instant import java.time.Instant
@ -20,21 +21,21 @@ interface InstanceService {
class InstanceServiceImpl( class InstanceServiceImpl(
private val instanceRepository: InstanceRepository, private val instanceRepository: InstanceRepository,
private val resourceResolveService: ResourceResolveService, private val resourceResolveService: ResourceResolveService,
private val objectMapper: ObjectMapper @Qualifier("activitypub") private val objectMapper: ObjectMapper
) : InstanceService { ) : InstanceService {
override suspend fun fetchInstance(url: String): Instance { override suspend fun fetchInstance(url: String): Instance {
val u = URL(url) val u = URL(url)
val resolveInstanceUrl = u.protocol + "://" + u.host val resolveInstanceUrl = u.protocol + "://" + u.host
val nodeinfoJson = resourceResolveService.resolve("$resolveInstanceUrl/.well-known/nodeinfo").bodyAsText() val nodeinfoJson = resourceResolveService.resolve("$resolveInstanceUrl/.well-known/nodeinfo").bodyAsText()
val nodeinfo = objectMapper.readValue(nodeinfoJson, Nodeinfo::class.java) val nodeinfo = objectMapper.readValue(nodeinfoJson, Nodeinfo::class.java)
val nodeinfoPathMap = nodeinfo.links.map { it.rel to it.href }.toMap() val nodeinfoPathMap = nodeinfo.links.associate { it.rel to it.href }
for ((key, value) in nodeinfoPathMap) { for ((key, value) in nodeinfoPathMap) {
when (key) { when (key) {
"http://nodeinfo.diaspora.software/ns/schema/2.0" -> { "http://nodeinfo.diaspora.software/ns/schema/2.0" -> {
val nodeinfo20 = objectMapper.readValue( val nodeinfo20 = objectMapper.readValue(
resourceResolveService.resolve(value).bodyAsText(), resourceResolveService.resolve(value!!).bodyAsText(),
Nodeinfo2_0::class.java Nodeinfo2_0::class.java
) )