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

Release #209

Merged
merged 7 commits into from
Jan 18, 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
3 changes: 2 additions & 1 deletion api/src/main/kotlin/filter/ErrorWebFilter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,14 @@ class ErrorWebFilter(
private fun makeErrorBody(
exception: Snu4tException,
): ErrorBody {
return ErrorBody(exception.error.errorCode, exception.errorMessage, exception.displayMessage)
return ErrorBody(exception.error.errorCode, exception.errorMessage, exception.displayMessage, exception.ext)
}
}

private data class ErrorBody(
val errcode: Long,
val message: String,
val displayMessage: String,
// TODO: 구버전 대응용 ext 필드. 추후 삭제
val ext: Map<String, String> = mapOf(),
)
4 changes: 2 additions & 2 deletions api/src/main/kotlin/handler/TimetableHandler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,9 @@ class TimetableHandler(
suspend fun modifyTimetableTheme(req: ServerRequest): ServerResponse = handle(req) {
val userId = req.userId
val timetableId = req.pathVariable("timetableId")
val theme = req.awaitBody<TimetableModifyThemeRequestDto>().theme
val body = req.awaitBody<TimetableModifyThemeRequestDto>()

timetableService.modifyTimetableTheme(userId, timetableId, theme).let(::TimetableLegacyDto)
timetableService.modifyTimetableTheme(userId, timetableId, body.theme, body.themeId).let(::TimetableLegacyDto)
}

suspend fun setPrimary(req: ServerRequest): ServerResponse = handle(req) {
Expand Down
23 changes: 14 additions & 9 deletions api/src/main/kotlin/handler/TimetableLectureHandler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ package com.wafflestudio.snu4t.handler

import com.fasterxml.jackson.annotation.JsonProperty
import com.wafflestudio.snu4t.middleware.SnuttRestApiDefaultMiddleware
import com.wafflestudio.snu4t.timetables.dto.TimetableLegacyDto
import com.wafflestudio.snu4t.timetables.dto.request.CustomTimetableLectureAddLegacyRequestDto
import com.wafflestudio.snu4t.timetables.dto.request.TimetableLectureModifyLegacyRequestDto
import com.wafflestudio.snu4t.timetables.service.TimetableLectureService
import org.springframework.stereotype.Component
import org.springframework.web.reactive.function.server.ServerRequest
import org.springframework.web.reactive.function.server.ServerResponse
import org.springframework.web.reactive.function.server.awaitBody
import org.springframework.web.reactive.function.server.awaitBodyOrNull

@Component
class TimetableLectureHandler(
Expand All @@ -21,55 +23,58 @@ class TimetableLectureHandler(
val userId = req.userId
val timetableId = req.pathVariable("timetableId")
val customTimetable = req.awaitBody<CustomTimetableLectureAddLegacyRequestDto>()
val isForced = customTimetable.isForced
val isForced = req.parseQueryParam<Boolean>("isForced") ?: customTimetable.isForced

timetableLectureService.addCustomTimetableLecture(
userId = userId,
timetableId = timetableId,
timetableLectureRequest = customTimetable,
isForced = isForced,
)
).let(::TimetableLegacyDto)
}

suspend fun addLecture(req: ServerRequest): ServerResponse = handle(req) {
val userId = req.userId
val timetableId = req.pathVariable("timetableId")
val lectureId = req.pathVariable("lectureId")
val isForced = req.awaitBody<ForcedReq>().isForced
val isForced = req.parseQueryParam<Boolean>("isForced") ?: req.awaitBodyOrNull<ForcedReq>()?.isForced ?: false

timetableLectureService.addLecture(
userId = userId,
timetableId = timetableId,
lectureId = lectureId,
isForced = isForced,
)
).let(::TimetableLegacyDto)
}

suspend fun resetTimetableLecture(req: ServerRequest): ServerResponse = handle(req) {
val userId = req.userId
val timetableId = req.pathVariable("timetableId")
val timetableLectureId = req.pathVariable("timetableLectureId")
val isForced = req.parseQueryParam<Boolean>("isForced") ?: req.awaitBodyOrNull<ForcedReq>()?.isForced ?: false

timetableLectureService.resetTimetableLecture(
userId = userId,
timetableId = timetableId,
timetableLectureId = timetableLectureId,
)
isForced,
).let(::TimetableLegacyDto)
}

