Skip to content

Commit

Permalink
#21 Added users GET/POST
Browse files Browse the repository at this point in the history
  • Loading branch information
vityaman committed Apr 10, 2024
1 parent d545ab1 commit 6421489
Show file tree
Hide file tree
Showing 9 changed files with 215 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package ru.itmo.lms.botalka.api.http.endpoint

import org.springframework.beans.factory.annotation.Autowired
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.RestController
import ru.itmo.lms.botalka.api.http.UserDraftMessage
import ru.itmo.lms.botalka.api.http.UserMessage
import ru.itmo.lms.botalka.api.http.apis.UserApi
import ru.itmo.lms.botalka.api.http.message.toMessage
import ru.itmo.lms.botalka.api.http.message.toModel
import ru.itmo.lms.botalka.domain.model.User
import ru.itmo.lms.botalka.logic.UserService

@RestController
class UserHttpApi(
@Autowired private val userService: UserService,
) : UserApi {
override suspend fun userIdGet(id: Int): ResponseEntity<UserMessage> {
val userId = User.Id(id)
val user = userService.getById(userId)
return ResponseEntity.ok(user.toMessage())
}

override suspend fun userPost(
userDraftMessage: UserDraftMessage,
): ResponseEntity<UserMessage> {
val draft = userDraftMessage.toModel()
val user = userService.create(draft)
return ResponseEntity.ok(user.toMessage())
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package ru.itmo.lms.botalka.api.http.message

import ru.itmo.lms.botalka.api.http.UserDraftMessage
import ru.itmo.lms.botalka.api.http.UserMessage
import ru.itmo.lms.botalka.api.http.UserRoleMessage
import ru.itmo.lms.botalka.domain.model.User

fun User.Role.toMessage(): UserRoleMessage =
when (this) {
User.Role.TEACHER -> UserRoleMessage.TEACHER
User.Role.STUDENT -> UserRoleMessage.STUDENT
}

fun User.toMessage(): UserMessage =
UserMessage(
id = this.id.number,
alias = this.alias.text,
roles = this.roles.map { it.toMessage() },
)

fun User.Draft.toMessage(): UserDraftMessage =
UserDraftMessage(
alias = this.alias.text,
)

fun UserDraftMessage.toModel(): User.Draft =
User.Draft(
alias = User.Alias(this.alias),
)
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ data class User(
but got ${text.abbreviated()}
""".trimIndent()
}
require(regex.matches(text)) {
require(!regex.matches(text)) {
"""
User alias must contain only latin letters,
but got ${text.abbreviated()}
Expand All @@ -42,4 +42,8 @@ data class User(
TEACHER,
STUDENT,
}
}

data class Draft(
val alias: Alias,
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package ru.itmo.lms.botalka.logic

import ru.itmo.lms.botalka.domain.model.User

interface UserService {
suspend fun getById(id: User.Id): User
suspend fun create(user: User.Draft): User
suspend fun promote(id: User.Id, role: User.Role)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package ru.itmo.lms.botalka.logic.basic

import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service
import ru.itmo.lms.botalka.domain.model.User
import ru.itmo.lms.botalka.logic.UserService
import ru.itmo.lms.botalka.storage.UserStorage

@Service
class BasicUserService(
@Autowired private val storage: UserStorage,
) : UserService {
override suspend fun getById(id: User.Id): User =
storage.getById(id)

override suspend fun create(user: User.Draft): User =
storage.create(user)

override suspend fun promote(id: User.Id, role: User.Role) {
TODO("Not yet implemented")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package ru.itmo.lms.botalka.storage

import ru.itmo.lms.botalka.domain.model.User

interface UserStorage {
suspend fun getById(id: User.Id): User
suspend fun create(user: User.Draft): User
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package ru.itmo.lms.botalka.storage.jooq

import kotlinx.coroutines.reactor.awaitSingle
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Repository
import ru.itmo.lms.botalka.domain.model.User
import ru.itmo.lms.botalka.storage.UserStorage
import ru.itmo.lms.botalka.storage.jooq.entity.toModel
import ru.itmo.lms.botalka.storage.jooq.tables.references.USER

@Repository
class JooqUserStorage(
@Autowired private val database: JooqDatabase,
) : UserStorage {
override suspend fun getById(id: User.Id): User =
database.execute
.selectFrom(USER)
.where(USER.ID.equal(id.number))
.toMono()
.map { it.toModel() }
.awaitSingle()

override suspend fun create(user: User.Draft): User =
database.execute
.insertInto(USER, USER.ALIAS)
.values(user.alias.text)
.returningResult(USER.fields().asList())
.coerce(USER)
.toMono()
.map { it.toModel() }
.awaitSingle()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package ru.itmo.lms.botalka.storage.jooq.entity

import ru.itmo.lms.botalka.domain.model.User
import ru.itmo.lms.botalka.storage.jooq.tables.records.UserRecord

fun UserRecord.toModel(): User =
User(
id = User.Id(this.id!!),
alias = User.Alias(this.alias),
roles = emptySet(),
)

fun User.Draft.toRecord(): UserRecord =
UserRecord(
alias = this.alias.text,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package ru.itmo.lms.botalka.api.http.endpoint

import io.kotest.matchers.collections.shouldBeUnique
import io.kotest.matchers.collections.shouldContainExactly
import io.kotest.matchers.collections.shouldHaveSize
import io.kotest.matchers.shouldBe
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import ru.itmo.lms.botalka.BotalkaTestSuite
import ru.itmo.lms.botalka.api.http.apis.UserApi
import ru.itmo.lms.botalka.api.http.message.toMessage
import ru.itmo.lms.botalka.domain.model.User

@SpringBootTest
class UserApiTest(
@Autowired private val api: UserApi,
) : BotalkaTestSuite() {
private val drafts = listOf(
User.Draft(User.Alias("admin")),
User.Draft(User.Alias("vityaman")),
User.Draft(User.Alias("dima")),
User.Draft(User.Alias("tester")),
)

@Test
fun createAndGetUser() {
val draftToResultList = runBlocking {
drafts.map { draft ->
val message = draft.toMessage()
val result = async { api.userPost(message).body }
Pair(draft, result)
}.map { (draft, result) ->
Pair(draft, result.await() ?: throw NullPointerException())
}
}

draftToResultList.forEach { (l, r) ->
l.alias.text shouldBe r.alias
r.roles shouldHaveSize 0
}

val results = draftToResultList.map { it.second }

val ids = results.map { it.id }
ids.shouldBeUnique()

runBlocking {
results.forEach { expected ->
launch {
val actual = api.userIdGet(expected.id).body!!
expected.id shouldBe actual.id
expected.alias shouldBe actual.alias
expected.roles shouldContainExactly actual.roles
}
}
}
}
}

0 comments on commit 6421489

Please sign in to comment.