Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Weekly/11/Reservation/test] Reservation 서비스 유닛테스트 #140

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package org.ktc2.cokaen.wouldyouin._common.util;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.UUID;
import net.coobird.thumbnailator.Thumbnails;
import org.ktc2.cokaen.wouldyouin._common.exception.ExtensionParsingException;
import org.ktc2.cokaen.wouldyouin._common.exception.FailToReadImageException;
import org.ktc2.cokaen.wouldyouin._common.exception.FailedToUploadImageException;
Expand Down Expand Up @@ -37,6 +40,17 @@ public static void saveFile(byte[] file, Path path) {
}
}

public static void createThumbnail(String originalPath, String originFileName, String thumbnailPath, int width, int height) {
try {
Files.createDirectories(Paths.get(thumbnailPath));
Thumbnails.of(new File(originalPath))
.size(height, width)
.toFile(new File(thumbnailPath, originFileName));
} catch (IOException e) {
throw new FailedToUploadImageException("썸네일을 생성하는데 실패했습니다.");
}
}

public static void deleteFile(Path path) {
try {
Files.deleteIfExists(path);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ public enum Category {
전체,
밴드,
연극,
뮤지컬,
원데이클래스,
뮤지컬,
전시회,
공예,
축제
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,6 @@ public ResponseEntity<ApiResponseBody<AdvertisementResponse>> createAdvertisemen
return ApiResponse.created(advertisementService.create(advertisementRequest, image));
}

@PutMapping(path = "/{adId}", consumes = {MediaType.APPLICATION_JSON_VALUE,
MediaType.MULTIPART_FORM_DATA_VALUE})
public ResponseEntity<ApiResponseBody<AdvertisementResponse>> updateAdvertisement(
@PathVariable Long adId,
@Valid @RequestPart AdvertisementRequest advertisementRequest,
@RequestPart(required = false) MultipartFile image,
@Authorize(MemberType.admin) MemberIdentifier identifier) {
return ApiResponse.ok(advertisementService.update(identifier, adId, advertisementRequest, image));
}

@DeleteMapping("/{adId}")
public ResponseEntity<ApiResponseBody<Void>> deleteAdvertisement(
@PathVariable Long adId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,6 @@ public class AdvertisementService {
private final AdvertisementRepository adRepository;
private final AdvertisementImageService adImageService;

@Transactional
public Advertisement getByIdOrThrow(Long adId) {
return adRepository.findById(adId)
.orElseThrow(() -> new EntityNotFoundException("해당하는 광고를 찾을 수 없습니다."));
}

@Transactional(readOnly = true)
public AdvertisementResponse getById(Long adId) {
return AdvertisementResponse.from(getByIdOrThrow(adId));
Expand All @@ -40,7 +34,6 @@ public List<AdvertisementResponse> getAllActiveAdvertisements() {
.map(AdvertisementResponse::from).toList();
}

// Todo: 롤백될 경우, 저장한 이미지 삭제
@Transactional
public AdvertisementResponse create(AdvertisementRequest adRequest, MultipartFile image) {
AdvertisementImage adImage = adImageService.saveImage(image);
Expand All @@ -49,30 +42,15 @@ public AdvertisementResponse create(AdvertisementRequest adRequest, MultipartFil
return AdvertisementResponse.from(ad);
}

// Todo: 수정될 때 이미지가 null인 경우 기존 이미지로 대체하는 로직 프론트와 협의
// Todo: 롤백될 경우, 저장한 이미지 삭제
@Transactional
public AdvertisementResponse update(MemberIdentifier identifier, Long adId, AdvertisementRequest adRequest, MultipartFile multipartFile) {
Advertisement ad = getByIdOrThrow(adId);
Optional.ofNullable(multipartFile).ifPresentOrElse(
image -> {
adImageService.deleteImage(identifier, ad.getAdvertisementImage().getId());
AdvertisementImage adImage = adImageService.saveImage(image);
ad.updateFrom(adRequest, adImage);
adImage.setAdvertisement(ad);
},
() -> {
AdvertisementImage adImage = ad.getAdvertisementImage();
ad.updateFrom(adRequest, adImage);
}
);
return AdvertisementResponse.from(ad);
}

@Transactional
public void delete(MemberIdentifier identifier, Long adId) {
Advertisement ad = getByIdOrThrow(adId);
adImageService.deleteImage(identifier, ad.getAdvertisementImage().getId());
adRepository.deleteById(adId);
}

private Advertisement getByIdOrThrow(Long adId) {
return adRepository.findById(adId)
.orElseThrow(() -> new EntityNotFoundException("해당하는 광고를 찾을 수 없습니다."));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ private CurationCard getByIdOrThrow(Long id) {
return curationCardRepository.findById(id).orElseThrow(() -> new EntityNotFoundException("해당하는 큐레이션 카드를 찾을 수 없습니다."));
}

public List<String> getImageUrls(CurationCard curationCard) {
private List<String> getImageUrls(CurationCard curationCard) {
return curationCard.getCurationImages().stream()
.map(curationImageService::getImageUrl)
.toList();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import org.ktc2.cokaen.wouldyouin.image.application.ImageServiceFactory;
import org.ktc2.cokaen.wouldyouin.image.application.ImageStorageService;
import org.ktc2.cokaen.wouldyouin.member.persist.MemberType;
import org.ktc2.cokaen.wouldyouin.payment.application.PaymentService;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
Expand All @@ -31,15 +30,15 @@ public class ImageController {

private final ImageServiceFactory imageServiceFactory;
private final ImageStorageService imageStorageService;
private final PaymentService paymentService;

@GetMapping(value = "/{directory}/{file}", produces = {MediaType.IMAGE_PNG_VALUE, MediaType.IMAGE_JPEG_VALUE})
public ResponseEntity<byte[]> getImage(@PathVariable String directory, @PathVariable String file) {
return ResponseEntity.status(HttpStatus.OK).body(imageStorageService.readFromDirectory(Paths.get(directory, file)));
}

@GetMapping(value = "/{directory}/{thumbnail}/{file}", produces = {MediaType.IMAGE_PNG_VALUE, MediaType.IMAGE_JPEG_VALUE})
public ResponseEntity<byte[]> getThumnailImage(@PathVariable String directory, @PathVariable String thumbnail, @PathVariable String file) {
public ResponseEntity<byte[]> getThumnailImage(@PathVariable String directory, @PathVariable String thumbnail,
@PathVariable String file) {
return ResponseEntity.status(HttpStatus.OK).body(imageStorageService.readFromDirectory(Paths.get(directory, thumbnail, file)));
}

Expand All @@ -51,7 +50,6 @@ public ResponseEntity<ApiResponseBody<List<ImageResponse>>> uploadImages(
return ApiResponse.ok(imageServiceFactory.getImageService(imageDomain).saveImages(images));
}

// Todo: 삭제로직 수정필요
@DeleteMapping("/{imageId}")
public ResponseEntity<ApiResponseBody<Void>> deleteImage(
@PathVariable Long imageId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,17 @@
import org.springframework.web.multipart.MultipartFile;

@Service
@RequiredArgsConstructor
public class AdvertisementImageService extends ImageService<AdvertisementImage> {

@Value("${image.upload.ad.child-path}")
private String childPath;
private final ImageStorageService imageStorageService;
private final AdvertisementImageRepository adImageRepository;

public AdvertisementImageService(ImageStorageService imageStorageService, AdvertisementImageRepository adImageRepository) {
this.imageStorageService = imageStorageService;
this.adImageRepository = adImageRepository;
}

@Override
protected ImageRepository<AdvertisementImage> getImageRepository() {
return adImageRepository;
Expand All @@ -48,7 +51,6 @@ protected AdvertisementImage mapToEntityFrom(ImageRequest imageRequest) {
.build();
}

// TODO : ad image 삭제 인가
@Override
protected void validateMemberId(MemberIdentifier identifier, AdvertisementImage image) {
if (!identifier.type().equals(MemberType.admin)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,17 @@
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
public class CurationImageService extends ImageService<CurationImage> {

@Value("${image.upload.curation.child-path}")
private String childPath;
private final CurationImageRepository curationImageRepository;

public CurationImageService(ImageStorageService imageStorageService, CurationImageRepository curationImageRepository) {
this.imageStorageService = imageStorageService;
this.curationImageRepository = curationImageRepository;
}

@Override
public ImageRepository<CurationImage> getImageRepository() {
return curationImageRepository;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,18 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@RequiredArgsConstructor
@Service
public class EventImageService extends ImageService<EventImage> {

@Value("${image.upload.event.child-path}")
private String childPath;
private final EventImageRepository eventImageRepository;

public EventImageService(ImageStorageService imageStorageService, EventImageRepository eventImageRepository) {
this.imageStorageService = imageStorageService;
this.eventImageRepository = eventImageRepository;
}

@Override
public ImageRepository<EventImage> getImageRepository() {
return eventImageRepository;
Expand All @@ -48,7 +52,8 @@ protected EventImage mapToEntityFrom(ImageRequest imageRequest) {

@Override
protected void validateMemberId(MemberIdentifier identifier, EventImage image) {
if (!identifier.type().equals(MemberType.admin) && !identifier.id().equals(image.getEvent().getHost().getId())) {
if (!identifier.type().equals(MemberType.admin) &&
!identifier.id().equals(image.getEvent().getHost().getId())) {
throw new UnauthorizedException("해당 이벤트 이미지에 접근할 권한이 없습니다.");
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import org.ktc2.cokaen.wouldyouin.image.api.dto.ImageResponse;
import org.ktc2.cokaen.wouldyouin.image.persist.Image;
import org.ktc2.cokaen.wouldyouin.image.persist.ImageRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand All @@ -20,11 +19,10 @@
@RequiredArgsConstructor
public abstract class ImageService<T extends Image> {

@Autowired
protected ImageStorageService imageStorageService;

@Value("${image.api-url}")
private String parentPath;
private String imageApiHeader;

protected abstract ImageRepository<T> getImageRepository();

Expand All @@ -36,43 +34,42 @@ public abstract class ImageService<T extends Image> {

protected abstract void validateMemberId(MemberIdentifier identifier, T image);

@Transactional(readOnly = true)
public T getById(Long id) {
return getImageRepository().findById(id)
.orElseThrow(() -> new EntityNotFoundException(getImageDomain().name() + " 이미지를 찾을 수 없습니다."));
}

@Transactional
public List<ImageResponse> saveImages(List<MultipartFile> images) {
return images.stream()
.map(image -> create(imageStorageService.saveToDirectory(image, getChildPath())))
.toList();
}

// TODO: delete 이미지 바꿔야함
@Transactional
public void deleteImage(MemberIdentifier identifier, Long imageId) {
T image = getById(imageId);
validateMemberId(identifier, image);
delete(imageId);
imageStorageService.delete(getChildPath(), image.getName());
// ToDO : 썸네일도 지워라
}

public String createThumbnail(String fileName) {
return imageStorageService.createThumbnailImage(parentPath, getChildPath(), fileName);
}

public T getById(Long id) {
return getImageRepository().findById(id)
.orElseThrow(() -> new EntityNotFoundException(getImageDomain().name() + " 이미지를 찾을 수 없습니다."));
}

protected ImageResponse create(ImageRequest imageRequest) {
T image = mapToEntityFrom(imageRequest);
return ImageResponse.from(getImageRepository().save(image), getImageUrl(image));
}

public String getImageUrl(T image) {
return UriUtil.assembleFullUrl(parentPath, getChildPath(), image.getName());
}

protected void delete(Long id) {
getById(id);
getImageRepository().deleteById(id);
}

public String createThumbnail(String fileName) {
return imageStorageService.createThumbnailImage(imageApiHeader, getChildPath(), fileName);
}

public String getImageUrl(T image) {
return UriUtil.assembleFullUrl(imageApiHeader, getChildPath(), image.getName());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,6 @@ public class ImageStorageService {
@Value("${image.upload.thumbnail.width}")
private Integer thumbnailWidth;

@Value("${image.upload.thumbnail.extension}")
private String thumbnailExtension;

private final RestClientUtil client;

public byte[] readFromDirectory(Path childPath) {
Expand All @@ -56,7 +53,6 @@ public ImageRequest saveToDirectory(String imageUrl, String childPath) {
throw new FailedToUploadImageException("이미지 URL에 대한 요청을 실패하였습니다.");
}
);

String contentType = Optional.ofNullable(response.getHeaders().getContentType()).orElseThrow(
() -> new FailedToUploadImageException("응답 헤더에 콘텐츠 타입이 없어 이미지를 가져올 수 없습니다.")
).toString();
Expand All @@ -71,23 +67,15 @@ public ImageRequest saveToDirectory(String imageUrl, String childPath) {
return ImageRequest.of(fileName, (long)body.length, extension);
}

// TODO : 썸네일 생성 코드 리팩토링, 파일 유틸로 이동
public String createThumbnailImage(String apiHeader, String childPath, String originFileName) {
String fileName = originFileName;
String originImagePath = Path.of(parentPath, childPath, originFileName).toString();
String thumbnailImagePath = Path.of(parentPath, childPath, thumbnailChildPath).toString();
try {
Files.createDirectories(Paths.get(thumbnailImagePath));
Thumbnails.of(new File(originImagePath))
.size(thumbnailHeight, thumbnailWidth)
.toFile(new File(thumbnailImagePath, fileName));
} catch (Exception e) {
throw new RuntimeException(e);
}
return UriUtil.assembleFullUrl(apiHeader,childPath, thumbnailChildPath, fileName);
FileUtil.createThumbnail(originImagePath, originFileName, thumbnailImagePath, thumbnailWidth, thumbnailHeight);
return UriUtil.assembleFullUrl(apiHeader,childPath, thumbnailChildPath, originFileName);
}

public void delete(String childPath, String fileName) {
FileUtil.deleteFile(Path.of(parentPath, childPath, fileName));
FileUtil.deleteFile(Path.of(parentPath, childPath, thumbnailChildPath, fileName));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,17 @@
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
public class MemberImageService extends ImageService<MemberImage> {

@Value("${image.upload.member.child-path}")
private String childPath;
private final MemberImageRepository memberImageRepository;

public MemberImageService(ImageStorageService imageStorageService, MemberImageRepository memberImageRepository) {
this.imageStorageService = imageStorageService;
this.memberImageRepository = memberImageRepository;
}

@Override
public ImageRepository<MemberImage> getImageRepository() {
return memberImageRepository;
Expand All @@ -48,7 +52,8 @@ protected MemberImage mapToEntityFrom(ImageRequest imageRequest) {

@Override
protected void validateMemberId(MemberIdentifier identifier, MemberImage image) {
if (!identifier.type().equals(MemberType.admin) && !identifier.id().equals(image.getBaseMember().getId())) {
if (!identifier.type().equals(MemberType.admin) &&
!identifier.id().equals(image.getBaseMember().getId())) {
throw new UnauthorizedException("해당 프로필 이미지에 접근할 권한이 없습니다.");
}
}
Expand Down
Loading