suspend fun modifyTimetableLecture(req: ServerRequest): ServerResponse = handle(req) {
val userId = req.userId
val timetableId = req.pathVariable("timetableId")
val timetableLectureId = req.pathVariable("timetableLectureId")
val modifyRequestDto = req.awaitBody<TimetableLectureModifyLegacyRequestDto>()
val isForced = modifyRequestDto.isForced
val isForced = req.parseQueryParam<Boolean>("isForced") ?: modifyRequestDto.isForced

timetableLectureService.modifyTimetableLecture(
userId = userId,
timetableId = timetableId,
timetableLectureId = timetableLectureId,
modifyTimetableLectureRequestDto = modifyRequestDto,
isForced = isForced,
)
).let(::TimetableLegacyDto)
}

suspend fun deleteTimetableLecture(req: ServerRequest): ServerResponse = handle(req) {
Expand All @@ -81,11 +86,11 @@ class TimetableLectureHandler(
userId = userId,
timetableId = timetableId,
timetableLectureId = timetableLectureId,
)
).let(::TimetableLegacyDto)
}

data class ForcedReq(
@JsonProperty("is_forced")
val isForced: Boolean
val isForced: Boolean?
)
}
74 changes: 74 additions & 0 deletions api/src/main/kotlin/handler/TimetableThemeHandler.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package com.wafflestudio.snu4t.handler

import com.wafflestudio.snu4t.common.enum.BasicThemeType
import com.wafflestudio.snu4t.common.exception.InvalidPathParameterException
import com.wafflestudio.snu4t.middleware.SnuttRestApiDefaultMiddleware
import com.wafflestudio.snu4t.timetables.dto.TimetableThemeDto
import com.wafflestudio.snu4t.timetables.dto.request.TimetableThemeAddRequestDto
import com.wafflestudio.snu4t.timetables.dto.request.TimetableThemeModifyRequestDto
import com.wafflestudio.snu4t.timetables.service.TimetableThemeService
import org.springframework.stereotype.Component
import org.springframework.web.reactive.function.server.ServerRequest
import org.springframework.web.reactive.function.server.awaitBody

