Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#123 Add OpenAPI operation id #131

Merged
merged 5 commits into from
May 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import ru.vityaman.lms.botalka.core.external.yandex.YandexOAuthCode
class AuthHttpApi(
private val yandex: SpringYandexOAuthService,
) : AuthApi {
override suspend fun authYandexCodePut(
override suspend fun authViaYandexCode(
auth2ViaCodeMessage: Auth2ViaCodeMessage,
): ResponseEntity<TokensMessage> {
val code = YandexOAuthCode(auth2ViaCodeMessage.code)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class HomeworkHttpApi(
@Autowired private val workspace: WorkspaceService,
@Autowired private val authority: Authority,
) : HomeworkApi {
override suspend fun homeworkPost(
override suspend fun postHomework(
homeworkDraftMessage: HomeworkDraftMessage,
): ResponseEntity<HomeworkMessage> {
val draft = homeworkDraftMessage.toModel()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ import ru.vityaman.lms.botalka.app.spring.api.http.server.apis.MonitoringApi

@RestController
class MonitoringHttpApi : MonitoringApi {
override suspend fun monitoringPingGet(): ResponseEntity<String> =
override suspend fun monitoringPing(): ResponseEntity<String> =
ResponseEntity.ok("pong")
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class PromotionHttpApi(
@Autowired private val promotionService: PromotionService,
@Autowired private val authority: Authority,
) : PromotionApi {
override suspend fun promotionRequestIdPatch(
override suspend fun patchPromotionRequest(
id: Int,
promotionRequestPatchMessage: PromotionRequestPatchMessage,
): ResponseEntity<Unit> {
Expand Down Expand Up @@ -54,7 +54,7 @@ class PromotionHttpApi(
return ResponseEntity.status(HttpStatus.NO_CONTENT).build()
}

override suspend fun promotionRequestPost(
override suspend fun postPromotionRequest(
promotionRequestDraftMessage: PromotionRequestDraftMessage,
): ResponseEntity<PromotionRequestMessage> {
val user = SpringSecurityContext.principal().id
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ class UserHttpApi(
@Autowired private val userService: UserService,
@Autowired private val auth: SpringYandexOAuthService,
) : UserApi {
override suspend fun userIdGet(id: Int): ResponseEntity<UserMessage> {
override suspend fun getUserById(id: Int): ResponseEntity<UserMessage> {
val userId = User.Id(id)
val user = userService.getById(userId)
.orNotFound("User with id ${userId.number} not found")
return ResponseEntity.ok(user.toMessage())
}

override suspend fun userPost(
override suspend fun postUser(
postUserRequestMessage: PostUserRequestMessage,
): ResponseEntity<PostUserResponseMessage> {
val draft = postUserRequestMessage.user.toModel()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import ru.vityaman.lms.botalka.app.spring.api.http.server.GeneralErrorMessage
import ru.vityaman.lms.botalka.core.exception.AlreadyAskedPromotionException
import ru.vityaman.lms.botalka.core.exception.AuthenticationException
import ru.vityaman.lms.botalka.core.exception.AuthorizationException
import ru.vityaman.lms.botalka.core.exception.DeadlinePassedException
import ru.vityaman.lms.botalka.core.exception.DomainException
import ru.vityaman.lms.botalka.core.exception.InvalidValueException
import ru.vityaman.lms.botalka.core.exception.NotFoundException
import ru.vityaman.lms.botalka.core.exception.PromotionRequestResolvedException
import ru.vityaman.lms.botalka.core.external.yandex.YandexUnavailableException
import ru.vityaman.lms.botalka.core.security.exception.AuthenticationException
import ru.vityaman.lms.botalka.core.security.exception.AuthorizationException
import ru.vityaman.lms.botalka.core.exception.ServiceUnavailableException

val DomainException.httpCode: HttpStatus
get() = when (this) {
Expand All @@ -22,8 +22,7 @@ val DomainException.httpCode: HttpStatus
is DeadlinePassedException -> HttpStatus.BAD_REQUEST
is AuthenticationException -> HttpStatus.UNAUTHORIZED
is AuthorizationException -> HttpStatus.FORBIDDEN
is YandexUnavailableException -> HttpStatus.SERVICE_UNAVAILABLE
else -> TODO("Unexpected domain exception $this")
is ServiceUnavailableException -> HttpStatus.SERVICE_UNAVAILABLE
}

fun DomainException.toResponseEntity() =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import org.springframework.stereotype.Component
import org.springframework.web.server.ServerWebExchange
import reactor.core.publisher.Mono
import ru.vityaman.lms.botalka.app.spring.api.http.error.DomainExceptionMarshalling
import ru.vityaman.lms.botalka.core.exception.AuthenticationException
import ru.vityaman.lms.botalka.core.exception.DomainException
import ru.vityaman.lms.botalka.core.security.exception.AuthenticationException

@Component
class SpringJwtContextRepository(
Expand Down
28 changes: 18 additions & 10 deletions botalka/src/main/kotlin/ru/vityaman/lms/botalka/commons/BiMap.kt
Original file line number Diff line number Diff line change
@@ -1,26 +1,34 @@
package ru.vityaman.lms.botalka.commons

import java.util.EnumMap
import kotlin.enums.enumEntries

@OptIn(ExperimentalStdlibApi::class)
interface BiMap<T, U> {
fun toRight(value: T): U
fun toLeft(value: U): T

companion object {
inline fun <reified T : Enum<T>, reified U : Enum<U>> from(
vararg pairs: Pair<T, U>,
): BiMap<T, U> =
object : BiMap<T, U> {
private val rhs = pairs
.associateTo(EnumMap(T::class.java)) { (l, r) -> l to r }

private val lhs = pairs
.associateTo(EnumMap(U::class.java)) { (l, r) -> r to l }
): BiMap<T, U> = object : BiMap<T, U> {
private val rhs = pairs
.associateTo(EnumMap(T::class.java)) { (l, r) -> l to r }

private val lhs = pairs
.associateTo(EnumMap(U::class.java)) { (l, r) -> r to l }

init {
val left = pairs.map { it.first }.toSet()
val right = pairs.map { it.second }.toSet()
require(left.size == right.size)
require(enumEntries<T>().size == left.size)
}

override fun toRight(value: T): U = rhs[value]!!
override fun toRight(value: T): U = rhs[value]!!

override fun toLeft(value: U): T = lhs[value]!!
}
override fun toLeft(value: U): T = lhs[value]!!
}

@JvmName("BiMapLeftToRight")
operator fun <T, U> BiMap<T, U>.invoke(value: T): U =
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package ru.vityaman.lms.botalka.core.security.exception
package ru.vityaman.lms.botalka.core.exception

class AuthenticationException(message: String, cause: Throwable? = null) :
SecurityException(message, cause)
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package ru.vityaman.lms.botalka.core.security.exception
package ru.vityaman.lms.botalka.core.exception

import ru.vityaman.lms.botalka.core.model.User
import ru.vityaman.lms.botalka.core.security.persmission.Permission
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package ru.vityaman.lms.botalka.core.exception

open class DomainException(message: String, cause: Throwable? = null) :
sealed class DomainException(message: String, cause: Throwable? = null) :
Exception(message, cause)
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package ru.vityaman.lms.botalka.core.exception

sealed class SecurityException(message: String, cause: Throwable? = null) :
DomainException(message, cause)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package ru.vityaman.lms.botalka.core.exception

open class ServiceUnavailableException(
message: String,
cause: Throwable? = null,
) :
DomainException(message, cause)
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package ru.vityaman.lms.botalka.core.external.yandex

import ru.vityaman.lms.botalka.core.exception.DomainException
import ru.vityaman.lms.botalka.core.exception.ServiceUnavailableException

class YandexUnavailableException(
string: String = "Yandex is unavailable",
cause: Throwable? = null,
) :
DomainException(string, cause)
ServiceUnavailableException(string, cause)

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package ru.vityaman.lms.botalka.core.security.persmission

import ru.vityaman.lms.botalka.core.exception.AuthorizationException
import ru.vityaman.lms.botalka.core.model.User
import ru.vityaman.lms.botalka.core.security.exception.AuthorizationException

class AuthorizedUser(
private val authority: Authority,
Expand Down
7 changes: 7 additions & 0 deletions botalka/src/main/resources/static/openapi/api.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ paths:
/monitoring/ping:
get:
tags: [ Monitoring ]
operationId: monitoringPing
summary: Checks if service is alive
description: Returns 'pong', if service is alive, else we will cry
responses:
Expand All @@ -21,6 +22,7 @@ paths:
/homework:
post:
tags: [ Homework ]
operationId: postHomework
summary: Creates a homework
description: Creates a homework to be published at specified time
security:
Expand Down Expand Up @@ -154,6 +156,7 @@ paths:
/user/{id}:
get:
tags: [ User ]
operationId: getUserById
summary: Find a user by its id
description: Find a user by its id
security:
Expand Down Expand Up @@ -186,6 +189,7 @@ paths:
/user:
post:
tags: [ User ]
operationId: postUser
summary: Creates a new user
description: Creates a new user
requestBody:
Expand All @@ -211,6 +215,7 @@ paths:
/promotion/request:
post:
tags: [ User ]
operationId: postPromotionRequest
summary: Request for a user role promotion
description: |
Creates a promotion request that needs to be approved
Expand Down Expand Up @@ -252,6 +257,7 @@ paths:
/promotion/request/{id}:
patch:
tags: [ User ]
operationId: patchPromotionRequest
summary: Updates a promotion request status
description: |
Used by admin to approve or cancel promotion request.
Expand Down Expand Up @@ -295,6 +301,7 @@ paths:
/auth/yandex/code:
put:
tags: [ Auth ]
operationId: authViaYandexCode
summary: Authenticate via Yandex ID authentication code
description: Takes an authentication code and checks authority
requestBody:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class Api private constructor(

private suspend fun ofRegisteredAs(message: PostUserRequestMessage) =
ofNewbie().user
.userPost(message)
.postUser(message)
.awaitSingle()
.let { Api(Data(it.user.id, it.token)) }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,22 @@ package ru.vityaman.lms.botalka.app.spring.api.http.client
import kotlinx.coroutines.reactor.awaitSingle

suspend fun Api.requestRole(role: UserRoleMessage): Int =
user.promotionRequestPost(
user.postPromotionRequest(
PromotionRequestDraftMessage(
role = role,
),
).awaitSingle().id

suspend fun Api.approve(requestId: Int): Unit =
user.promotionRequestIdPatch(
user.patchPromotionRequest(
requestId,
PromotionRequestPatchMessage(
status = PromotionRequestStatusMessage.approved,
),
).awaitSingle()

suspend fun Api.reject(requestId: Int): Unit =
user.promotionRequestIdPatch(
user.patchPromotionRequest(
requestId,
PromotionRequestPatchMessage(
status = PromotionRequestStatusMessage.canceled,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class AuthApiTest : BotalkaTestSuite() {
fun yandexCodeSmoking(): Unit = runBlocking {
val api = Api.ofNewbie()

api.user.userPost(
api.user.postUser(
PostUserRequestMessage(
user = UserDraftMessage("tester"),
credential = Auth2ViaCodeMessage(
Expand All @@ -25,7 +25,7 @@ class AuthApiTest : BotalkaTestSuite() {
),
).awaitSingle()

api.auth.authYandexCodePut(
api.auth.authViaYandexCode(
Auth2ViaCodeMessage(
kind = CredentialKindMessage.yandex_code,
code = 654_321,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ class HomeworkApiTest : BotalkaTestSuite() {

val draftToResultList = drafts.map { draft ->
val result = async {
tester.homework.homeworkPost(draft).awaitSingle()
tester.homework.postHomework(draft).awaitSingle()
}
Pair(draft, result)
}.map { (draft, result) ->
Expand Down Expand Up @@ -121,7 +121,7 @@ class HomeworkApiTest : BotalkaTestSuite() {
publicationMoment: OffsetDateTime,
deadlineMoment: OffsetDateTime,
) {
this.homework.homeworkPost(
this.homework.postHomework(
sampleHomework(
publicationMoment = publicationMoment,
deadlineMoment = deadlineMoment,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class MonitoringApiTest : BotalkaTestSuite() {
@Test
fun pingSucceeds(): Unit = runBlocking {
val api = Api.ofNewbie()
val body = api.monitoring.monitoringPingGet().awaitSingle()
val body = api.monitoring.monitoringPing().awaitSingle()
assertEquals(body, "pong")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ class RatingApiTest : BotalkaTestSuite() {
}

private suspend fun Api.homeworkPost(draft: HomeworkDraftMessage): Int =
homework.homeworkPost(draft).awaitSingle().id
homework.postHomework(draft).awaitSingle().id

private suspend fun Api.submit(homework: Int) =
this.homework.postEvent(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,5 +94,5 @@ class ResolvePromotionApiTest : BotalkaTestSuite() {
}

private suspend fun roles(id: Int) =
admin.user.userIdGet(id).awaitSingle().roles
admin.user.getUserById(id).awaitSingle().roles
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class UserApiTest : BotalkaTestSuite() {
draft.user,
async {
Api.ofNewbie().user
.userPost(draft)
.postUser(draft)
.awaitSingle()
.user
},
Expand All @@ -53,7 +53,7 @@ class UserApiTest : BotalkaTestSuite() {
}.onEach { expected ->
launch {
val actual = admin.user
.userIdGet(expected.id)
.getUserById(expected.id)
.awaitSingle()
expected.id shouldBe actual.id
expected.alias shouldBe actual.alias
Expand All @@ -65,7 +65,7 @@ class UserApiTest : BotalkaTestSuite() {
@Test
fun userNotFound(): Unit = runBlocking {
assertThrows<WebClientResponseException.NotFound> {
admin.user.userIdGet(666).awaitSingle()
admin.user.getUserById(666).awaitSingle()
}
}

Expand All @@ -76,7 +76,7 @@ class UserApiTest : BotalkaTestSuite() {
repeat(2) {
launch {
val draft = UserDraftMessage("vanechka")
Api.ofNewbie().user.userPost(
Api.ofNewbie().user.postUser(
PostUserRequestMessage(
user = draft,
credential = Auth2ViaCodeMessage(
Expand Down
Loading
Loading