Skip to content

Commit

Permalink
feat: presigned url 발급 api 구현 및 balance 등록 api request 수정
Browse files Browse the repository at this point in the history
  • Loading branch information
wjdtkdgns committed Jun 1, 2024
1 parent ac4225a commit f0c37db
Show file tree
Hide file tree
Showing 25 changed files with 273 additions and 62 deletions.
7 changes: 2 additions & 5 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ allOpen {

dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("org.springframework.boot:spring-boot-starter-validation")
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.springframework.boot:spring-boot-starter-data-redis")
Expand All @@ -63,10 +62,8 @@ dependencies {
implementation("io.arrow-kt:arrow-fx-coroutines:1.2.1")
implementation("io.arrow-kt:arrow-fx-stm:1.2.1")

implementation(
platform("io.awspring.cloud:spring-cloud-aws-dependencies:3.1.0")
)
implementation("io.awspring.cloud:spring-cloud-aws-starter-parameter-store")
implementation(platform("io.awspring.cloud:spring-cloud-aws-dependencies:3.1.0"))
implementation("io.awspring.cloud:spring-cloud-aws-s3")

implementation("io.github.oshai:kotlin-logging-jvm:6.0.3")
implementation("net.logstash.logback:logstash-logback-encoder:7.4")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ data class AuthUserImpl(
}
}

const val AUTH_TOKEN_KEY = "X-AUTH-TOKEN"
const val AUTH_TOKEN_KEY = "X-MANIA-AUTH-TOKEN"

data class AuthUserToken(
val key: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,19 +38,19 @@ class BalanceFacade(

suspend fun createBalance(user: AuthUser, request: CreateBalanceRequest): GetBalanceResponse {
val analyzedData = mediaPipeService.requestImgAnalysis(
frontImgUrl = request.frontImgUrl,
sideImgUrl = request.sideImgUrl
frontImgKey = request.frontImgKey,
sideImgKey = request.sideImgKey
)

return txTemplates.writer.coExecute {
Balance(
uid = user.uid,
frontShoulderAngle = analyzedData.frontShoulderAngle,
frontPelvisAngle = analyzedData.frontPelvisAngle,
frontKneeAngle = analyzedData.frontKneeAngle,
frontAnkleAngle = analyzedData.frontAnkleAngle,
sideNeckAngle = analyzedData.sideNeckAngle,
sideBodyAngle = analyzedData.sideBodyAngle,
frontShoulderAngle = analyzedData.frontShoulderAngle.toInteger(),
frontPelvisAngle = analyzedData.frontPelvisAngle.toInteger(),
frontKneeAngle = analyzedData.frontKneeAngle.toInteger(),
frontAnkleAngle = analyzedData.frontAnkleAngle.toInteger(),
sideNeckAngle = analyzedData.sideNeckAngle.toInteger(),
sideBodyAngle = analyzedData.sideBodyAngle.toInteger(),
leftWeight = request.leftWeight.toInteger(),
rightWeight = request.rightWeight.toInteger()
).run { balanceService.saveSync(this) }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.balancemania.api.balance.application

import com.balancemania.api.client.mediapipe.MediaPipeClient
import com.balancemania.api.client.mediapipe.model.MediaPipeImgAnalysisRequest
import com.balancemania.api.client.mediapipe.model.MediaPipeImgAnalysisResponse
import com.balancemania.api.extension.withMDCContext
import kotlinx.coroutines.Dispatchers
Expand All @@ -10,9 +11,14 @@ import org.springframework.stereotype.Service
class MediaPipeService(
private val mediaPipeClient: MediaPipeClient,
) {
suspend fun requestImgAnalysis(frontImgUrl: String, sideImgUrl: String): MediaPipeImgAnalysisResponse {
suspend fun requestImgAnalysis(frontImgKey: String, sideImgKey: String): MediaPipeImgAnalysisResponse {
val body = MediaPipeImgAnalysisRequest(
frontImgKey = frontImgKey,
sideImgKey = sideImgKey
)

return withMDCContext(Dispatchers.IO) {
mediaPipeClient.getImgAnalysis(frontImgUrl, sideImgUrl)
mediaPipeClient.getImgAnalysis(body)
}
}
}
24 changes: 12 additions & 12 deletions src/main/kotlin/com/balancemania/api/balance/model/BalanceModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,22 @@ data class BalanceModel(
val uid: Long,

/** 정면 어깨 각도 */
val frontShoulderAngle: Long,
val frontShoulderAngle: Float,

/** 정면 골반 각도 */
val frontPelvisAngle: Long,
val frontPelvisAngle: Float,

/** 정면 무릎 각도 */
val frontKneeAngle: Long,
val frontKneeAngle: Float,

/** 정면 발목 각도 */
val frontAnkleAngle: Long,
val frontAnkleAngle: Float,

/** 측면 목 각도 */
val sideNeckAngle: Long,
val sideNeckAngle: Float,

/** 측면 신체 각도 */
val sideBodyAngle: Long,
val sideBodyAngle: Float,

/** 좌측 무게 */
val leftWeight: Float,
Expand All @@ -45,12 +45,12 @@ data class BalanceModel(
return BalanceModel(
id = balance.id,
uid = balance.uid,
frontPelvisAngle = balance.frontPelvisAngle,
frontShoulderAngle = balance.frontShoulderAngle,
frontKneeAngle = balance.frontKneeAngle,
frontAnkleAngle = balance.frontAnkleAngle,
sideBodyAngle = balance.sideBodyAngle,
sideNeckAngle = balance.sideNeckAngle,
frontPelvisAngle = balance.frontPelvisAngle.toRealNumber(),
frontShoulderAngle = balance.frontShoulderAngle.toRealNumber(),
frontKneeAngle = balance.frontKneeAngle.toRealNumber(),
frontAnkleAngle = balance.frontAnkleAngle.toRealNumber(),
sideBodyAngle = balance.sideBodyAngle.toRealNumber(),
sideNeckAngle = balance.sideNeckAngle.toRealNumber(),
leftWeight = balance.leftWeight.toRealNumber(),
rightWeight = balance.rightWeight.toRealNumber(),
createdAt = balance.createdAt,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.balancemania.api.balance.model.request

data class CreateBalanceRequest(
val frontImgUrl: String,
val sideImgUrl: String,
val frontImgKey: String,
val sideImgKey: String,
val leftWeight: Float,
val rightWeight: Float,
)
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ class MediaPipeClientConfig(

@Bean
fun mediaPipeClient(): MediaPipeClient {
val webClient = WebClientFactory.generateWithoutBaseUrl()
val webClient = WebClientFactory.generateWithoutBaseUrl(
connectionTimeoutMillis = 1000 * 10,
readTimeoutMillis = 1000 * 10,
writeTimeoutMillis = 1000 * 10,
)
logger.info { "initialized mediaPipe client" }
return SuspendableMediaPipeClient(webClient, mediaPipeConfig)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package com.balancemania.api.client.mediapipe

import com.balancemania.api.client.mediapipe.model.MediaPipeImgAnalysisRequest
import com.balancemania.api.client.mediapipe.model.MediaPipeImgAnalysisResponse

interface MediaPipeClient {
suspend fun getImgAnalysis(
frontImgUrl: String,
sideImgUrl: String,
body: MediaPipeImgAnalysisRequest
): MediaPipeImgAnalysisResponse
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.balancemania.api.client.mediapipe

import com.balancemania.api.client.mediapipe.model.MediaPipeImgAnalysisRequest
import com.balancemania.api.client.mediapipe.model.MediaPipeImgAnalysisResponse
import com.balancemania.api.config.MediaPipeConfig
import com.balancemania.api.exception.ErrorCode
Expand All @@ -13,9 +14,10 @@ class SuspendableMediaPipeClient(
) : MediaPipeClient {
private val logger = KotlinLogging.logger { }

override suspend fun getImgAnalysis(frontImgUrl: String, sideImgUrl: String): MediaPipeImgAnalysisResponse {
override suspend fun getImgAnalysis(body: MediaPipeImgAnalysisRequest): MediaPipeImgAnalysisResponse {
return webClient.post()
.uri(mediaPipeConfig.url)
.bodyValue(body)
.retrieve()
.bodyToMono(MediaPipeImgAnalysisResponse::class.java)
.block() ?: throw FailToExecuteException(ErrorCode.EXTERNAL_SERVER_ERROR)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.balancemania.api.client.mediapipe.model

data class MediaPipeImgAnalysisRequest(
/** 정면 사진 키 */
val frontImgKey: String,
/** 측면 사진 키 */
val sideImgKey: String,
)
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,20 @@ package com.balancemania.api.client.mediapipe.model

data class MediaPipeImgAnalysisResponse(
/** 정면 어깨 각도 */
val frontShoulderAngle: Long,
val frontShoulderAngle: Float,

/** 정면 골반 각도 */
val frontPelvisAngle: Long,
val frontPelvisAngle: Float,

/** 정면 무릎 각도 */
val frontKneeAngle: Long,
val frontKneeAngle: Float,

/** 정면 발목 각도 */
val frontAnkleAngle: Long,
val frontAnkleAngle: Float,

/** 측면 목 각도 */
val sideNeckAngle: Long,
val sideNeckAngle: Float,

/** 측면 신체 각도 */
val sideBodyAngle: Long,
val sideBodyAngle: Float,
)
18 changes: 18 additions & 0 deletions src/main/kotlin/com/balancemania/api/config/S3Config.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.balancemania.api.config

import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider
import software.amazon.awssdk.regions.Region
import software.amazon.awssdk.services.s3.presigner.S3Presigner

@Configuration
class S3Config {
@Bean
fun s3Presigner(): S3Presigner {
return S3Presigner.builder()
.credentialsProvider(DefaultCredentialsProvider.create())
.region(Region.AP_NORTHEAST_2)
.build()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,5 @@ class MdcFilter : Filter {
logger.info { "[${MDC.get(MDC_KEY_TRACE_ID)}] ${httpRes.status} ${httpReq.requestURI} " }
MDC.clear()
}

}
}
Original file line number Diff line number Diff line change
@@ -1,25 +1,40 @@
package com.balancemania.api.config.swagger

import com.balancemania.api.auth.model.AUTH_TOKEN_KEY
import com.balancemania.api.auth.model.AuthUser
import io.swagger.v3.oas.models.Components
import io.swagger.v3.oas.models.OpenAPI
import io.swagger.v3.oas.models.info.Info
import io.swagger.v3.oas.models.security.SecurityRequirement
import io.swagger.v3.oas.models.security.SecurityScheme
import io.swagger.v3.oas.models.servers.Server
import org.springdoc.core.utils.SpringDocUtils
import org.springframework.boot.info.BuildProperties
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.web.reactive.result.view.RequestContext
import org.springframework.web.server.WebSession

@Configuration
class SwaggerConfig(
class SpringDocConfig(
private val buildProperties: BuildProperties,
) {
init {
SpringDocUtils
.getConfig()
.addRequestWrapperToIgnore(
AuthUser::class.java,
WebSession::class.java,
RequestContext::class.java
)
}

@Bean
fun openApi(): OpenAPI {
// val securityRequirement = SecurityRequirement().addList(AUTH_TOKEN_KEY)
val securityRequirement = SecurityRequirement().addList(AUTH_TOKEN_KEY)
return OpenAPI()
// .components(authSetting())
// .security(listOf(securityRequirement))
.components(authSetting())
.security(listOf(securityRequirement))
.addServersItem(Server().url("/"))
.info(
Info()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,7 @@ object Zone {
fun LocalDateTime.toInstant(): Instant {
return this.toInstant(ZoneOffset.of("+09:00"))
}

fun Instant.toCurrentZone(): Instant {
return this.plusSeconds(60 * 60 * 9)
}
23 changes: 23 additions & 0 deletions src/main/kotlin/com/balancemania/api/s3/application/S3Facade.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.balancemania.api.s3.application

import com.balancemania.api.auth.model.AuthUser
import com.balancemania.api.extension.toCurrentZone
import com.balancemania.api.s3.model.ImageType
import com.balancemania.api.s3.model.response.S3PresignedUrlResponse
import org.springframework.stereotype.Service

@Service
class S3Facade(
private val s3PresignedUrlService: S3PresignedUrlService,
) {
suspend fun getPresignedUrl(user: AuthUser, imgType: ImageType): S3PresignedUrlResponse {
return s3PresignedUrlService.generatePresignedUrl(user, imgType)
.let { model ->
S3PresignedUrlResponse(
url = model.url,
exp = model.exp.toCurrentZone(),
key = model.key
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package com.balancemania.api.s3.application

import com.balancemania.api.auth.model.AuthUser
import com.balancemania.api.s3.model.ImageType
import com.balancemania.api.s3.model.S3PresignedUrlModel
import io.awspring.cloud.s3.ObjectMetadata
import io.github.oshai.kotlinlogging.KotlinLogging
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Service
import software.amazon.awssdk.services.s3.model.ObjectCannedACL
import software.amazon.awssdk.services.s3.model.PutObjectRequest
import software.amazon.awssdk.services.s3.presigner.S3Presigner
import software.amazon.awssdk.services.s3.presigner.model.PutObjectPresignRequest
import java.time.Duration
import java.util.UUID

@Service
class S3PresignedUrlService(
@Value("\${spring.cloud.aws.s3.bucket}")
private val bucket: String,
private val s3Presigner: S3Presigner,
) {
companion object {
/** 5분 */
const val PRESIGNED_EXP_TIME = 5L
}

val logger = KotlinLogging.logger { }

suspend fun generatePresignedUrl(user: AuthUser, imageType: ImageType): S3PresignedUrlModel {
val fileName = "${user.uid}/${UUID.randomUUID()}.${imageType.type}"

val metadata = ObjectMetadata.builder()
.acl(ObjectCannedACL.PUBLIC_READ)
.build()

val putObjectRequest = PutObjectRequest.builder()
.key(fileName)
.contentType("image/${imageType.type}")
.bucket(bucket)
.metadata(metadata.metadata)
.build()

val preSignRequest = PutObjectPresignRequest.builder()
.signatureDuration(Duration.ofMinutes(PRESIGNED_EXP_TIME))
.putObjectRequest(putObjectRequest)
.build()

val presignedRequest = s3Presigner.presignPutObject(preSignRequest)

return S3PresignedUrlModel(
url = presignedRequest.url().toString(),
exp = presignedRequest.expiration(),
key = fileName
)
}
}
9 changes: 9 additions & 0 deletions src/main/kotlin/com/balancemania/api/s3/model/ImageType.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.balancemania.api.s3.model

enum class ImageType(val type: String) {
JPEG("jpeg"),
PNG("png"),
JPG("jpeg"),

;
}
Loading

0 comments on commit f0c37db

Please sign in to comment.