@Component
class TimetableThemeHandler(
private val timetableThemeService: TimetableThemeService,
snuttRestApiDefaultMiddleware: SnuttRestApiDefaultMiddleware,
) : ServiceHandler(snuttRestApiDefaultMiddleware) {
suspend fun getThemes(req: ServerRequest) = handle(req) {
val userId = req.userId

timetableThemeService.getThemes(userId).map(::TimetableThemeDto)
}

suspend fun addTheme(req: ServerRequest) = handle(req) {
val userId = req.userId
val body = req.awaitBody<TimetableThemeAddRequestDto>()

timetableThemeService.addTheme(userId, body.name, body.colors).let(::TimetableThemeDto)
}

suspend fun modifyTheme(req: ServerRequest) = handle(req) {
val userId = req.userId
val themeId = req.pathVariable("themeId")
val body = req.awaitBody<TimetableThemeModifyRequestDto>()

timetableThemeService.modifyTheme(userId, themeId, body.name, body.colors).let(::TimetableThemeDto)
}

suspend fun deleteTheme(req: ServerRequest) = handle(req) {
val userId = req.userId
val themeId = req.pathVariable("themeId")

timetableThemeService.deleteTheme(userId, themeId)
}

suspend fun copyTheme(req: ServerRequest) = handle(req) {
val userId = req.userId
val themeId = req.pathVariable("themeId")

timetableThemeService.copyTheme(userId, themeId).let(::TimetableThemeDto)
}

suspend fun setDefault(req: ServerRequest) = handle(req) {
val userId = req.userId
val themeId = req.pathVariable("themeId")

timetableThemeService.setDefault(userId, themeId).let(::TimetableThemeDto)
}

suspend fun setBasicThemeTypeDefault(req: ServerRequest) = handle(req) {
val userId = req.userId
val basicThemeType = req.pathVariable("basicThemeTypeValue").toIntOrNull()?.let { BasicThemeType.from(it) } ?: throw InvalidPathParameterException("basicThemeTypeValue")

timetableThemeService.setDefault(userId, basicThemeType = basicThemeType).let(::TimetableThemeDto)
}

suspend fun unsetDefault(req: ServerRequest) = handle(req) {
val userId = req.userId
val themeId = req.pathVariable("themeId")

timetableThemeService.unsetDefault(userId, themeId).let(::TimetableThemeDto)
}
}
18 changes: 18 additions & 0 deletions api/src/main/kotlin/router/MainRouter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import com.wafflestudio.snu4t.handler.LectureSearchHandler
import com.wafflestudio.snu4t.handler.NotificationHandler
import com.wafflestudio.snu4t.handler.TimetableHandler
import com.wafflestudio.snu4t.handler.TimetableLectureHandler
import com.wafflestudio.snu4t.handler.TimetableThemeHandler
import com.wafflestudio.snu4t.handler.UserHandler
import com.wafflestudio.snu4t.handler.VacancyNotifcationHandler
import com.wafflestudio.snu4t.router.docs.AdminDocs
Expand All @@ -20,6 +21,7 @@ import com.wafflestudio.snu4t.router.docs.ConfigDocs
import com.wafflestudio.snu4t.router.docs.FriendDocs
import com.wafflestudio.snu4t.router.docs.LectureSearchDocs
import com.wafflestudio.snu4t.router.docs.NotificationDocs
import com.wafflestudio.snu4t.router.docs.ThemeDocs
import com.wafflestudio.snu4t.router.docs.TimetableDocs
import com.wafflestudio.snu4t.router.docs.UserDocs
import com.wafflestudio.snu4t.router.docs.VacancyNotificationDocs
Expand All @@ -39,6 +41,7 @@ class MainRouter(
private val vacancyNotificationHandler: VacancyNotifcationHandler,
private val timeTableHandler: TimetableHandler,
private val timeTableLectureHandler: TimetableLectureHandler,
private val timetableThemeHandler: TimetableThemeHandler,
private val bookmarkHandler: BookmarkHandler,
private val lectureSearchHandler: LectureSearchHandler,
private val friendHandler: FriendHandler,
Expand Down Expand Up @@ -174,6 +177,21 @@ class MainRouter(
}
}

@Bean
@ThemeDocs
fun timetableThemeRoute() = v1CoRouter {
"/themes".nest {
GET("", timetableThemeHandler::getThemes)
POST("", timetableThemeHandler::addTheme)
PATCH("{themeId}", timetableThemeHandler::modifyTheme)
DELETE("{themeId}", timetableThemeHandler::deleteTheme)
POST("{themeId}/copy", timetableThemeHandler::copyTheme)
POST("{themeId}/default", timetableThemeHandler::setDefault)
POST("basic/{basicThemeTypeValue}/default", timetableThemeHandler::setBasicThemeTypeDefault)
DELETE("{themeId}/default", timetableThemeHandler::unsetDefault)
}
}

private fun v1CoRouter(r: CoRouterFunctionDsl.() -> Unit) = coRouter {
path("/v1").or("").nest(r)
}
Expand Down
91 changes: 91 additions & 0 deletions api/src/main/kotlin/router/docs/ThemeDocs.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package com.wafflestudio.snu4t.router.docs

import com.wafflestudio.snu4t.timetables.dto.TimetableThemeDto
import com.wafflestudio.snu4t.timetables.dto.request.TimetableThemeAddRequestDto
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.Parameter
import io.swagger.v3.oas.annotations.enums.ParameterIn
import io.swagger.v3.oas.annotations.media.ArraySchema
import io.swagger.v3.oas.annotations.media.Content
import io.swagger.v3.oas.annotations.media.Schema
import io.swagger.v3.oas.annotations.parameters.RequestBody
import io.swagger.v3.oas.annotations.responses.ApiResponse
import org.springdoc.core.annotations.RouterOperation
import org.springdoc.core.annotations.RouterOperations
import org.springframework.http.MediaType
import org.springframework.web.bind.annotation.RequestMethod

