diff --git a/src/main/kotlin/dev/usbharu/hideout/domain/model/hideout/entity/User.kt b/src/main/kotlin/dev/usbharu/hideout/domain/model/hideout/entity/User.kt index d79d617b..38eb81a2 100644 --- a/src/main/kotlin/dev/usbharu/hideout/domain/model/hideout/entity/User.kt +++ b/src/main/kotlin/dev/usbharu/hideout/domain/model/hideout/entity/User.kt @@ -1,7 +1,10 @@ package dev.usbharu.hideout.domain.model.hideout.entity +import dev.usbharu.hideout.config.ApplicationConfig +import dev.usbharu.hideout.config.CharacterLimit import dev.usbharu.hideout.config.Config import org.slf4j.LoggerFactory +import org.springframework.stereotype.Component import java.time.Instant data class User private constructor( @@ -145,4 +148,121 @@ data class User private constructor( ) } } + + @Component + class UserBuilder(private val characterLimit: CharacterLimit, private val applicationConfig: ApplicationConfig) { + private val logger = LoggerFactory.getLogger(UserBuilder::class.java) + + @Suppress("LongParameterList", "FunctionMinLength", "LongMethod") + fun of( + id: Long, + name: String, + domain: String, + screenName: String, + description: String, + password: String? = null, + inbox: String, + outbox: String, + url: String, + publicKey: String, + privateKey: String? = null, + createdAt: Instant, + keyId: String, + following: String? = null, + followers: String? = null + ): User { + // idは0未満ではいけない + require(id >= 0) { "id must be greater than or equal to 0." } + + // nameは空文字以外を含める必要がある + require(name.isNotBlank()) { "name must contain non-blank characters." } + + // nameは指定された長さ以下である必要がある + val limitedName = if (name.length >= characterLimit.account.id) { + logger.warn("name must not exceed ${characterLimit.account.id} characters.") + name.substring(0, characterLimit.account.id) + } else { + name + } + + // domainは空文字以外を含める必要がある + require(domain.isNotBlank()) { "domain must contain non-blank characters." } + + // domainは指定された長さ以下である必要がある + require(domain.length <= characterLimit.general.domain) { + "domain must not exceed ${characterLimit.general.domain} characters." + } + + // screenNameは空文字以外を含める必要がある + require(screenName.isNotBlank()) { "screenName must contain non-blank characters." } + + // screenNameは指定された長さ以下である必要がある + val limitedScreenName = if (screenName.length >= characterLimit.account.name) { + logger.warn("screenName must not exceed ${characterLimit.account.name} characters.") + screenName.substring(0, characterLimit.account.name) + } else { + screenName + } + + // descriptionは指定された長さ以下である必要がある + val limitedDescription = if (description.length >= characterLimit.account.description) { + logger.warn("description must not exceed ${characterLimit.account.description} characters.") + description.substring(0, characterLimit.account.description) + } else { + description + } + + // ローカルユーザーはpasswordとprivateKeyをnullにしてはいけない + if (domain == applicationConfig.url.host) { + requireNotNull(password) { "password and privateKey must not be null for local users." } + requireNotNull(privateKey) { "password and privateKey must not be null for local users." } + } + + // urlは空文字以外を含める必要がある + require(url.isNotBlank()) { "url must contain non-blank characters." } + + // urlは指定された長さ以下である必要がある + require(url.length <= characterLimit.general.url) { + "url must not exceed ${characterLimit.general.url} characters." + } + + // inboxは空文字以外を含める必要がある + require(inbox.isNotBlank()) { "inbox must contain non-blank characters." } + + // inboxは指定された長さ以下である必要がある + require(inbox.length <= characterLimit.general.url) { + "inbox must not exceed ${characterLimit.general.url} characters." + } + + // outboxは空文字以外を含める必要がある + require(outbox.isNotBlank()) { "outbox must contain non-blank characters." } + + // outboxは指定された長さ以下である必要がある + require(outbox.length <= characterLimit.general.url) { + "outbox must not exceed ${characterLimit.general.url} characters." + } + + require(keyId.isNotBlank()) { + "keyId must contain non-blank characters." + } + + return User( + id = id, + name = limitedName, + domain = domain, + screenName = limitedScreenName, + description = limitedDescription, + password = password, + inbox = inbox, + outbox = outbox, + url = url, + publicKey = publicKey, + privateKey = privateKey, + createdAt = createdAt, + keyId = keyId, + followers = followers, + following = following + ) + } + } } diff --git a/src/main/kotlin/dev/usbharu/hideout/query/FollowerQueryServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/query/FollowerQueryServiceImpl.kt index 10be4068..75d27e7b 100644 --- a/src/main/kotlin/dev/usbharu/hideout/query/FollowerQueryServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/query/FollowerQueryServiceImpl.kt @@ -9,7 +9,7 @@ import org.springframework.stereotype.Repository import java.time.Instant @Repository -class FollowerQueryServiceImpl : FollowerQueryService { +class FollowerQueryServiceImpl(private val userBuilder: User.UserBuilder) : FollowerQueryService { override suspend fun findFollowersById(id: Long): List { val followers = Users.alias("FOLLOWERS") return Users.innerJoin( @@ -41,7 +41,7 @@ class FollowerQueryServiceImpl : FollowerQueryService { ) .select { Users.id eq id } .map { - User.of( + userBuilder.of( id = it[followers[Users.id]], name = it[followers[Users.name]], domain = it[followers[Users.domain]], @@ -92,7 +92,7 @@ class FollowerQueryServiceImpl : FollowerQueryService { ) .select { Users.name eq name and (Users.domain eq domain) } .map { - User.of( + userBuilder.of( id = it[followers[Users.id]], name = it[followers[Users.name]], domain = it[followers[Users.domain]], @@ -143,7 +143,7 @@ class FollowerQueryServiceImpl : FollowerQueryService { ) .select { followers[Users.id] eq id } .map { - User.of( + userBuilder.of( id = it[followers[Users.id]], name = it[followers[Users.name]], domain = it[followers[Users.domain]], @@ -194,7 +194,7 @@ class FollowerQueryServiceImpl : FollowerQueryService { ) .select { followers[Users.name] eq name and (followers[Users.domain] eq domain) } .map { - User.of( + userBuilder.of( id = it[followers[Users.id]], name = it[followers[Users.name]], domain = it[followers[Users.domain]], diff --git a/src/main/kotlin/dev/usbharu/hideout/service/user/UserServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/service/user/UserServiceImpl.kt index bb349ff3..6688ce48 100644 --- a/src/main/kotlin/dev/usbharu/hideout/service/user/UserServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/service/user/UserServiceImpl.kt @@ -21,6 +21,7 @@ class UserServiceImpl( private val apSendFollowService: APSendFollowService, private val userQueryService: UserQueryService, private val followerQueryService: FollowerQueryService, + private val userBuilder: User.UserBuilder, private val applicationConfig: ApplicationConfig ) : UserService { @@ -35,7 +36,7 @@ class UserServiceImpl( val hashedPassword = userAuthService.hash(user.password) val keyPair = userAuthService.generateKeyPair() val userUrl = "${applicationConfig.url}/users/${user.name}" - val userEntity = User.of( + val userEntity = userBuilder.of( id = nextId, name = user.name, domain = applicationConfig.url.host, @@ -57,7 +58,7 @@ class UserServiceImpl( override suspend fun createRemoteUser(user: RemoteUserCreateDto): User { val nextId = userRepository.nextId() - val userEntity = User.of( + val userEntity = userBuilder.of( id = nextId, name = user.name, domain = user.domain,