From 07e83109dfbb79c1e9e7200cc0c3bb245544782b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=86=A1=EC=98=81=EB=AF=BC?= Date: Sun, 8 Sep 2024 17:27:46 +0900 Subject: [PATCH] feat: add admin apis --- admin-service/build.gradle.kts | 2 +- .../kr/mafoo/admin/config/WebClientConfig.kt | 10 +++- .../mafoo/admin/controller/AdminController.kt | 59 ++++++++++++++++--- .../src/main/resources/application.yaml | 5 ++ 4 files changed, 65 insertions(+), 11 deletions(-) diff --git a/admin-service/build.gradle.kts b/admin-service/build.gradle.kts index 5604c09..849b6f8 100644 --- a/admin-service/build.gradle.kts +++ b/admin-service/build.gradle.kts @@ -22,7 +22,7 @@ repositories { dependencies { implementation("org.springframework.boot:spring-boot-starter-actuator") - implementation("org.springframework.boot:spring-boot-starter-security") + //implementation("org.springframework.boot:spring-boot-starter-security") implementation("org.springframework.boot:spring-boot-starter-webflux") implementation("com.fasterxml.jackson.module:jackson-module-kotlin") implementation("org.jetbrains.kotlin:kotlin-reflect") diff --git a/admin-service/src/main/kotlin/kr/mafoo/admin/config/WebClientConfig.kt b/admin-service/src/main/kotlin/kr/mafoo/admin/config/WebClientConfig.kt index 8a5e02e..00c997a 100644 --- a/admin-service/src/main/kotlin/kr/mafoo/admin/config/WebClientConfig.kt +++ b/admin-service/src/main/kotlin/kr/mafoo/admin/config/WebClientConfig.kt @@ -1,17 +1,21 @@ package kr.mafoo.admin.config +import org.springframework.beans.factory.annotation.Value import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.http.codec.ClientCodecConfigurer import org.springframework.web.reactive.function.client.WebClient @Configuration -class WebClientConfig { +class WebClientConfig( + @Value("\${app.url.user-service}") private val userServiceUrl: String, + @Value("\${app.url.photo-service}") private val photoServiceUrl: String, +) { @Bean("userServiceWebClient") fun userServiceWebClient(): WebClient { return WebClient .builder() - .baseUrl("http://user-serivce") + .baseUrl(userServiceUrl) .codecs { clientCodecConfigurer: ClientCodecConfigurer -> clientCodecConfigurer .defaultCodecs() @@ -24,7 +28,7 @@ class WebClientConfig { fun photoServiceWebClient(): WebClient { return WebClient .builder() - .baseUrl("http://photo-serivce") + .baseUrl(photoServiceUrl) .codecs { clientCodecConfigurer: ClientCodecConfigurer -> clientCodecConfigurer .defaultCodecs() diff --git a/admin-service/src/main/kotlin/kr/mafoo/admin/controller/AdminController.kt b/admin-service/src/main/kotlin/kr/mafoo/admin/controller/AdminController.kt index 45db5a3..0730dab 100644 --- a/admin-service/src/main/kotlin/kr/mafoo/admin/controller/AdminController.kt +++ b/admin-service/src/main/kotlin/kr/mafoo/admin/controller/AdminController.kt @@ -1,12 +1,17 @@ package kr.mafoo.admin.controller +import kotlinx.coroutines.reactor.awaitSingle import org.springframework.beans.factory.annotation.Qualifier +import org.springframework.core.io.buffer.DataBuffer import org.springframework.http.MediaType +import org.springframework.http.client.MultipartBodyBuilder import org.springframework.http.codec.multipart.FilePart +import org.springframework.util.MultiValueMap import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestPart import org.springframework.web.bind.annotation.RestController +import org.springframework.web.reactive.function.BodyInserters import org.springframework.web.reactive.function.client.WebClient import reactor.core.publisher.Flux @@ -15,18 +20,58 @@ import reactor.core.publisher.Flux class AdminController( @Qualifier("photoServiceWebClient") private val photoWebClient: WebClient, ) { - @PostMapping("/upload-image", consumes = [MediaType.MULTIPART_FORM_DATA_VALUE]) + companion object { + const val MEMBER_ID_HEADER_KEY = "X-MEMBER-ID" + } + + @PostMapping("/upload-image-with-new-album", + consumes = [MediaType.MULTIPART_FORM_DATA_VALUE], + produces = [MediaType.APPLICATION_JSON_VALUE] + ) suspend fun uploadImage( - memberId: String, - @RequestPart("files") files: Flux - ) { - photoWebClient + @RequestPart("memberId") memberId: String, + @RequestPart("files") files: Flux, + @RequestPart("albumName") albumName: String, + ): Flux> { + // #1. upload photos + val multipartBuilder = MultipartBodyBuilder() + files.collectList().awaitSingle().forEach { + multipartBuilder + .asyncPart("files", it.content(), DataBuffer::class.java) + .filename(it.filename()) + } + + val uploadedPhotos = photoWebClient .post() .uri("/v1/photos/files") - .header("X-MEMBER-ID", memberId) + .header(MEMBER_ID_HEADER_KEY, memberId) .contentType(MediaType.MULTIPART_FORM_DATA) - .bodyValue(files) + .body(BodyInserters.fromMultipartData(multipartBuilder.build())) + .retrieve() + .bodyToFlux(LinkedHashMap::class.java) + .collectList() + .awaitSingle() + + // #2. create album + val newAlbum = photoWebClient + .post() + .uri("/v1/albums") + .header(MEMBER_ID_HEADER_KEY, memberId) + .bodyValue(mapOf("name" to albumName, "type" to "HEART")) .retrieve() + .bodyToMono(LinkedHashMap::class.java) + .awaitSingle() + val albumId = newAlbum["albumId"] ?: throw RuntimeException() + + // #3. bulk update album ids + val photoIds = uploadedPhotos.map { it["photoId"] ?: throw RuntimeException() } + return photoWebClient + .patch() + .uri("/v1/photos/bulk/album") + .header(MEMBER_ID_HEADER_KEY, memberId) + .bodyValue(mapOf("albumId" to albumId, "photoIds" to photoIds)) + .retrieve() + .bodyToFlux(LinkedHashMap::class.java) } } diff --git a/admin-service/src/main/resources/application.yaml b/admin-service/src/main/resources/application.yaml index e6c2369..743d3e4 100644 --- a/admin-service/src/main/resources/application.yaml +++ b/admin-service/src/main/resources/application.yaml @@ -7,3 +7,8 @@ management: web: exposure: include: health,metrics,prometheus + +app: + url: + photo-service: http://photo-service + user-service: http://user-service