Skip to content

Commit

Permalink
Merge pull request #289 from wafflestudio/develop
Browse files Browse the repository at this point in the history
Release
  • Loading branch information
Hank-Choi authored Oct 13, 2024
2 parents 918b3fb + 381077c commit b6c3e96
Show file tree
Hide file tree
Showing 17 changed files with 278 additions and 13 deletions.
32 changes: 32 additions & 0 deletions api/src/main/kotlin/handler/FeedbackHandler.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.wafflestudio.snu4t.handler

import com.wafflestudio.snu4t.common.client.AppVersion
import com.wafflestudio.snu4t.common.dto.OkResponse
import com.wafflestudio.snu4t.feedback.dto.FeedbackPostRequestDto
import com.wafflestudio.snu4t.feedback.service.FeedbackService
import com.wafflestudio.snu4t.middleware.SnuttRestApiNoAuthMiddleware
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

@Component
class FeedbackHandler(
private val feedbackService: FeedbackService,
snuttRestApiNoAuthMiddleware: SnuttRestApiNoAuthMiddleware,
) : ServiceHandler(snuttRestApiNoAuthMiddleware) {
suspend fun postFeedback(req: ServerRequest): ServerResponse =
handle(req) {
val body = req.awaitBody<FeedbackPostRequestDto>()
val clientInfo = req.clientInfo!!
feedbackService.addGithubIssue(
email = body.email,
message = body.message,
osType = clientInfo.osType,
osVersion = clientInfo.osVersion,
appVersion = clientInfo.appVersion ?: AppVersion("Unknown"),
deviceModel = clientInfo.deviceModel ?: "Unknown",
)
OkResponse()
}
}
12 changes: 12 additions & 0 deletions api/src/main/kotlin/router/MainRouter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import com.wafflestudio.snu4t.handler.ConfigHandler
import com.wafflestudio.snu4t.handler.CoursebookHandler
import com.wafflestudio.snu4t.handler.DeviceHandler
import com.wafflestudio.snu4t.handler.EvHandler
import com.wafflestudio.snu4t.handler.FeedbackHandler
import com.wafflestudio.snu4t.handler.FriendHandler
import com.wafflestudio.snu4t.handler.FriendTableHandler
import com.wafflestudio.snu4t.handler.LectureSearchHandler
Expand All @@ -27,6 +28,7 @@ import com.wafflestudio.snu4t.router.docs.BuildingsDocs
import com.wafflestudio.snu4t.router.docs.ConfigDocs
import com.wafflestudio.snu4t.router.docs.CoursebookDocs
import com.wafflestudio.snu4t.router.docs.EvDocs
import com.wafflestudio.snu4t.router.docs.FeedbackDocs
import com.wafflestudio.snu4t.router.docs.FriendDocs
import com.wafflestudio.snu4t.router.docs.LectureSearchDocs
import com.wafflestudio.snu4t.router.docs.NotificationDocs
Expand Down Expand Up @@ -64,6 +66,7 @@ class MainRouter(
private val evHandler: EvHandler,
private val coursebookHandler: CoursebookHandler,
private val tagHandler: TagHandler,
private val feedbackHandler: FeedbackHandler,
private val staticPageHandler: StaticPageHandler,
) {
@Bean
Expand Down Expand Up @@ -301,6 +304,15 @@ class MainRouter(
}
}

@Bean
@FeedbackDocs
fun feedbackRouter() =
v1CoRouter {
"/feedback".nest {
POST("", feedbackHandler::postFeedback)
}
}

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

import com.wafflestudio.snu4t.common.dto.OkResponse
import com.wafflestudio.snu4t.feedback.dto.FeedbackPostRequestDto
import io.swagger.v3.oas.annotations.Operation
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/feedback",
method = [RequestMethod.POST],
produces = [MediaType.APPLICATION_JSON_VALUE],
operation =
Operation(
operationId = "postFeedback",
requestBody =
RequestBody(
content = [
Content(
schema = Schema(implementation = FeedbackPostRequestDto::class),
mediaType = MediaType.APPLICATION_JSON_VALUE,
),
],
),
responses = [
ApiResponse(
responseCode = "200",
content = [Content(schema = Schema(implementation = OkResponse::class))],
),
],
),
),
)
annotation class FeedbackDocs()
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ class SugangSnuSyncJobConfig {
sugangSnuSyncService.addCoursebook(newCoursebook)
sugangSnuNotificationService.notifyCoursebookUpdate(newCoursebook)
}
sugangSnuSyncService.flushCache()
}
RepeatStatus.FINISHED
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,6 @@ interface SugangSnuSyncService {
suspend fun addCoursebook(coursebook: Coursebook)

suspend fun isSyncWithSugangSnu(latestCoursebook: Coursebook): Boolean

suspend fun flushCache()
}

@Service
Expand Down Expand Up @@ -85,8 +83,6 @@ class SugangSnuSyncServiceImpl(
return latestCoursebook.isSyncedToSugangSnu(sugangSnuLatestCoursebook)
}

