Skip to content

Commit

Permalink
Merge pull request #19 from YAPP-Github/feature/#17
Browse files Browse the repository at this point in the history
feat: ์‚ฌ์ง„ ์„œ๋น„์Šค ์š”์ฒญ Validation ์ž‘์—…
  • Loading branch information
CChuYong authored Jul 12, 2024
2 parents 4602744 + 2999ab7 commit d0292a8
Show file tree
Hide file tree
Showing 12 changed files with 144 additions and 7 deletions.
1 change: 1 addition & 0 deletions photo-service/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ dependencies {
implementation("org.springframework.boot:spring-boot-starter-data-r2dbc")
implementation("org.springframework.boot:spring-boot-starter-security")
implementation("org.springframework.boot:spring-boot-starter-webflux")
implementation("org.springframework.boot:spring-boot-starter-validation")
implementation("org.flywaydb:flyway-core")
implementation("org.flywaydb:flyway-mysql")
implementation("org.springframework:spring-jdbc")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package kr.mafoo.photo.annotation;

import jakarta.validation.Constraint;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import jakarta.validation.Payload;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Constraint(validatedBy = {MatchEnum.EnumValidator.class})
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface MatchEnum {
String message() default "ENUM ํƒ€์ž…์ด ์˜ฌ๋ฐ”๋ฅด์ง€ ์•Š์Šต๋‹ˆ๋‹ค";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
Class<? extends java.lang.Enum<?>> enumClass();

class EnumValidator implements ConstraintValidator<MatchEnum, String> {
private MatchEnum annotation;

@Override
public void initialize(MatchEnum constraintAnnotation) {
this.annotation = constraintAnnotation;
}

@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (value == null) return false;
boolean result = false;
Object[] enumValues = this.annotation.enumClass().getEnumConstants();
if (enumValues != null) {
for (Object enumValue : enumValues) {
if (value.equals(enumValue.toString())) {
result = true;
break;
}
}
}
return result;
}
}
}
25 changes: 25 additions & 0 deletions photo-service/src/main/java/kr/mafoo/photo/annotation/ULID.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package kr.mafoo.photo.annotation;

