mirror of https://github.com/usbharu/Hideout.git
feat: 投稿のAPIを作成
This commit is contained in:
parent
e1f10ab064
commit
7fad9fcfa6
|
@ -1,7 +1,6 @@
|
|||
package dev.usbharu.hideout.repository
|
||||
|
||||
import dev.usbharu.hideout.domain.model.hideout.entity.Post
|
||||
import dev.usbharu.hideout.repository.toPost
|
||||
import dev.usbharu.hideout.service.IdGenerateService
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import org.jetbrains.exposed.sql.*
|
||||
|
|
|
@ -2,24 +2,37 @@ package dev.usbharu.hideout.routing.api.internal.v1
|
|||
|
||||
import dev.usbharu.hideout.domain.model.hideout.dto.PostCreateDto
|
||||
import dev.usbharu.hideout.domain.model.hideout.form.Post
|
||||
import dev.usbharu.hideout.plugins.TOKEN_AUTH
|
||||
import dev.usbharu.hideout.service.IPostService
|
||||
import dev.usbharu.hideout.util.InstantParseUtil
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.auth.*
|
||||
import io.ktor.server.auth.jwt.*
|
||||
import io.ktor.server.request.*
|
||||
import io.ktor.server.routing.*
|
||||
|
||||
fun Route.posts(postService: IPostService){
|
||||
route("/posts"){
|
||||
authenticate(){
|
||||
post{
|
||||
fun Route.posts(postService: IPostService) {
|
||||
route("/posts") {
|
||||
authenticate(TOKEN_AUTH) {
|
||||
post {
|
||||
val principal = call.principal<JWTPrincipal>() ?: throw RuntimeException("no principal")
|
||||
val username = principal.payload.getClaim("username").asString()
|
||||
val username = principal.payload.getClaim("uid").asString()
|
||||
|
||||
val receive = call.receive<Post>()
|
||||
val postCreateDto = PostCreateDto(receive.text,username)
|
||||
val postCreateDto = PostCreateDto(receive.text, username)
|
||||
postService.create(postCreateDto)
|
||||
}
|
||||
}
|
||||
authenticate(TOKEN_AUTH, optional = true) {
|
||||
get {
|
||||
val userId = call.principal<JWTPrincipal>()?.payload?.getClaim("uid")?.asLong()
|
||||
val since = InstantParseUtil.parse(call.request.queryParameters["since"])
|
||||
val until = InstantParseUtil.parse(call.request.queryParameters["until"])
|
||||
val minId = call.request.queryParameters["minId"]?.toLong()
|
||||
val maxId = call.request.queryParameters["maxId"]?.toLong()
|
||||
val limit = call.request.queryParameters["limit"]?.toInt()
|
||||
postService.findAll(since, until, minId, maxId, limit, userId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,8 +2,20 @@ package dev.usbharu.hideout.service
|
|||
|
||||
import dev.usbharu.hideout.domain.model.hideout.dto.PostCreateDto
|
||||
import dev.usbharu.hideout.domain.model.hideout.entity.Post
|
||||
import java.time.Instant
|
||||
|
||||
interface IPostService {
|
||||
suspend fun create(post: Post)
|
||||
suspend fun create(post: PostCreateDto)
|
||||
suspend fun findAll(
|
||||
since: Instant? = null,
|
||||
until: Instant? = null,
|
||||
minId: Long? = null,
|
||||
maxId: Long? = null,
|
||||
limit: Int? = 10,
|
||||
userId: Long? = null
|
||||
): List<Post>
|
||||
|
||||
suspend fun findById(id: String): Post
|
||||
suspend fun delete(id: String)
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ class JwtServiceImpl(
|
|||
.withAudience("${Config.configData.url}/users/${user.name}")
|
||||
.withIssuer(Config.configData.url)
|
||||
.withKeyId(keyId.await().toString())
|
||||
.withClaim("username", user.name)
|
||||
.withClaim("uid", user.id)
|
||||
.withExpiresAt(now.plus(30, ChronoUnit.MINUTES))
|
||||
.sign(Algorithm.RSA256(publicKey.await(), privateKey.await()))
|
||||
|
||||
|
|
|
@ -3,8 +3,14 @@ package dev.usbharu.hideout.service.impl
|
|||
import dev.usbharu.hideout.domain.model.hideout.dto.PostCreateDto
|
||||
import dev.usbharu.hideout.domain.model.hideout.entity.Post
|
||||
import dev.usbharu.hideout.repository.IPostRepository
|
||||
import dev.usbharu.hideout.repository.Posts
|
||||
import dev.usbharu.hideout.repository.UsersFollowers
|
||||
import dev.usbharu.hideout.repository.toPost
|
||||
import dev.usbharu.hideout.service.IPostService
|
||||
import dev.usbharu.hideout.service.activitypub.ActivityPubNoteService
|
||||
import org.jetbrains.exposed.sql.orWhere
|
||||
import org.jetbrains.exposed.sql.select
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
import org.koin.core.annotation.Single
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.time.Instant
|
||||
|
@ -34,4 +40,35 @@ class PostService(
|
|||
)
|
||||
postRepository.save(postEntity)
|
||||
}
|
||||
|
||||
override suspend fun findAll(
|
||||
since: Instant?,
|
||||
until: Instant?,
|
||||
minId: Long?,
|
||||
maxId: Long?,
|
||||
limit: Int?,
|
||||
userId: Long?
|
||||
): List<Post> {
|
||||
return transaction {
|
||||
val select = Posts.select {
|
||||
Posts.visibility.eq(0)
|
||||
}
|
||||
if (userId != null) {
|
||||
select.orWhere {
|
||||
Posts.userId.inSubQuery(
|
||||
UsersFollowers.slice(UsersFollowers.userId).select(UsersFollowers.followerId eq userId)
|
||||
)
|
||||
}
|
||||
}
|
||||
select.map { it.toPost() }
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun findById(id: String): Post {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override suspend fun delete(id: String) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
package dev.usbharu.hideout.util
|
||||
|
||||
import java.time.Instant
|
||||
import java.time.format.DateTimeParseException
|
||||
|
||||
object InstantParseUtil {
|
||||
fun parse(str: String?): Instant? {
|
||||
return try {
|
||||
Instant.ofEpochMilli(str?.toLong() ?: return null)
|
||||
} catch (e: NumberFormatException) {
|
||||
try {
|
||||
Instant.parse(str)
|
||||
} catch (e: DateTimeParseException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,161 @@
|
|||
@file:OptIn(ExperimentalCoroutinesApi::class)
|
||||
|
||||
package dev.usbharu.hideout.service.impl
|
||||
|
||||
import dev.usbharu.hideout.domain.model.hideout.entity.Post
|
||||
import dev.usbharu.hideout.repository.Posts
|
||||
import dev.usbharu.hideout.repository.UsersFollowers
|
||||
import dev.usbharu.hideout.service.TwitterSnowflakeIdGenerateService
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.jetbrains.exposed.sql.Database
|
||||
import org.jetbrains.exposed.sql.SchemaUtils
|
||||
import org.jetbrains.exposed.sql.batchInsert
|
||||
import org.jetbrains.exposed.sql.insert
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.mockito.kotlin.mock
|
||||
import java.time.Instant
|
||||
import kotlin.test.assertContentEquals
|
||||
|
||||
class PostServiceTest {
|
||||
|
||||
lateinit var db: Database
|
||||
|
||||
@BeforeEach
|
||||
fun setUp() {
|
||||
db = Database.connect("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1", driver = "org.h2.Driver")
|
||||
transaction(db) {
|
||||
SchemaUtils.create(Posts)
|
||||
connection.prepareStatement("SET REFERENTIAL_INTEGRITY FALSE", false).executeUpdate()
|
||||
}
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
fun tearDown() {
|
||||
transaction(db) {
|
||||
SchemaUtils.drop(Posts)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `findAll 公開投稿を取得できる`() = runTest {
|
||||
val postService = PostService(mock(), mock(), mock())
|
||||
|
||||
suspend fun createPost(userId: Long, text: String, visibility: Int = 0): Post {
|
||||
return Post(
|
||||
TwitterSnowflakeIdGenerateService.generateId(),
|
||||
userId,
|
||||
null,
|
||||
text,
|
||||
Instant.now().toEpochMilli(),
|
||||
visibility,
|
||||
"https://example.com"
|
||||
)
|
||||
}
|
||||
|
||||
val userA: Long = 1
|
||||
val userB: Long = 2
|
||||
|
||||
val posts = listOf(
|
||||
createPost(userA, "hello"),
|
||||
createPost(userA, "hello1"),
|
||||
createPost(userA, "hello2"),
|
||||
createPost(userA, "hello3"),
|
||||
createPost(userA, "hello4"),
|
||||
createPost(userA, "hello5"),
|
||||
createPost(userA, "hello6"),
|
||||
createPost(userB, "good bay", 1),
|
||||
createPost(userB, "good bay1", 1),
|
||||
createPost(userB, "good bay2", 1),
|
||||
createPost(userB, "good bay3", 1),
|
||||
createPost(userB, "good bay4", 1),
|
||||
createPost(userB, "good bay5", 1),
|
||||
createPost(userB, "good bay6", 1),
|
||||
)
|
||||
|
||||
transaction {
|
||||
Posts.batchInsert(posts) {
|
||||
this[Posts.id] = it.id
|
||||
this[Posts.userId] = it.userId
|
||||
this[Posts.overview] = it.overview
|
||||
this[Posts.text] = it.text
|
||||
this[Posts.createdAt] = it.createdAt
|
||||
this[Posts.visibility] = it.visibility
|
||||
this[Posts.url] = it.url
|
||||
this[Posts.replyId] = it.replyId
|
||||
this[Posts.repostId] = it.repostId
|
||||
}
|
||||
}
|
||||
|
||||
val expect = posts.filter { it.visibility == 0 }
|
||||
|
||||
val actual = postService.findAll()
|
||||
assertContentEquals(expect, actual)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `findAll フォロー限定投稿を見れる`() = runTest {
|
||||
val postService = PostService(mock(), mock(), mock())
|
||||
|
||||
suspend fun createPost(userId: Long, text: String, visibility: Int = 0): Post {
|
||||
return Post(
|
||||
TwitterSnowflakeIdGenerateService.generateId(),
|
||||
userId,
|
||||
null,
|
||||
text,
|
||||
Instant.now().toEpochMilli(),
|
||||
visibility,
|
||||
"https://example.com"
|
||||
)
|
||||
}
|
||||
|
||||
val userA: Long = 1
|
||||
val userB: Long = 2
|
||||
|
||||
val posts = listOf(
|
||||
createPost(userA, "hello"),
|
||||
createPost(userA, "hello1"),
|
||||
createPost(userA, "hello2"),
|
||||
createPost(userA, "hello3"),
|
||||
createPost(userA, "hello4"),
|
||||
createPost(userA, "hello5"),
|
||||
createPost(userA, "hello6"),
|
||||
createPost(userB, "good bay", 1),
|
||||
createPost(userB, "good bay1", 1),
|
||||
createPost(userB, "good bay2", 1),
|
||||
createPost(userB, "good bay3", 1),
|
||||
createPost(userB, "good bay4", 1),
|
||||
createPost(userB, "good bay5", 1),
|
||||
createPost(userB, "good bay6", 1),
|
||||
)
|
||||
|
||||
transaction(db) {
|
||||
SchemaUtils.create(UsersFollowers)
|
||||
}
|
||||
|
||||
transaction {
|
||||
Posts.batchInsert(posts) {
|
||||
this[Posts.id] = it.id
|
||||
this[Posts.userId] = it.userId
|
||||
this[Posts.overview] = it.overview
|
||||
this[Posts.text] = it.text
|
||||
this[Posts.createdAt] = it.createdAt
|
||||
this[Posts.visibility] = it.visibility
|
||||
this[Posts.url] = it.url
|
||||
this[Posts.replyId] = it.replyId
|
||||
this[Posts.repostId] = it.repostId
|
||||
}
|
||||
UsersFollowers.insert {
|
||||
it[id] = 100L
|
||||
it[userId] = userB
|
||||
it[followerId] = userA
|
||||
}
|
||||
}
|
||||
|
||||
val actual = postService.findAll(userId = userA)
|
||||
assertContentEquals(posts, actual)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue