Skip to content

Commit

Permalink
Merge branch 'main' into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
pminsung12 authored Jul 16, 2024
2 parents 2a69f41 + d8309ce commit 632a77d
Show file tree
Hide file tree
Showing 41 changed files with 926 additions and 32 deletions.
1 change: 1 addition & 0 deletions application/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ dependencies {
implementation(project(":domain"))
implementation(project(":usecase"))
implementation(project(":infrastructure:jpa"))
implementation(project(":infrastructure:ncp"))

// spring
implementation("org.springframework.boot:spring-boot-starter")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package org.depromeet.spot.application.common.config;

import org.depromeet.spot.jpa.config.JpaConfig;
import org.depromeet.spot.ncp.NcpConfig;
import org.depromeet.spot.usecase.config.UsecaseConfig;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@ComponentScan(basePackages = {"org.depromeet.spot.application"})
@Configuration
@Import(value = {UsecaseConfig.class, JpaConfig.class})
@Import(value = {UsecaseConfig.class, JpaConfig.class, NcpConfig.class})
public class SpotApplicationConfig {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package org.depromeet.spot.application.media;

import jakarta.validation.Valid;

import org.depromeet.spot.application.media.dto.request.CreatePresignedUrlRequest;
import org.depromeet.spot.application.media.dto.response.MediaUrlResponse;
import org.depromeet.spot.usecase.port.out.media.CreatePresignedUrlPort;
import org.depromeet.spot.usecase.port.out.media.CreatePresignedUrlPort.PresignedUrlRequest;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1")
@Tag(name = "미디어 (이미지, 영상)")
public class MediaController {

private final CreatePresignedUrlPort createPresignedUrlPort;

@ResponseStatus(HttpStatus.CREATED)
@PostMapping(value = "/members/{memberId}/reviews/images")
@Operation(summary = "리뷰 이미지 업로드 url을 생성합니다.")
public MediaUrlResponse createReviewImageUploadUrl(
@PathVariable Long memberId, @RequestBody @Valid CreatePresignedUrlRequest request) {
PresignedUrlRequest command =
new PresignedUrlRequest(request.fileExtension(), request.property());
String presignedUrl = createPresignedUrlPort.forReview(memberId, command);
return new MediaUrlResponse(presignedUrl);
}

@ResponseStatus(HttpStatus.CREATED)
@PostMapping(value = "/stadiums/images")
@Operation(summary = "공연장 이미지 업로드 url을 생성합니다.")
public MediaUrlResponse createStadiumSeatUploadUrl(
@RequestBody @Valid CreatePresignedUrlRequest request) {
PresignedUrlRequest command =
new PresignedUrlRequest(request.fileExtension(), request.property());
String presignedUrl = createPresignedUrlPort.forStadiumSeat(command);
return new MediaUrlResponse(presignedUrl);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package org.depromeet.spot.application.media.dto.request;

import org.depromeet.spot.domain.media.MediaProperty;

public record CreatePresignedUrlRequest(String fileExtension, MediaProperty property) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package org.depromeet.spot.application.media.dto.response;

public record MediaUrlResponse(String presignedUrl) {}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import org.depromeet.spot.application.member.dto.request.MemberRequest;
import org.depromeet.spot.application.member.dto.response.MemberResponse;
import org.depromeet.spot.usecase.port.in.MemberUsecase;
import org.depromeet.spot.usecase.port.in.member.MemberUsecase;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
Expand Down
4 changes: 3 additions & 1 deletion application/src/main/resources/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ server:
spring:
# 서브모듈 profile
profiles:
include: jpa
include:
- jpa
- ncp
# swagger를 이용해 API 명세서 생성
doc:
swagger-ui:
Expand Down
5 changes: 5 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ subprojects {
// lombok
compileOnly("org.projectlombok:lombok:_")
annotationProcessor("org.projectlombok:lombok:_")
testCompileOnly("org.projectlombok:lombok:_")
testAnnotationProcessor("org.projectlombok:lombok:_")

// configuration processor
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor:_")

// test
testImplementation("org.springframework.boot:spring-boot-starter-test:_")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.depromeet.spot.common.exception.media;

import org.depromeet.spot.common.exception.ErrorCode;
import org.springframework.http.HttpStatus;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public enum MediaErrorCode implements ErrorCode {
INVALID_EXTENSION(HttpStatus.BAD_REQUEST, "ME001", "허용하지 않는 확장자입니다."),
INVALID_STADIUM_MEDIA(HttpStatus.BAD_REQUEST, "ME002", "경기장과 관련된 미디어 파일이 아닙니다."),
INVALID_REVIEW_MEDIA(HttpStatus.BAD_REQUEST, "ME003", "리뷰와 관련된 미디어 파일이 아닙니다."),
INVALID_MEDIA(HttpStatus.INTERNAL_SERVER_ERROR, "ME004", "잘못된 미디어 형식입니다."),
;

private final HttpStatus status;
private final String code;
private String message;

public MediaErrorCode appended(final String s) {
message = message + " {" + s + "}";
return this;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package org.depromeet.spot.common.exception.media;

import org.depromeet.spot.common.exception.BusinessException;

public abstract class MediaException extends BusinessException {

protected MediaException(MediaErrorCode errorCode) {
super(errorCode);
}

public static class InvalidExtensionException extends MediaException {
public InvalidExtensionException() {
super(MediaErrorCode.INVALID_EXTENSION);
}

public InvalidExtensionException(final String s) {
super(MediaErrorCode.INVALID_EXTENSION.appended(s));
}
}

public static class InvalidStadiumMediaException extends MediaException {
public InvalidStadiumMediaException() {
super(MediaErrorCode.INVALID_STADIUM_MEDIA);
}
}

public static class InvalidReviewMediaException extends MediaException {
public InvalidReviewMediaException() {
super(MediaErrorCode.INVALID_REVIEW_MEDIA);
}
}

public static class InvalidMediaException extends MediaException {
public InvalidMediaException() {
super(MediaErrorCode.INVALID_MEDIA);
}
}
}
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,4 @@ services:
volumes:
- ./:/app
- ~/.gradle:/root/.gradle
command: ./gradlew :application:bootRun
command: ./gradlew :application:bootRun
28 changes: 28 additions & 0 deletions domain/src/main/java/org/depromeet/spot/domain/media/Media.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package org.depromeet.spot.domain.media;

import org.depromeet.spot.common.exception.media.MediaException.InvalidMediaException;

import lombok.Getter;

@Getter
public class Media {

private final String url;
private final String fileName;

public Media(final String url, final String fileName) {
checkIsValidMedia(url, fileName);
this.url = url;
this.fileName = fileName;
}

private void checkIsValidMedia(final String url, final String fileName) {
if (isBlankOrNull(url) || isBlankOrNull(fileName)) {
throw new InvalidMediaException();
}
}

private boolean isBlankOrNull(final String str) {
return str == null || str.isBlank();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.depromeet.spot.domain.media;

public enum MediaProperty {
REVIEW,
STADIUM,
;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package org.depromeet.spot.domain.media.extension;

import java.util.Arrays;
import java.util.Map;
import java.util.stream.Collectors;

import org.depromeet.spot.common.exception.media.MediaException.InvalidExtensionException;

import lombok.Getter;

@Getter
public enum ImageExtension {
JPG("jpg"),
JPEG("jpeg"),
PNG("png"),
;

private final String value;

private static final Map<String, ImageExtension> cachedImageExtension =
Arrays.stream(ImageExtension.values())
.collect(
Collectors.toMap(extension -> extension.value, extension -> extension));

ImageExtension(final String value) {
this.value = value;
}

public static boolean isValid(final String reqExtension) {
return cachedImageExtension.containsKey(reqExtension);
}

public static ImageExtension from(final String reqExtension) {
ImageExtension extension = cachedImageExtension.get(reqExtension);
if (extension == null) {
throw new InvalidExtensionException(reqExtension);
}
return extension;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package org.depromeet.spot.domain.media.extension;

import java.util.Arrays;
import java.util.Map;
import java.util.stream.Collectors;

import org.depromeet.spot.common.exception.media.MediaException.InvalidExtensionException;

import lombok.Getter;

@Getter
public enum StadiumSeatMediaExtension {
SVG("svg"),
;

private final String value;

private static final Map<String, StadiumSeatMediaExtension> cachedStadiumMedia =
Arrays.stream(StadiumSeatMediaExtension.values())
.collect(
Collectors.toMap(extension -> extension.value, extension -> extension));

StadiumSeatMediaExtension(final String value) {
this.value = value;
}

public static boolean isValid(final String reqExtension) {
return cachedStadiumMedia.containsKey(reqExtension);
}

public static StadiumSeatMediaExtension from(final String reqExtension) {
StadiumSeatMediaExtension extension = cachedStadiumMedia.get(reqExtension);
if (extension == null) {
throw new InvalidExtensionException(reqExtension);
}
return extension;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package org.depromeet.spot.domain.media;

import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;

import org.depromeet.spot.common.exception.media.MediaException.InvalidMediaException;
import org.junit.jupiter.api.Test;

class MediaTest {

@Test
public void url_공백이면_미디어를_생성할__없다() {
// given
final String url = "";
final String fileName = "file";

// when
// then
assertThatThrownBy(() -> new Media(url, fileName))
.isInstanceOf(InvalidMediaException.class);
}

@Test
public void url_null이면_미디어를_생성할__없다() {
// given
final String url = null;
final String fileName = "file";

// when
// then
assertThatThrownBy(() -> new Media(url, fileName))
.isInstanceOf(InvalidMediaException.class);
}

@Test
public void fileName_공백이면_미디어를_생성할__없다() {
// given
final String url = "url";
final String fileName = "";

// when
// then
assertThatThrownBy(() -> new Media(url, fileName))
.isInstanceOf(InvalidMediaException.class);
}

@Test
public void fileName_null이면_미디어를_생성할__없다() {
// given
final String url = "url";
final String fileName = "";

// when
// then
assertThatThrownBy(() -> new Media(url, fileName))
.isInstanceOf(InvalidMediaException.class);
}
}
Loading

0 comments on commit 632a77d

Please sign in to comment.