feat: Actorのインスタンス化時にチェックするように

This commit is contained in:
usbharu 2024-02-26 15:58:32 +09:00
parent 15b3cf6796
commit 7719863b80
4 changed files with 78 additions and 30 deletions

View File

@ -18,37 +18,56 @@ package dev.usbharu.hideout.core.domain.model.actor
import dev.usbharu.hideout.application.config.ApplicationConfig import dev.usbharu.hideout.application.config.ApplicationConfig
import dev.usbharu.hideout.application.config.CharacterLimit import dev.usbharu.hideout.application.config.CharacterLimit
import jakarta.validation.Validator
import jakarta.validation.constraints.*
import org.hibernate.validator.constraints.URL
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.stereotype.Component import org.springframework.stereotype.Component
import java.time.Instant import java.time.Instant
import kotlin.math.max import kotlin.math.max
data class Actor private constructor( data class Actor private constructor(
@get:NotNull
@get:Positive
val id: Long, val id: Long,
@get:Pattern(regexp = "^[a-zA-Z0-9_-]{1,300}\$")
@get:Size(min = 1)
val name: String, val name: String,
@get:Pattern(regexp = "^([a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]*\\.){1,255}[a-zA-Z]{2,}\$")
val domain: String, val domain: String,
val screenName: String, val screenName: String,
val description: String, val description: String,
@get:URL
val inbox: String, val inbox: String,
@get:URL
val outbox: String, val outbox: String,
@get:URL
val url: String, val url: String,
@get:NotBlank
val publicKey: String, val publicKey: String,
val privateKey: String? = null, val privateKey: String? = null,
@get:PastOrPresent
val createdAt: Instant, val createdAt: Instant,
@get:NotBlank
val keyId: String, val keyId: String,
val followers: String? = null, val followers: String? = null,
val following: String? = null, val following: String? = null,
@get:Positive
val instance: Long? = null, val instance: Long? = null,
val locked: Boolean, val locked: Boolean,
val followersCount: Int = 0, val followersCount: Int = 0,
val followingCount: Int = 0, val followingCount: Int = 0,
val postsCount: Int = 0, val postsCount: Int = 0,
val lastPostDate: Instant? = null, val lastPostDate: Instant? = null,
val emojis: List<Long> = emptyList() val emojis: List<Long> = emptyList(),
) { ) {
@Component @Component
class UserBuilder(private val characterLimit: CharacterLimit, private val applicationConfig: ApplicationConfig) { class UserBuilder(
private val characterLimit: CharacterLimit,
private val applicationConfig: ApplicationConfig,
private val validator: Validator,
) {
private val logger = LoggerFactory.getLogger(UserBuilder::class.java) private val logger = LoggerFactory.getLogger(UserBuilder::class.java)
@ -74,7 +93,7 @@ data class Actor private constructor(
followingCount: Int = 0, followingCount: Int = 0,
postsCount: Int = 0, postsCount: Int = 0,
lastPostDate: Instant? = null, lastPostDate: Instant? = null,
emojis: List<Long> = emptyList() emojis: List<Long> = emptyList(),
): Actor { ): Actor {
if (id == 0L) { if (id == 0L) {
return Actor( return Actor(
@ -176,7 +195,7 @@ data class Actor private constructor(
"keyId must contain non-blank characters." "keyId must contain non-blank characters."
} }
return Actor( val actor = Actor(
id = id, id = id,
name = limitedName, name = limitedName,
domain = domain, domain = domain,
@ -199,6 +218,13 @@ data class Actor private constructor(
lastPostDate = lastPostDate, lastPostDate = lastPostDate,
emojis = emojis emojis = emojis
) )
val validate = validator.validate(actor)
for (constraintViolation in validate) {
throw IllegalArgumentException("${constraintViolation.propertyPath} : ${constraintViolation.message}")
}
return actor
} }
} }

View File

@ -0,0 +1,13 @@
package dev.usbharu.hideout.core.domain.model.actor
import org.junit.jupiter.api.Test
import utils.UserBuilder
class ActorTest {
@Test
fun validator() {
org.junit.jupiter.api.assertThrows<IllegalArgumentException> {
UserBuilder.localUserOf(name = "うんこ")
}
}
}

View File

@ -25,6 +25,7 @@ import dev.usbharu.hideout.core.domain.model.actor.Actor
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
import dev.usbharu.hideout.core.domain.model.post.Post import dev.usbharu.hideout.core.domain.model.post.Post
import dev.usbharu.hideout.core.service.post.DefaultPostContentFormatter import dev.usbharu.hideout.core.service.post.DefaultPostContentFormatter
import jakarta.validation.Validation
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
@ -37,7 +38,11 @@ import kotlin.test.assertEquals
import kotlin.test.assertNull import kotlin.test.assertNull
class ActorServiceTest { class ActorServiceTest {
val actorBuilder = Actor.UserBuilder(CharacterLimit(), ApplicationConfig(URL("https://example.com"))) val actorBuilder = Actor.UserBuilder(
CharacterLimit(),
ApplicationConfig(URL("https://example.com")),
Validation.buildDefaultValidatorFactory().validator
)
val postBuilder = Post.PostBuilder(CharacterLimit(), DefaultPostContentFormatter(HtmlSanitizeConfig().policy())) val postBuilder = Post.PostBuilder(CharacterLimit(), DefaultPostContentFormatter(HtmlSanitizeConfig().policy()))
@Test @Test
fun `createLocalUser ローカルユーザーを作成できる`() = runTest { fun `createLocalUser ローカルユーザーを作成できる`() = runTest {

View File

@ -20,12 +20,16 @@ import dev.usbharu.hideout.application.config.ApplicationConfig
import dev.usbharu.hideout.application.config.CharacterLimit import dev.usbharu.hideout.application.config.CharacterLimit
import dev.usbharu.hideout.application.service.id.TwitterSnowflakeIdGenerateService import dev.usbharu.hideout.application.service.id.TwitterSnowflakeIdGenerateService
import dev.usbharu.hideout.core.domain.model.actor.Actor import dev.usbharu.hideout.core.domain.model.actor.Actor
import jakarta.validation.Validation
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import java.net.URL import java.net.URL
import java.time.Instant import java.time.Instant
object UserBuilder { object UserBuilder {
private val actorBuilder = Actor.UserBuilder(CharacterLimit(), ApplicationConfig(URL("https://example.com"))) private val actorBuilder = Actor.UserBuilder(
CharacterLimit(), ApplicationConfig(URL("https://example.com")),
Validation.buildDefaultValidatorFactory().validator
)
private val idGenerator = TwitterSnowflakeIdGenerateService private val idGenerator = TwitterSnowflakeIdGenerateService
@ -43,7 +47,7 @@ object UserBuilder {
createdAt: Instant = Instant.now(), createdAt: Instant = Instant.now(),
keyId: String = "https://$domain/users/$id#pubkey", keyId: String = "https://$domain/users/$id#pubkey",
followers: String = "https://$domain/users/$id/followers", followers: String = "https://$domain/users/$id/followers",
following: String = "https://$domain/users/$id/following" following: String = "https://$domain/users/$id/following",
): Actor { ): Actor {
return actorBuilder.of( return actorBuilder.of(
id = id, id = id,
@ -77,7 +81,7 @@ object UserBuilder {
createdAt: Instant = Instant.now(), createdAt: Instant = Instant.now(),
keyId: String = "https://$domain/$id#pubkey", keyId: String = "https://$domain/$id#pubkey",
followers: String = "https://$domain/$id/followers", followers: String = "https://$domain/$id/followers",
following: String = "https://$domain/$id/following" following: String = "https://$domain/$id/following",
): Actor { ): Actor {
return actorBuilder.of( return actorBuilder.of(
id = id, id = id,