import jakarta.validation.Constraint;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = {ULID.ULIDValidator.class})
public @interface ULID {
String message() default "ULID ํ˜•์‹์ด ์•„๋‹™๋‹ˆ๋‹ค";
Class[] groups() default {};
Class[] payload() default {};

class ULIDValidator implements ConstraintValidator<ULID, String> {
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (value == null) return false;
return value.matches("[0-7][0-9A-HJKMNP-TV-Z]{25}");
}
}

}
9 changes: 9 additions & 0 deletions photo-service/src/main/java/kr/mafoo/photo/api/AlbumApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,18 @@
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import kr.mafoo.photo.annotation.RequestMemberId;
import kr.mafoo.photo.annotation.ULID;
import kr.mafoo.photo.controller.dto.request.AlbumCreateRequest;
import kr.mafoo.photo.controller.dto.request.AlbumUpdateRequest;
import kr.mafoo.photo.controller.dto.response.AlbumResponse;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@Validated
@Tag(name = "์•จ๋ฒ” ๊ด€๋ จ API", description = "์•จ๋ฒ” ์กฐํšŒ, ์ƒ์„ฑ, ์ˆ˜์ •, ์‚ญ์ œ ๋“ฑ API")
@RequestMapping("/v1/albums")
public interface AlbumApi {
Expand All @@ -27,6 +31,7 @@ Mono<AlbumResponse> getAlbum(
@RequestMemberId
String memberId,

@ULID
@Parameter(description = "์•จ๋ฒ” ID", example = "test_album_id")
@PathVariable
String albumId
Expand All @@ -38,6 +43,7 @@ Mono<AlbumResponse> createAlbum(
@RequestMemberId
String memberId,

@Valid
@RequestBody
AlbumCreateRequest request
);
Expand All @@ -48,10 +54,12 @@ Mono<AlbumResponse> updateAlbum(
@RequestMemberId
String memberId,

@ULID
@Parameter(description = "์•จ๋ฒ” ID", example = "test_album_id")
@PathVariable
String albumId,

@Valid
@RequestBody
AlbumUpdateRequest request
);
Expand All @@ -62,6 +70,7 @@ Mono<Void> deleteAlbum(
@RequestMemberId
String memberId,

@ULID
@Parameter(description = "์•จ๋ฒ” ID", example = "test_album_id")
@PathVariable
String albumId
Expand Down
9 changes: 9 additions & 0 deletions photo-service/src/main/java/kr/mafoo/photo/api/PhotoApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,18 @@
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import kr.mafoo.photo.annotation.RequestMemberId;
import kr.mafoo.photo.annotation.ULID;
import kr.mafoo.photo.controller.dto.request.PhotoCreateRequest;
import kr.mafoo.photo.controller.dto.request.PhotoUpdateAlbumIdRequest;
import kr.mafoo.photo.controller.dto.response.PhotoResponse;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@Validated
@Tag(name = "์‚ฌ์ง„ ๊ด€๋ จ API", description = "์‚ฌ์ง„ ์กฐํšŒ, ์ƒ์„ฑ, ์ˆ˜์ •, ์‚ญ์ œ ๋“ฑ API")
@RequestMapping("/v1/photos")
public interface PhotoApi {
Expand All @@ -20,6 +24,7 @@ Flux<PhotoResponse> getPhotos(
@RequestMemberId
String memberId,

@ULID
@Parameter(description = "์•จ๋ฒ” ID", example = "test_album_id")
@RequestParam
String albumId
Expand All @@ -31,6 +36,7 @@ Mono<PhotoResponse> createPhoto(
@RequestMemberId
String memberId,

@Valid
@RequestBody
PhotoCreateRequest request
);
Expand All @@ -41,10 +47,12 @@ Mono<PhotoResponse> updatePhotoAlbum(
@RequestMemberId
String memberId,

@ULID
@Parameter(description = "์‚ฌ์ง„ ID", example = "test_photo_id")
@PathVariable
String photoId,

@Valid
@RequestBody
PhotoUpdateAlbumIdRequest request
);
Expand All @@ -55,6 +63,7 @@ Mono<Void> deletePhoto(
@RequestMemberId
String memberId,

@ULID
@Parameter(description = "์‚ฌ์ง„ ID", example = "test_photo_id")
@PathVariable
String photoId
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,45 @@
package kr.mafoo.photo.config;

import jakarta.validation.ConstraintViolationException;
import kr.mafoo.photo.controller.dto.response.ErrorResponse;
import kr.mafoo.photo.exception.DomainException;
import kr.mafoo.photo.exception.ErrorCode;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.bind.support.WebExchangeBindException;

@ControllerAdvice
@RestControllerAdvice
public class WebExceptionHandler {
@ExceptionHandler(DomainException.class)
public ResponseEntity<ErrorResponse> handleDomainException(DomainException exception) {
return ResponseEntity
.badRequest()
.body(ErrorResponse.fromErrorCode(exception.getErrorCode()));
}

@ExceptionHandler({MethodArgumentNotValidException.class,
ConstraintViolationException.class,
WebExchangeBindException.class})
public ResponseEntity<ErrorResponse> validException(Exception ex) {
String errorMessage = "์ž…๋ ฅ๊ฐ’ ๊ฒ€์ฆ ์˜ค๋ฅ˜: ";
if (ex instanceof MethodArgumentNotValidException mex) {
errorMessage += mex.getBindingResult().getAllErrors().get(0).getDefaultMessage();
} else if (ex instanceof ConstraintViolationException cvex) {
errorMessage += cvex.getConstraintViolations().iterator().next().getMessage();
} else if (ex instanceof WebExchangeBindException wex) {
errorMessage += wex.getAllErrors().get(0).getDefaultMessage();
} else {
errorMessage += "์•Œ ์ˆ˜ ์—†๋Š” ์˜ค๋ฅ˜";
}
ErrorResponse response = new ErrorResponse(
ErrorCode.REQUEST_INPUT_NOT_VALID.getCode(),
errorMessage
);

return ResponseEntity
.badRequest()
.body(response);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,10 @@ public Mono<AlbumResponse> createAlbum(

@Override
public Mono<AlbumResponse> updateAlbum(String memberId, String albumId, AlbumUpdateRequest request) {
AlbumType albumType = AlbumType.valueOf(request.type());
return albumService
.updateAlbumName(albumId, request.name(), memberId)
.then(albumService.updateAlbumType(albumId, request.type(), memberId))
.then(albumService.updateAlbumType(albumId, albumType, memberId))
.map(AlbumResponse::fromEntity);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
package kr.mafoo.photo.controller.dto.request;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import kr.mafoo.photo.annotation.MatchEnum;
import kr.mafoo.photo.domain.AlbumType;
import org.hibernate.validator.constraints.Length;

@Schema(description = "์•จ๋ฒ” ์ƒ์„ฑ ์š”์ฒญ")
public record AlbumCreateRequest(
@NotBlank
@Length(min = 1, max = 100)
@Schema(description = "์•จ๋ฒ” ์ด๋ฆ„", example = "์‹œ๊ธˆ์น˜ํŒŒ์Šทํ•˜")
String name,

@Schema(description = "์•จ๋ฒ” ํƒ€์ž…", example = "TYPE_A")
@MatchEnum(enumClass = AlbumType.class)
@Schema(description = "์•จ๋ฒ” ํƒ€์ž…")
String type
) {
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
package kr.mafoo.photo.controller.dto.request;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import kr.mafoo.photo.annotation.MatchEnum;
import kr.mafoo.photo.domain.AlbumType;
import org.hibernate.validator.constraints.Length;

@Schema(description = "์•จ๋ฒ” ์ˆ˜์ • ์š”์ฒญ")
public record AlbumUpdateRequest(
@NotBlank
@Length(min = 1, max = 100)
@Schema(description = "์•จ๋ฒ” ์ด๋ฆ„", example = "์‹œ๊ธˆ์น˜ํŒŒ์Šทํ•˜")
String name,

@Schema(description = "์•จ๋ฒ” ํƒ€์ž…", example = "TYPE_A")
AlbumType type
@MatchEnum(enumClass = AlbumType.class)
@Schema(description = "์•จ๋ฒ” ํƒ€์ž…")
String type
) {
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package kr.mafoo.photo.controller.dto.request;

import io.swagger.v3.oas.annotations.media.Schema;
import org.hibernate.validator.constraints.URL;

@Schema(description = "์‚ฌ์ง„ ์ƒ์„ฑ ์š”์ฒญ")
public record PhotoCreateRequest(
@URL
@Schema(description = "QR URL", example = "qr_url")
String qrUrl
) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package kr.mafoo.photo.controller.dto.request;

import io.swagger.v3.oas.annotations.media.Schema;
import kr.mafoo.photo.annotation.ULID;

@Schema(description = "์‚ฌ์ง„ ์•จ๋ฒ” ์ˆ˜์ • ์š”์ฒญ")
public record PhotoUpdateAlbumIdRequest(
@ULID
@Schema(description = "์•จ๋ฒ” ID", example = "test_album_id")
String albumId
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
@RequiredArgsConstructor
public enum ErrorCode {

REDIRECT_URI_NOT_FOUND("EX001", "๋ฆฌ๋‹ค์ด๋ ‰ํŠธ URI๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค"),
REDIRECT_URI_NOT_FOUND("EX0001", "๋ฆฌ๋‹ค์ด๋ ‰ํŠธ URI๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค"),
REQUEST_INPUT_NOT_VALID("EX0002", "์ž…๋ ฅ ๊ฐ’์ด ์˜ฌ๋ฐ”๋ฅด์ง€ ์•Š์Šต๋‹ˆ๋‹ค."),

ALBUM_NOT_FOUND("AE0001", "์•จ๋ฒ”์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค"),
PHOTO_NOT_FOUND("PE0001", "์‚ฌ์ง„์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค"),
Expand Down

0 comments on commit d0292a8

Please sign in to comment.