override suspend fun flushCache() = cache.flushDatabase()

private fun compareLectures(
newLectures: Iterable<Lecture>,
oldLectures: Iterable<Lecture>,
Expand Down
7 changes: 0 additions & 7 deletions core/src/main/kotlin/common/cache/Cache.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import kotlinx.coroutines.reactive.awaitSingle
import org.slf4j.LoggerFactory
import org.springframework.data.redis.core.ReactiveStringRedisTemplate
import org.springframework.data.redis.core.deleteAndAwait
Expand All @@ -33,8 +32,6 @@ interface Cache {
suspend fun acquireLock(builtKey: BuiltCacheKey): Boolean

suspend fun releaseLock(builtKey: BuiltCacheKey): Boolean

suspend fun flushDatabase()
}

@Component
Expand Down Expand Up @@ -104,10 +101,6 @@ class RedisCache(
log.debug("[CACHE DEL] {}", builtKey.key)
return redisTemplate.deleteAndAwait(builtKey.key) > 0
}

override suspend fun flushDatabase() {
redisTemplate.execute { it.serverCommands().flushDb() }.awaitSingle()
}
}

suspend inline fun <reified T : Any> Cache.get(
Expand Down
1 change: 1 addition & 0 deletions core/src/main/kotlin/common/exception/ErrorType.kt
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ enum class ErrorType(
THEME_NOT_FOUND(HttpStatus.NOT_FOUND, 40406, "테마를 찾을 수 없습니다.", "테마를 찾을 수 없습니다."),
EV_DATA_NOT_FOUND(HttpStatus.NOT_FOUND, 40407, "강의평 데이터를 찾을 수 없습니다.", "강의평 데이터를 찾을 수 없습니다."),
TAG_LIST_NOT_FOUND(HttpStatus.NOT_FOUND, 40408, "태그 리스트를 찾을 수 없습니다.", "태그 리스트를 찾을 수 없습니다."),
FRIEND_LINK_NOT_FOUND(HttpStatus.NOT_FOUND, 40409, "친구 링크가 유효하지 않습니다.", "친구 링크가 유효하지 않습니다."),

DUPLICATE_VACANCY_NOTIFICATION(HttpStatus.CONFLICT, 40900, "빈자리 알림 중복"),
DUPLICATE_EMAIL(HttpStatus.CONFLICT, 40901, "이미 사용 중인 이메일입니다.", "이미 사용 중인 이메일입니다."),
Expand Down
2 changes: 2 additions & 0 deletions core/src/main/kotlin/common/exception/Snu4tException.kt
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ object ConfigNotFoundException : Snu4tException(ErrorType.CONFIG_NOT_FOUND)

object FriendNotFoundException : Snu4tException(ErrorType.FRIEND_NOT_FOUND)

object FriendLinkNotFoundException : Snu4tException(ErrorType.FRIEND_LINK_NOT_FOUND)

object UserNotFoundByNicknameException : Snu4tException(ErrorType.USER_NOT_FOUND_BY_NICKNAME)

object ThemeNotFoundException : Snu4tException(ErrorType.THEME_NOT_FOUND)
Expand Down
29 changes: 29 additions & 0 deletions core/src/main/kotlin/common/github/api/GithubRestApi.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.wafflestudio.snu4t.common.github.api

import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.HttpHeaders.ACCEPT
import org.springframework.http.HttpHeaders.USER_AGENT
import org.springframework.web.reactive.function.client.WebClient

@Configuration
class GithubRestApiConfig {
companion object {
const val GITHUB_API_BASE_URI = "https://api.github.com"
}

@Bean
fun githubRestApi(): GithubRestApi =
WebClient.builder().baseUrl(GITHUB_API_BASE_URI)
.defaultHeaders {
it.setAll(
mapOf(
ACCEPT to "application/vnd.github.v3+json",
USER_AGENT to "snutt-timetable",
),
)
}
.build().let(::GithubRestApi)
}

class GithubRestApi(webClient: WebClient) : WebClient by webClient
37 changes: 37 additions & 0 deletions core/src/main/kotlin/common/github/client/GithubRestApiClient.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.wafflestudio.snu4t.common.github.client

import com.wafflestudio.snu4t.common.extension.post
import com.wafflestudio.snu4t.common.github.api.GithubRestApi
import com.wafflestudio.snu4t.common.github.dto.GithubIssue
import org.springframework.http.HttpHeaders.AUTHORIZATION
import org.springframework.stereotype.Service

interface GithubRestApiClient {
suspend fun addRepoIssue(
repoOwner: String,
repoName: String,
token: String,
issue: GithubIssue,
)
}

@Service
class GithubRestApiClientImpl(
private val githubRestApi: GithubRestApi,
) : GithubRestApiClient {
override suspend fun addRepoIssue(
repoOwner: String,
repoName: String,
token: String,
issue: GithubIssue,
) {
githubRestApi.post<Any>(
uri = "/repos/$repoOwner/$repoName/issues",
body = issue,
headers =
mapOf(
AUTHORIZATION to "token $token",
),
)
}
}
7 changes: 7 additions & 0 deletions core/src/main/kotlin/common/github/dto/GithubIssue.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.wafflestudio.snu4t.common.github.dto

data class GithubIssue(
val title: String,
val body: String,
val labels: List<String> = listOf(),
)
22 changes: 22 additions & 0 deletions core/src/main/kotlin/feedback/dto/FeedbackDto.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.wafflestudio.snu4t.feedback.dto

data class FeedbackDto(
val email: String,
val platform: String,
val appVersion: String,
val deviceModel: String,
val currentSeoulTime: String,
val profileName: String,
val message: String,
) {
fun toGithubIssueBody() =
"""
이메일: `$email`
플랫폼/디바이스: $platform / $deviceModel
버전: $appVersion
프로필: $profileName
날짜/시간(UTC+9): $currentSeoulTime
$message
""".trimIndent()
}
6 changes: 6 additions & 0 deletions core/src/main/kotlin/feedback/dto/FeedbackPostRequestDto.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.wafflestudio.snu4t.feedback.dto

data class FeedbackPostRequestDto(
val email: String,
val message: String,
)
73 changes: 73 additions & 0 deletions core/src/main/kotlin/feedback/service/FeedbackService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package com.wafflestudio.snu4t.feedback.service

import com.wafflestudio.snu4t.common.client.AppVersion
import com.wafflestudio.snu4t.common.client.OsType
import com.wafflestudio.snu4t.common.github.client.GithubRestApiClient
import com.wafflestudio.snu4t.common.github.dto.GithubIssue
import com.wafflestudio.snu4t.config.PhaseUtils
import com.wafflestudio.snu4t.feedback.dto.FeedbackDto
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Service
import java.time.Instant
import java.time.ZoneId
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter

interface FeedbackService {
suspend fun addGithubIssue(
email: String,
message: String,
osType: OsType,
osVersion: String?,
appVersion: AppVersion,
deviceModel: String,
)
}

@Service
class FeedbackServiceImpl(
@Value("\${github.feedback.token}") private val token: String,
@Value("\${github.feedback.repo.owner}") private val repoOwner: String,
@Value("\${github.feedback.repo.name}") private val repoName: String,
private val githubRestApiClient: GithubRestApiClient,
) : FeedbackService {
override suspend fun addGithubIssue(
email: String,
message: String,
osType: OsType,
osVersion: String?,
appVersion: AppVersion,
deviceModel: String,
) {
val osTypeString = osType.toString().lowercase()
val platform = osVersion?.let { "$osTypeString ($osVersion)" } ?: osTypeString
val currentSeoulTime =
DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(
ZonedDateTime.ofInstant(
Instant.now(),
ZoneId.of("Asia/Seoul"),
),
)
val profileName = PhaseUtils.getPhase().name.lowercase()
githubRestApiClient.addRepoIssue(
repoOwner = repoOwner,
repoName = repoName,
token = token,
issue =
GithubIssue(
title = message,
body =
FeedbackDto(
email = email,
platform = platform,
appVersion = appVersion.toString(),
deviceModel = deviceModel,
currentSeoulTime = currentSeoulTime,
profileName = profileName,
message = message,
).toGithubIssueBody(),
labels = listOf(osTypeString),
),
)
}
}
3 changes: 2 additions & 1 deletion core/src/main/kotlin/friend/service/FriendService.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.wafflestudio.snu4t.friend.service

import com.wafflestudio.snu4t.common.exception.DuplicateFriendException
import com.wafflestudio.snu4t.common.exception.FriendLinkNotFoundException
import com.wafflestudio.snu4t.common.exception.FriendNotFoundException
import com.wafflestudio.snu4t.common.exception.InvalidDisplayNameException
import com.wafflestudio.snu4t.common.exception.InvalidFriendException
Expand Down Expand Up @@ -232,7 +233,7 @@ class FriendServiceImpl(
): Pair<Friend, User> {
val fromUserId =
redisTemplate.opsForValue()
.getAndAwait(FRIEND_LINK_REDIS_PREFIX + requestToken) ?: throw FriendNotFoundException
.getAndAwait(FRIEND_LINK_REDIS_PREFIX + requestToken) ?: throw FriendLinkNotFoundException
val fromUser = userRepository.findByIdAndActiveTrue(fromUserId) ?: throw UserNotFoundException
if (fromUser.id == userId) {
throw InvalidFriendException
Expand Down
7 changes: 7 additions & 0 deletions core/src/main/resources/application-common.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ google:
http:
response-timeout: 3s

github:
feedback:
token:
repo:
owner:
name:

api:
server:
snuttev:
Expand Down
Loading

0 comments on commit b6c3e96

Please sign in to comment.