@RouterOperations(
RouterOperation(
path = "/v1/themes", method = [RequestMethod.GET], produces = [MediaType.APPLICATION_JSON_VALUE],
operation = Operation(
operationId = "getThemes",
parameters = [Parameter(`in` = ParameterIn.QUERY, name = "state", required = true)],
responses = [ApiResponse(responseCode = "200", content = [Content(array = ArraySchema(schema = Schema(implementation = TimetableThemeDto::class)))])],
),
),
RouterOperation(
path = "/v1/themes", method = [RequestMethod.POST], produces = [MediaType.APPLICATION_JSON_VALUE],
operation = Operation(
operationId = "addTheme",
requestBody = RequestBody(
content = [Content(schema = Schema(implementation = TimetableThemeAddRequestDto::class))],
required = true,
),
responses = [ApiResponse(responseCode = "200", content = [Content(schema = Schema(implementation = TimetableThemeDto::class))])],
),
),
RouterOperation(
path = "/v1/themes/{themeId}", method = [RequestMethod.PATCH], produces = [MediaType.APPLICATION_JSON_VALUE],
operation = Operation(
operationId = "modifyTheme",
parameters = [Parameter(`in` = ParameterIn.PATH, name = "themeId", required = true)],
requestBody = RequestBody(
content = [Content(schema = Schema(implementation = TimetableThemeAddRequestDto::class))],
required = true,
),
responses = [ApiResponse(responseCode = "200", content = [Content(schema = Schema(implementation = TimetableThemeDto::class))])],
),
),
RouterOperation(
path = "/v1/themes/{themeId}", method = [RequestMethod.DELETE], produces = [MediaType.APPLICATION_JSON_VALUE],
operation = Operation(
operationId = "deleteTheme",
parameters = [Parameter(`in` = ParameterIn.PATH, name = "themeId", required = true)],
responses = [ApiResponse(responseCode = "200", content = [Content(schema = Schema())])],
),
),
RouterOperation(
path = "/v1/themes/{themeId}/copy", method = [RequestMethod.POST], produces = [MediaType.APPLICATION_JSON_VALUE],
operation = Operation(
operationId = "copyTheme",
parameters = [Parameter(`in` = ParameterIn.PATH, name = "themeId", required = true)],
responses = [ApiResponse(responseCode = "200", content = [Content(schema = Schema(implementation = TimetableThemeDto::class))])],
),
),
RouterOperation(
path = "/v1/themes/{themeId}/default", method = [RequestMethod.POST], produces = [MediaType.APPLICATION_JSON_VALUE],
operation = Operation(
operationId = "setDefault",
parameters = [Parameter(`in` = ParameterIn.PATH, name = "themeId", required = true)],
responses = [ApiResponse(responseCode = "200", content = [Content(schema = Schema(implementation = TimetableThemeDto::class))])],
),
),
RouterOperation(
path = "/v1/themes/basic/{basicThemeTypeValue}/default", method = [RequestMethod.POST], produces = [MediaType.APPLICATION_JSON_VALUE],
operation = Operation(
operationId = "setBasicThemeTypeDefault",
parameters = [Parameter(`in` = ParameterIn.PATH, name = "basicThemeTypeValue", required = true)],
responses = [ApiResponse(responseCode = "200", content = [Content(schema = Schema(implementation = TimetableThemeDto::class))])],
),
),
RouterOperation(
path = "/v1/themes/{themeId}/default", method = [RequestMethod.DELETE], produces = [MediaType.APPLICATION_JSON_VALUE],
operation = Operation(
operationId = "unsetDefault",
parameters = [Parameter(`in` = ParameterIn.PATH, name = "themeId", required = true)],
responses = [ApiResponse(responseCode = "200", content = [Content(schema = Schema(implementation = TimetableThemeDto::class))])],
),
),
)
annotation class ThemeDocs
Loading
Loading