diff --git a/src/main/kotlin/dev/usbharu/hideout/config/Config.kt b/src/main/kotlin/dev/usbharu/hideout/config/Config.kt deleted file mode 100644 index 1623ff25..00000000 --- a/src/main/kotlin/dev/usbharu/hideout/config/Config.kt +++ /dev/null @@ -1,64 +0,0 @@ -package dev.usbharu.hideout.config - -import com.fasterxml.jackson.databind.ObjectMapper -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper - -@Deprecated("Config is deprecated") -object Config { - var configData: ConfigData = ConfigData() -} - -@Deprecated("Config is deprecated") -data class ConfigData( - val url: String = "", - val domain: String = url.substringAfter("://").substringBeforeLast(":"), - val objectMapper: ObjectMapper = jacksonObjectMapper(), - val characterLimit: CharacterLimit = CharacterLimit() -) - -@Deprecated("Config is deprecated") -data class CharacterLimit( - val general: General = General.of(), - val post: Post = Post(), - val account: Account = Account(), - val instance: Instance = Instance() -) { - @Deprecated("Config is deprecated") - data class General private constructor( - val url: Int, - val domain: Int, - val publicKey: Int, - val privateKey: Int - ) { - companion object { - @Suppress("FunctionMinLength") - fun of(url: Int? = null, domain: Int? = null, publicKey: Int? = null, privateKey: Int? = null): General { - return General( - url ?: 1000, - domain ?: 1000, - publicKey ?: 10000, - privateKey ?: 10000 - ) - } - } - } - - @Deprecated("Config is deprecated") - data class Post( - val text: Int = 3000, - val overview: Int = 3000 - ) - - @Deprecated("Config is deprecated") - data class Account( - val id: Int = 300, - val name: Int = 300, - val description: Int = 10000 - ) - - @Deprecated("Config is deprecated") - data class Instance( - val name: Int = 600, - val description: Int = 10000 - ) -} diff --git a/src/main/kotlin/dev/usbharu/hideout/config/SpringConfig.kt b/src/main/kotlin/dev/usbharu/hideout/config/SpringConfig.kt index 18ee3845..a7932b53 100644 --- a/src/main/kotlin/dev/usbharu/hideout/config/SpringConfig.kt +++ b/src/main/kotlin/dev/usbharu/hideout/config/SpringConfig.kt @@ -43,3 +43,35 @@ data class StorageConfig( val accessKey: String, val secretKey: String ) + +@ConfigurationProperties("hideout.character-limit") +data class CharacterLimit( + val general: General = General(), + val post: Post = Post(), + val account: Account = Account(), + val instance: Instance = Instance() +) { + + data class General( + val url: Int = 1000, + val domain: Int = 1000, + val publicKey: Int = 10000, + val privateKey: Int = 10000 + ) + + data class Post( + val text: Int = 3000, + val overview: Int = 3000 + ) + + data class Account( + val id: Int = 300, + val name: Int = 300, + val description: Int = 10000 + ) + + data class Instance( + val name: Int = 600, + val description: Int = 10000 + ) +} diff --git a/src/main/kotlin/dev/usbharu/hideout/domain/model/hideout/entity/Post.kt b/src/main/kotlin/dev/usbharu/hideout/domain/model/hideout/entity/Post.kt index afe45261..7bffa75e 100644 --- a/src/main/kotlin/dev/usbharu/hideout/domain/model/hideout/entity/Post.kt +++ b/src/main/kotlin/dev/usbharu/hideout/domain/model/hideout/entity/Post.kt @@ -1,6 +1,7 @@ package dev.usbharu.hideout.domain.model.hideout.entity -import dev.usbharu.hideout.config.Config +import dev.usbharu.hideout.config.CharacterLimit +import org.springframework.stereotype.Component data class Post private constructor( val id: Long, @@ -16,7 +17,9 @@ data class Post private constructor( val apId: String = url, val mediaIds: List = emptyList() ) { - companion object { + + @Component + class PostBuilder(private val characterLimit: CharacterLimit) { @Suppress("FunctionMinLength", "LongParameterList") fun of( id: Long, @@ -32,8 +35,6 @@ data class Post private constructor( apId: String = url, mediaIds: List = emptyList() ): Post { - val characterLimit = Config.configData.characterLimit - require(id >= 0) { "id must be greater than or equal to 0." } require(userId >= 0) { "userId must be greater than or equal to 0." } 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..302a3c1e 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,9 @@ package dev.usbharu.hideout.domain.model.hideout.entity -import dev.usbharu.hideout.config.Config +import dev.usbharu.hideout.config.ApplicationConfig +import dev.usbharu.hideout.config.CharacterLimit import org.slf4j.LoggerFactory +import org.springframework.stereotype.Component import java.time.Instant data class User private constructor( @@ -27,9 +29,9 @@ data class User private constructor( " privateKey=$privateKey, createdAt=$createdAt, keyId='$keyId', followers=$followers," + " following=$following)" - companion object { - - private val logger = LoggerFactory.getLogger(User::class.java) + @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( @@ -49,8 +51,6 @@ data class User private constructor( following: String? = null, followers: String? = null ): User { - val characterLimit = Config.configData.characterLimit - // idは0未満ではいけない require(id >= 0) { "id must be greater than or equal to 0." } @@ -93,7 +93,7 @@ data class User private constructor( } // ローカルユーザーはpasswordとprivateKeyをnullにしてはいけない - if (domain == Config.configData.domain) { + 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." } } 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/query/PostQueryServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/query/PostQueryServiceImpl.kt index dc3d3d9a..c6708dae 100644 --- a/src/main/kotlin/dev/usbharu/hideout/query/PostQueryServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/query/PostQueryServiceImpl.kt @@ -4,27 +4,32 @@ import dev.usbharu.hideout.domain.model.hideout.entity.Post import dev.usbharu.hideout.exception.FailedToGetResourcesException import dev.usbharu.hideout.repository.Posts import dev.usbharu.hideout.repository.PostsMedia -import dev.usbharu.hideout.repository.toPost +import dev.usbharu.hideout.repository.QueryMapper +import dev.usbharu.hideout.repository.ResultRowMapper import dev.usbharu.hideout.util.singleOr import org.jetbrains.exposed.sql.select import org.springframework.stereotype.Repository @Repository -class PostQueryServiceImpl : PostQueryService { +class PostQueryServiceImpl( + private val postResultRowMapper: ResultRowMapper, + private val postQueryMapper: QueryMapper +) : PostQueryService { override suspend fun findById(id: Long): Post = Posts.leftJoin(PostsMedia) .select { Posts.id eq id } - .singleOr { FailedToGetResourcesException("id: $id is duplicate or does not exist.", it) }.toPost() + .singleOr { FailedToGetResourcesException("id: $id is duplicate or does not exist.", it) } + .let(postResultRowMapper::map) override suspend fun findByUrl(url: String): Post = Posts.leftJoin(PostsMedia) .select { Posts.url eq url } - .toPost() + .let(postQueryMapper::map) .singleOr { FailedToGetResourcesException("url: $url is duplicate or does not exist.", it) } override suspend fun findByApId(string: String): Post = Posts.leftJoin(PostsMedia) .select { Posts.apId eq string } - .toPost() + .let(postQueryMapper::map) .singleOr { FailedToGetResourcesException("apId: $string is duplicate or does not exist.", it) } } diff --git a/src/main/kotlin/dev/usbharu/hideout/query/UserQueryServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/query/UserQueryServiceImpl.kt index 351a8303..3db693e1 100644 --- a/src/main/kotlin/dev/usbharu/hideout/query/UserQueryServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/query/UserQueryServiceImpl.kt @@ -2,8 +2,9 @@ package dev.usbharu.hideout.query import dev.usbharu.hideout.domain.model.hideout.entity.User import dev.usbharu.hideout.exception.FailedToGetResourcesException +import dev.usbharu.hideout.repository.QueryMapper +import dev.usbharu.hideout.repository.ResultRowMapper import dev.usbharu.hideout.repository.Users -import dev.usbharu.hideout.repository.toUser import dev.usbharu.hideout.util.singleOr import org.jetbrains.exposed.sql.and import org.jetbrains.exposed.sql.select @@ -12,17 +13,22 @@ import org.slf4j.LoggerFactory import org.springframework.stereotype.Repository @Repository -class UserQueryServiceImpl : UserQueryService { +class UserQueryServiceImpl( + private val userResultRowMapper: ResultRowMapper, + private val userQueryMapper: QueryMapper +) : UserQueryService { private val logger = LoggerFactory.getLogger(UserQueryServiceImpl::class.java) override suspend fun findAll(limit: Int, offset: Long): List = - Users.selectAll().limit(limit, offset).map { it.toUser() } + Users.selectAll().limit(limit, offset).let(userQueryMapper::map) override suspend fun findById(id: Long): User = Users.select { Users.id eq id } - .singleOr { FailedToGetResourcesException("id: $id is duplicate or does not exist.", it) }.toUser() + .singleOr { FailedToGetResourcesException("id: $id is duplicate or does not exist.", it) } + .let(userResultRowMapper::map) - override suspend fun findByName(name: String): List = Users.select { Users.name eq name }.map { it.toUser() } + override suspend fun findByName(name: String): List = + Users.select { Users.name eq name }.let(userQueryMapper::map) override suspend fun findByNameAndDomain(name: String, domain: String): User = Users @@ -30,17 +36,17 @@ class UserQueryServiceImpl : UserQueryService { .singleOr { FailedToGetResourcesException("name: $name,domain: $domain is duplicate or does not exist.", it) } - .toUser() + .let(userResultRowMapper::map) override suspend fun findByUrl(url: String): User { logger.trace("findByUrl url: $url") return Users.select { Users.url eq url } .singleOr { FailedToGetResourcesException("url: $url is duplicate or does not exist.", it) } - .toUser() + .let(userResultRowMapper::map) } override suspend fun findByIds(ids: List): List = - Users.select { Users.id inList ids }.map { it.toUser() } + Users.select { Users.id inList ids }.let(userQueryMapper::map) override suspend fun existByNameAndDomain(name: String, domain: String): Boolean = Users.select { Users.name eq name and (Users.domain eq domain) }.empty().not() @@ -48,6 +54,6 @@ class UserQueryServiceImpl : UserQueryService { override suspend fun findByKeyId(keyId: String): User { return Users.select { Users.keyId eq keyId } .singleOr { FailedToGetResourcesException("keyId: $keyId is duplicate or does not exist.", it) } - .toUser() + .let(userResultRowMapper::map) } } diff --git a/src/main/kotlin/dev/usbharu/hideout/query/activitypub/NoteQueryServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/query/activitypub/NoteQueryServiceImpl.kt index b6affcbb..55746226 100644 --- a/src/main/kotlin/dev/usbharu/hideout/query/activitypub/NoteQueryServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/query/activitypub/NoteQueryServiceImpl.kt @@ -13,14 +13,15 @@ import org.springframework.stereotype.Repository import java.time.Instant @Repository -class NoteQueryServiceImpl(private val postRepository: PostRepository) : NoteQueryService { +class NoteQueryServiceImpl(private val postRepository: PostRepository, private val postQueryMapper: QueryMapper) : + NoteQueryService { override suspend fun findById(id: Long): Pair { return Posts .leftJoin(Users) .leftJoin(PostsMedia) .leftJoin(Media) .select { Posts.id eq id } - .let { it.toNote() to it.toPost().first() } + .let { it.toNote() to postQueryMapper.map(it).first() } } private suspend fun ResultRow.toNote(mediaList: List): Note { diff --git a/src/main/kotlin/dev/usbharu/hideout/repository/PostQueryMapper.kt b/src/main/kotlin/dev/usbharu/hideout/repository/PostQueryMapper.kt new file mode 100644 index 00000000..f71d5492 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/repository/PostQueryMapper.kt @@ -0,0 +1,17 @@ +package dev.usbharu.hideout.repository + +import dev.usbharu.hideout.domain.model.hideout.entity.Post +import org.jetbrains.exposed.sql.Query +import org.springframework.stereotype.Component + +@Component +class PostQueryMapper(private val postResultRowMapper: ResultRowMapper) : QueryMapper { + override fun map(query: Query): List { + return query.groupBy { it[Posts.id] } + .map { it.value } + .map { + it.first().let(postResultRowMapper::map) + .copy(mediaIds = it.mapNotNull { it.getOrNull(PostsMedia.mediaId) }) + } + } +} diff --git a/src/main/kotlin/dev/usbharu/hideout/repository/PostRepositoryImpl.kt b/src/main/kotlin/dev/usbharu/hideout/repository/PostRepositoryImpl.kt index 9100422a..d0a8ee96 100644 --- a/src/main/kotlin/dev/usbharu/hideout/repository/PostRepositoryImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/repository/PostRepositoryImpl.kt @@ -1,7 +1,6 @@ package dev.usbharu.hideout.repository import dev.usbharu.hideout.domain.model.hideout.entity.Post -import dev.usbharu.hideout.domain.model.hideout.entity.Visibility import dev.usbharu.hideout.exception.FailedToGetResourcesException import dev.usbharu.hideout.service.core.IdGenerateService import org.jetbrains.exposed.sql.* @@ -9,7 +8,10 @@ import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.springframework.stereotype.Repository @Repository -class PostRepositoryImpl(private val idGenerateService: IdGenerateService) : PostRepository { +class PostRepositoryImpl( + private val idGenerateService: IdGenerateService, + private val postQueryMapper: QueryMapper +) : PostRepository { override suspend fun generateId(): Long = idGenerateService.generateId() @@ -65,7 +67,7 @@ class PostRepositoryImpl(private val idGenerateService: IdGenerateService) : Pos override suspend fun findById(id: Long): Post = Posts.innerJoin(PostsMedia, onColumn = { Posts.id }, otherColumn = { PostsMedia.postId }) .select { Posts.id eq id } - .toPost() + .let(postQueryMapper::map) .singleOrNull() ?: throw FailedToGetResourcesException("id: $id was not found.") @@ -94,25 +96,3 @@ object PostsMedia : Table() { val mediaId = long("media_id").references(Media.id, ReferenceOption.CASCADE, ReferenceOption.CASCADE) override val primaryKey = PrimaryKey(postId, mediaId) } - -fun ResultRow.toPost(): Post { - return Post.of( - id = this[Posts.id], - userId = this[Posts.userId], - overview = this[Posts.overview], - text = this[Posts.text], - createdAt = this[Posts.createdAt], - visibility = Visibility.values().first { visibility -> visibility.ordinal == this[Posts.visibility] }, - url = this[Posts.url], - repostId = this[Posts.repostId], - replyId = this[Posts.replyId], - sensitive = this[Posts.sensitive], - apId = this[Posts.apId], - ) -} - -fun Query.toPost(): List { - return this.groupBy { it[Posts.id] } - .map { it.value } - .map { it.first().toPost().copy(mediaIds = it.mapNotNull { it.getOrNull(PostsMedia.mediaId) }) } -} diff --git a/src/main/kotlin/dev/usbharu/hideout/repository/PostResultRowMapper.kt b/src/main/kotlin/dev/usbharu/hideout/repository/PostResultRowMapper.kt new file mode 100644 index 00000000..7949e401 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/repository/PostResultRowMapper.kt @@ -0,0 +1,25 @@ +package dev.usbharu.hideout.repository + +import dev.usbharu.hideout.domain.model.hideout.entity.Post +import dev.usbharu.hideout.domain.model.hideout.entity.Visibility +import org.jetbrains.exposed.sql.ResultRow +import org.springframework.stereotype.Component + +@Component +class PostResultRowMapper(private val postBuilder: Post.PostBuilder) : ResultRowMapper { + override fun map(resultRow: ResultRow): Post { + return postBuilder.of( + id = resultRow[Posts.id], + userId = resultRow[Posts.userId], + overview = resultRow[Posts.overview], + text = resultRow[Posts.text], + createdAt = resultRow[Posts.createdAt], + visibility = Visibility.values().first { visibility -> visibility.ordinal == resultRow[Posts.visibility] }, + url = resultRow[Posts.url], + repostId = resultRow[Posts.repostId], + replyId = resultRow[Posts.replyId], + sensitive = resultRow[Posts.sensitive], + apId = resultRow[Posts.apId], + ) + } +} diff --git a/src/main/kotlin/dev/usbharu/hideout/repository/QueryMapper.kt b/src/main/kotlin/dev/usbharu/hideout/repository/QueryMapper.kt new file mode 100644 index 00000000..b5b5fdad --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/repository/QueryMapper.kt @@ -0,0 +1,7 @@ +package dev.usbharu.hideout.repository + +import org.jetbrains.exposed.sql.Query + +interface QueryMapper { + fun map(query: Query): List +} diff --git a/src/main/kotlin/dev/usbharu/hideout/repository/ResultRowMapper.kt b/src/main/kotlin/dev/usbharu/hideout/repository/ResultRowMapper.kt new file mode 100644 index 00000000..e2ea4b3f --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/repository/ResultRowMapper.kt @@ -0,0 +1,7 @@ +package dev.usbharu.hideout.repository + +import org.jetbrains.exposed.sql.ResultRow + +interface ResultRowMapper { + fun map(resultRow: ResultRow): T +} diff --git a/src/main/kotlin/dev/usbharu/hideout/repository/UserQueryMapper.kt b/src/main/kotlin/dev/usbharu/hideout/repository/UserQueryMapper.kt new file mode 100644 index 00000000..62828e72 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/repository/UserQueryMapper.kt @@ -0,0 +1,12 @@ +package dev.usbharu.hideout.repository + +import dev.usbharu.hideout.domain.model.hideout.entity.User +import org.jetbrains.exposed.sql.Query +import org.springframework.stereotype.Component + +@Component +class UserQueryMapper(private val userResultRowMapper: ResultRowMapper) : QueryMapper { + override fun map(query: Query): List { + return query.map(userResultRowMapper::map) + } +} diff --git a/src/main/kotlin/dev/usbharu/hideout/repository/UserRepositoryImpl.kt b/src/main/kotlin/dev/usbharu/hideout/repository/UserRepositoryImpl.kt index a6955a5d..0a1a9958 100644 --- a/src/main/kotlin/dev/usbharu/hideout/repository/UserRepositoryImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/repository/UserRepositoryImpl.kt @@ -1,16 +1,17 @@ package dev.usbharu.hideout.repository -import dev.usbharu.hideout.config.Config import dev.usbharu.hideout.domain.model.hideout.entity.User import dev.usbharu.hideout.service.core.IdGenerateService import org.jetbrains.exposed.dao.id.LongIdTable import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.springframework.stereotype.Repository -import java.time.Instant @Repository -class UserRepositoryImpl(private val idGenerateService: IdGenerateService) : +class UserRepositoryImpl( + private val idGenerateService: IdGenerateService, + private val userResultRowMapper: ResultRowMapper +) : UserRepository { override suspend fun save(user: User): User { @@ -55,9 +56,7 @@ class UserRepositoryImpl(private val idGenerateService: IdGenerateService) : } override suspend fun findById(id: Long): User? { - return Users.select { Users.id eq id }.map { - it.toUser() - }.singleOrNull() + return Users.select { Users.id eq id }.singleOrNull()?.let(userResultRowMapper::map) } override suspend fun deleteFollowRequest(id: Long, follower: Long) { @@ -78,26 +77,26 @@ class UserRepositoryImpl(private val idGenerateService: IdGenerateService) : object Users : Table("users") { val id: Column = long("id") - val name: Column = varchar("name", length = Config.configData.characterLimit.account.id) - val domain: Column = varchar("domain", length = Config.configData.characterLimit.general.domain) - val screenName: Column = varchar("screen_name", length = Config.configData.characterLimit.account.name) + val name: Column = varchar("name", length = 300) + val domain: Column = varchar("domain", length = 1000) + val screenName: Column = varchar("screen_name", length = 300) val description: Column = varchar( "description", - length = Config.configData.characterLimit.account.description + length = 10000 ) val password: Column = varchar("password", length = 255).nullable() - val inbox: Column = varchar("inbox", length = Config.configData.characterLimit.general.url).uniqueIndex() - val outbox: Column = varchar("outbox", length = Config.configData.characterLimit.general.url).uniqueIndex() - val url: Column = varchar("url", length = Config.configData.characterLimit.general.url).uniqueIndex() - val publicKey: Column = varchar("public_key", length = Config.configData.characterLimit.general.publicKey) + val inbox: Column = varchar("inbox", length = 1000).uniqueIndex() + val outbox: Column = varchar("outbox", length = 1000).uniqueIndex() + val url: Column = varchar("url", length = 1000).uniqueIndex() + val publicKey: Column = varchar("public_key", length = 10000) val privateKey: Column = varchar( "private_key", - length = Config.configData.characterLimit.general.privateKey + length = 10000 ).nullable() val createdAt: Column = long("created_at") - val keyId = varchar("key_id", length = Config.configData.characterLimit.general.url) - val following = varchar("following", length = Config.configData.characterLimit.general.url).nullable() - val followers = varchar("followers", length = Config.configData.characterLimit.general.url).nullable() + val keyId = varchar("key_id", length = 1000) + val following = varchar("following", length = 1000).nullable() + val followers = varchar("followers", length = 1000).nullable() override val primaryKey: PrimaryKey = PrimaryKey(id) @@ -106,26 +105,6 @@ object Users : Table("users") { } } -fun ResultRow.toUser(): User { - return User.of( - id = this[Users.id], - name = this[Users.name], - domain = this[Users.domain], - screenName = this[Users.screenName], - description = this[Users.description], - password = this[Users.password], - inbox = this[Users.inbox], - outbox = this[Users.outbox], - url = this[Users.url], - publicKey = this[Users.publicKey], - privateKey = this[Users.privateKey], - createdAt = Instant.ofEpochMilli((this[Users.createdAt])), - keyId = this[Users.keyId], - followers = this[Users.followers], - following = this[Users.following] - ) -} - object UsersFollowers : LongIdTable("users_followers") { val userId: Column = long("user_id").references(Users.id).index() val followerId: Column = long("follower_id").references(Users.id) diff --git a/src/main/kotlin/dev/usbharu/hideout/repository/UserResultRowMapper.kt b/src/main/kotlin/dev/usbharu/hideout/repository/UserResultRowMapper.kt new file mode 100644 index 00000000..8470a2f6 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/repository/UserResultRowMapper.kt @@ -0,0 +1,29 @@ +package dev.usbharu.hideout.repository + +import dev.usbharu.hideout.domain.model.hideout.entity.User +import org.jetbrains.exposed.sql.ResultRow +import org.springframework.stereotype.Component +import java.time.Instant + +@Component +class UserResultRowMapper(private val userBuilder: User.UserBuilder) : ResultRowMapper { + override fun map(resultRow: ResultRow): User { + return userBuilder.of( + id = resultRow[Users.id], + name = resultRow[Users.name], + domain = resultRow[Users.domain], + screenName = resultRow[Users.screenName], + description = resultRow[Users.description], + password = resultRow[Users.password], + inbox = resultRow[Users.inbox], + outbox = resultRow[Users.outbox], + url = resultRow[Users.url], + publicKey = resultRow[Users.publicKey], + privateKey = resultRow[Users.privateKey], + createdAt = Instant.ofEpochMilli((resultRow[Users.createdAt])), + keyId = resultRow[Users.keyId], + followers = resultRow[Users.followers], + following = resultRow[Users.following] + ) + } +} diff --git a/src/main/kotlin/dev/usbharu/hideout/service/ap/APNoteService.kt b/src/main/kotlin/dev/usbharu/hideout/service/ap/APNoteService.kt index 8a7b1a95..26a6306b 100644 --- a/src/main/kotlin/dev/usbharu/hideout/service/ap/APNoteService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/service/ap/APNoteService.kt @@ -69,6 +69,7 @@ class APNoteServiceImpl( private val postService: PostService, private val apResourceResolveService: APResourceResolveService, private val apRequestService: APRequestService, + private val postBuilder: Post.PostBuilder, private val transaction: Transaction ) : APNoteService, PostCreateInterceptor { @@ -224,7 +225,7 @@ class APNoteServiceImpl( // TODO: リモートのメディア処理を追加 postService.createRemote( - Post.of( + postBuilder.of( id = postRepository.generateId(), userId = person.second.id, text = note.content.orEmpty(), diff --git a/src/main/kotlin/dev/usbharu/hideout/service/post/PostServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/service/post/PostServiceImpl.kt index ac5e54c6..c98ee233 100644 --- a/src/main/kotlin/dev/usbharu/hideout/service/post/PostServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/service/post/PostServiceImpl.kt @@ -16,7 +16,8 @@ class PostServiceImpl( private val postRepository: PostRepository, private val userRepository: UserRepository, private val timelineService: TimelineService, - private val postQueryService: PostQueryService + private val postQueryService: PostQueryService, + private val postBuilder: Post.PostBuilder ) : PostService { private val interceptors = Collections.synchronizedList(mutableListOf()) @@ -45,7 +46,7 @@ class PostServiceImpl( private suspend fun internalCreate(post: PostCreateDto, isLocal: Boolean): Post { val user = userRepository.findById(post.userId) ?: throw UserNotFoundException("${post.userId} was not found") val id = postRepository.generateId() - val createPost = Post.of( + val createPost = postBuilder.of( id = id, userId = post.userId, overview = post.overview, 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, diff --git a/src/test/kotlin/dev/usbharu/hideout/service/ap/APNoteServiceImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/service/ap/APNoteServiceImplTest.kt index 2543c8e9..628a8eff 100644 --- a/src/test/kotlin/dev/usbharu/hideout/service/ap/APNoteServiceImplTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/service/ap/APNoteServiceImplTest.kt @@ -3,8 +3,8 @@ package dev.usbharu.hideout.service.ap -import dev.usbharu.hideout.config.Config -import dev.usbharu.hideout.config.ConfigData +import dev.usbharu.hideout.config.ApplicationConfig +import dev.usbharu.hideout.config.CharacterLimit import dev.usbharu.hideout.domain.model.hideout.entity.Post import dev.usbharu.hideout.domain.model.hideout.entity.User import dev.usbharu.hideout.domain.model.hideout.entity.Visibility @@ -23,21 +23,28 @@ import org.junit.jupiter.api.Test import org.mockito.Mockito.anyLong import org.mockito.Mockito.eq import org.mockito.kotlin.* -import utils.JsonObjectMapper import utils.JsonObjectMapper.objectMapper import utils.TestApplicationConfig.testApplicationConfig +import java.net.URL import java.time.Instant import kotlin.test.assertEquals class APNoteServiceImplTest { + + val userBuilder = User.UserBuilder(CharacterLimit(), ApplicationConfig(URL("https://example.com"))) + val postBuilder = Post.PostBuilder(CharacterLimit()) + @Test fun `createPost 新しい投稿`() { val mediaQueryService = mock { onBlocking { findByPostId(anyLong()) } doReturn emptyList() } + + + runTest { val followers = listOf( - User.of( + userBuilder.of( 2L, "follower", "follower.example.com", @@ -51,7 +58,7 @@ class APNoteServiceImplTest { createdAt = Instant.now(), keyId = "a" ), - User.of( + userBuilder.of( 3L, "follower2", "follower2.example.com", @@ -67,7 +74,7 @@ class APNoteServiceImplTest { ) ) val userQueryService = mock { - onBlocking { findById(eq(1L)) } doReturn User.of( + onBlocking { findById(eq(1L)) } doReturn userBuilder.of( 1L, "test", "example.com", @@ -101,9 +108,10 @@ class APNoteServiceImplTest { postService = mock(), apResourceResolveService = mock(), apRequestService = mock(), - transaction = mock() + transaction = mock(), + postBuilder = postBuilder ) - val postEntity = Post.of( + val postEntity = postBuilder.of( 1L, 1L, null, @@ -123,7 +131,7 @@ class APNoteServiceImplTest { val mediaQueryService = mock { onBlocking { findByPostId(anyLong()) } doReturn emptyList() } - Config.configData = ConfigData(objectMapper = JsonObjectMapper.objectMapper) + val httpClient = HttpClient( MockEngine { httpRequestData -> assertEquals("https://follower.example.com/inbox", httpRequestData.url.toString()) @@ -143,7 +151,8 @@ class APNoteServiceImplTest { postService = mock(), apResourceResolveService = mock(), apRequestService = mock(), - transaction = mock() + transaction = mock(), + postBuilder = postBuilder ) activityPubNoteService.createNoteJob( JobProps( diff --git a/src/test/kotlin/dev/usbharu/hideout/service/ap/APReceiveFollowServiceImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/service/ap/APReceiveFollowServiceImplTest.kt index d1d51fa7..38c266e1 100644 --- a/src/test/kotlin/dev/usbharu/hideout/service/ap/APReceiveFollowServiceImplTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/service/ap/APReceiveFollowServiceImplTest.kt @@ -3,12 +3,13 @@ package dev.usbharu.hideout.service.ap -import dev.usbharu.hideout.config.Config -import dev.usbharu.hideout.config.ConfigData +import dev.usbharu.hideout.config.ApplicationConfig +import dev.usbharu.hideout.config.CharacterLimit import dev.usbharu.hideout.domain.model.ap.Follow import dev.usbharu.hideout.domain.model.ap.Image import dev.usbharu.hideout.domain.model.ap.Key import dev.usbharu.hideout.domain.model.ap.Person +import dev.usbharu.hideout.domain.model.hideout.entity.Post import dev.usbharu.hideout.domain.model.hideout.entity.User import dev.usbharu.hideout.domain.model.job.ReceiveFollowJob import dev.usbharu.hideout.query.UserQueryService @@ -23,12 +24,16 @@ import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test import org.mockito.ArgumentMatchers.anyString import org.mockito.kotlin.* -import utils.JsonObjectMapper import utils.JsonObjectMapper.objectMapper import utils.TestTransaction +import java.net.URL import java.time.Instant class APReceiveFollowServiceImplTest { + + val userBuilder = User.UserBuilder(CharacterLimit(), ApplicationConfig(URL("https://example.com"))) + val postBuilder = Post.PostBuilder(CharacterLimit()) + @Test fun `receiveFollow フォロー受付処理`() = runTest { val jobQueueParentService = mock { @@ -80,7 +85,6 @@ class APReceiveFollowServiceImplTest { @Test fun `receiveFollowJob フォロー受付処理のJob`() = runTest { - Config.configData = ConfigData(objectMapper = JsonObjectMapper.objectMapper) val person = Person( type = emptyList(), name = "follower", @@ -112,7 +116,7 @@ class APReceiveFollowServiceImplTest { } val userQueryService = mock { onBlocking { findByUrl(eq("https://example.com")) } doReturn - User.of( + userBuilder.of( id = 1L, name = "test", domain = "example.com", @@ -122,11 +126,13 @@ class APReceiveFollowServiceImplTest { outbox = "https://example.com/outbox", url = "https://example.com", publicKey = "", + password = "a", + privateKey = "a", createdAt = Instant.now(), keyId = "a" ) onBlocking { findByUrl(eq("https://follower.example.com")) } doReturn - User.of( + userBuilder.of( id = 2L, name = "follower", domain = "follower.example.com", diff --git a/src/test/kotlin/dev/usbharu/hideout/service/ap/resource/APResourceResolveServiceImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/service/ap/resource/APResourceResolveServiceImplTest.kt index 344a07d8..87f5a553 100644 --- a/src/test/kotlin/dev/usbharu/hideout/service/ap/resource/APResourceResolveServiceImplTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/service/ap/resource/APResourceResolveServiceImplTest.kt @@ -1,6 +1,9 @@ package dev.usbharu.hideout.service.ap.resource +import dev.usbharu.hideout.config.ApplicationConfig +import dev.usbharu.hideout.config.CharacterLimit import dev.usbharu.hideout.domain.model.ap.Object +import dev.usbharu.hideout.domain.model.hideout.entity.Post import dev.usbharu.hideout.domain.model.hideout.entity.User import dev.usbharu.hideout.repository.UserRepository import io.ktor.client.* @@ -16,6 +19,7 @@ import org.mockito.kotlin.any import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock import org.mockito.kotlin.whenever +import java.net.URL import java.time.Instant import kotlin.test.assertEquals @@ -23,6 +27,9 @@ import kotlin.test.assertEquals @Disabled class APResourceResolveServiceImplTest { + val userBuilder = User.UserBuilder(CharacterLimit(), ApplicationConfig(URL("https://example.com"))) + val postBuilder = Post.PostBuilder(CharacterLimit()) + @Test fun `単純な一回のリクエスト`() = runTest { @@ -36,7 +43,7 @@ class APResourceResolveServiceImplTest { val userRepository = mock() whenever(userRepository.findById(any())).doReturn( - User.of( + userBuilder.of( 2L, "follower", "follower.example.com", @@ -72,7 +79,7 @@ class APResourceResolveServiceImplTest { val userRepository = mock() whenever(userRepository.findById(any())).doReturn( - User.of( + userBuilder.of( 2L, "follower", "follower.example.com", @@ -111,7 +118,7 @@ class APResourceResolveServiceImplTest { val userRepository = mock() whenever(userRepository.findById(any())).doReturn( - User.of( + userBuilder.of( 2L, "follower", "follower.example.com", @@ -161,7 +168,7 @@ class APResourceResolveServiceImplTest { val userRepository = mock() whenever(userRepository.findById(any())).doReturn( - User.of( + userBuilder.of( 2L, "follower", "follower.example.com", diff --git a/src/test/kotlin/dev/usbharu/hideout/service/user/UserServiceTest.kt b/src/test/kotlin/dev/usbharu/hideout/service/user/UserServiceTest.kt index 26e4ebf8..663d7f40 100644 --- a/src/test/kotlin/dev/usbharu/hideout/service/user/UserServiceTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/service/user/UserServiceTest.kt @@ -2,10 +2,12 @@ package dev.usbharu.hideout.service.user -import dev.usbharu.hideout.config.Config -import dev.usbharu.hideout.config.ConfigData +import dev.usbharu.hideout.config.ApplicationConfig +import dev.usbharu.hideout.config.CharacterLimit import dev.usbharu.hideout.domain.model.hideout.dto.RemoteUserCreateDto import dev.usbharu.hideout.domain.model.hideout.dto.UserCreateDto +import dev.usbharu.hideout.domain.model.hideout.entity.Post +import dev.usbharu.hideout.domain.model.hideout.entity.User import dev.usbharu.hideout.repository.UserRepository import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest @@ -13,14 +15,17 @@ import org.junit.jupiter.api.Test import org.mockito.ArgumentMatchers.anyString import org.mockito.kotlin.* import utils.TestApplicationConfig.testApplicationConfig +import java.net.URL import java.security.KeyPairGenerator import kotlin.test.assertEquals import kotlin.test.assertNull class UserServiceTest { + val userBuilder = User.UserBuilder(CharacterLimit(), ApplicationConfig(URL("https://example.com"))) + val postBuilder = Post.PostBuilder(CharacterLimit()) @Test fun `createLocalUser ローカルユーザーを作成できる`() = runTest { - Config.configData = ConfigData(domain = "example.com", url = "https://example.com") + val userRepository = mock { onBlocking { nextId() } doReturn 110001L } @@ -30,7 +35,15 @@ class UserServiceTest { onBlocking { generateKeyPair() } doReturn generateKeyPair } val userService = - UserServiceImpl(userRepository, userAuthService, mock(), mock(), mock(), testApplicationConfig) + UserServiceImpl( + userRepository, + userAuthService, + mock(), + mock(), + mock(), + userBuilder, + testApplicationConfig, + ) userService.createLocalUser(UserCreateDto("test", "testUser", "XXXXXXXXXXXXX", "test")) verify(userRepository, times(1)).save(any()) argumentCaptor { @@ -51,20 +64,20 @@ class UserServiceTest { @Test fun `createRemoteUser リモートユーザーを作成できる`() = runTest { - Config.configData = ConfigData(domain = "remote.example.com", url = "https://remote.example.com") val userRepository = mock { onBlocking { nextId() } doReturn 113345L } - val userService = UserServiceImpl(userRepository, mock(), mock(), mock(), mock(), testApplicationConfig) + val userService = + UserServiceImpl(userRepository, mock(), mock(), mock(), mock(), userBuilder, testApplicationConfig) val user = RemoteUserCreateDto( name = "test", - domain = "example.com", + domain = "remote.example.com", screenName = "testUser", description = "test user", - inbox = "https://example.com/inbox", - outbox = "https://example.com/outbox", - url = "https://example.com", + inbox = "https://remote.example.com/inbox", + outbox = "https://remote.example.com/outbox", + url = "https://remote.example.com", publicKey = "-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----", keyId = "a", following = "", @@ -79,10 +92,10 @@ class UserServiceTest { assertEquals("test user", firstValue.description) assertNull(firstValue.password) assertEquals(113345L, firstValue.id) - assertEquals("https://example.com", firstValue.url) - assertEquals("example.com", firstValue.domain) - assertEquals("https://example.com/inbox", firstValue.inbox) - assertEquals("https://example.com/outbox", firstValue.outbox) + assertEquals("https://remote.example.com", firstValue.url) + assertEquals("remote.example.com", firstValue.domain) + assertEquals("https://remote.example.com/inbox", firstValue.inbox) + assertEquals("https://remote.example.com/outbox", firstValue.outbox) assertEquals("-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----", firstValue.publicKey) assertNull(firstValue.privateKey) }