Skip to content

Commit

Permalink
[Weekly/10/Reservation/unitTest] Reservation unit 테스트 추가
Browse files Browse the repository at this point in the history
* feat: reservation unit test

* feat: 행사명으로 검색 로직 추가

* feat: url을 이미지로 저장하는 로직
  • Loading branch information
Daolove0323 authored and lja3723 committed Nov 8, 2024
1 parent 957552f commit 3e8d237
Show file tree
Hide file tree
Showing 20 changed files with 264 additions and 117 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,13 @@
import lombok.RequiredArgsConstructor;
import org.ktc2.cokaen.wouldyouin.Image.api.dto.ImageResponse;
import org.ktc2.cokaen.wouldyouin.Image.application.ImageServiceFactory;
import org.ktc2.cokaen.wouldyouin.Image.application.ImageStorage;
import org.ktc2.cokaen.wouldyouin._common.api.ApiResponse;
import org.ktc2.cokaen.wouldyouin._common.api.ApiResponseBody;
import org.ktc2.cokaen.wouldyouin._common.exception.FailToReadImageException;
import org.ktc2.cokaen.wouldyouin.auth.Authorize;
import org.ktc2.cokaen.wouldyouin.auth.MemberIdentifier;
import org.ktc2.cokaen.wouldyouin.member.persist.MemberType;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
Expand All @@ -29,8 +33,10 @@ public class ImageController {

private final ImageServiceFactory imageServiceFactory;

// Todo: authorize
@PostMapping("/images")
public ResponseEntity<ApiResponseBody<List<ImageResponse>>> uploadImages(@RequestParam List<MultipartFile> images,
public ResponseEntity<ApiResponseBody<List<ImageResponse>>> uploadImages(
@RequestParam List<MultipartFile> images,
@RequestParam(value = "type") ImageDomain imageDomain) {
return ApiResponse.ok(imageServiceFactory.getImageServiceByImageType(imageDomain).saveAndCreateImages(images));
}
Expand All @@ -44,9 +50,11 @@ public ResponseEntity<byte[]> getImage(@PathVariable String path) {
}
}

// Authorize 로직
// Todo: authorize
@DeleteMapping("/images/{id}")
public ResponseEntity<ApiResponseBody<Void>> deleteImage(@PathVariable Long id, @RequestParam ImageDomain imageDomain) {
public ResponseEntity<ApiResponseBody<Void>> deleteImage(
@PathVariable Long id,
@RequestParam(value = "type") ImageDomain imageDomain) {
imageServiceFactory.getImageServiceByImageType(imageDomain).deleteAndDelete(id);
return ApiResponse.noContent();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ public class ImageResponse {
private LocalDateTime createdDate;
private Long size;

public static ImageResponse from(Image image) {
public static ImageResponse from(Image image, String domainName) {
System.out.println("도메인" + domainName);
return ImageResponse.builder()
.id(image.getId())
.url(image.getUrl())
.url(domainName + image.getUrl())
.size(image.getSize())
.createdDate(image.getCreatedDate())
.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,10 @@
@RequiredArgsConstructor
public class AdvertisementImageService extends ImageService<AdvertisementImage> {

@Value("${image.upload.ad.sub-path}")
private String subPath;

private final ImageStorage imageStorage;

private final AdvertisementImageRepository adImageRepository;
@Value("${image.upload.ad.sub-path}")
private String subPath;

@Override
protected ImageRepository<AdvertisementImage> getImageRepository() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
package org.ktc2.cokaen.wouldyouin.Image.application;

import java.util.List;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import org.ktc2.cokaen.wouldyouin.Image.api.ImageDomain;
import org.ktc2.cokaen.wouldyouin.Image.api.dto.ImageRequest;
import org.ktc2.cokaen.wouldyouin.Image.persist.CurationImage;
import org.ktc2.cokaen.wouldyouin.Image.persist.EventImage;
import org.ktc2.cokaen.wouldyouin.Image.persist.EventImageRepository;
import org.ktc2.cokaen.wouldyouin.Image.persist.ImageRepository;
import org.ktc2.cokaen.wouldyouin._common.exception.EntityNotFoundException;
import org.ktc2.cokaen.wouldyouin._common.exception.EntityParamIsNullException;
import org.ktc2.cokaen.wouldyouin.advertisement.persist.Advertisement;
import org.ktc2.cokaen.wouldyouin.curation.persist.CurationCard;
import org.ktc2.cokaen.wouldyouin.event.persist.Event;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.ktc2.cokaen.wouldyouin.Image.persist.ImageRepository;
import org.ktc2.cokaen.wouldyouin._common.exception.EntityNotFoundException;
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;
import org.springframework.web.multipart.MultipartFile;
Expand All @@ -20,6 +21,9 @@ public abstract class ImageService<T extends Image> {
@Autowired
protected ImageStorage imageStorage;

@Value("${spring.wouldyouin-domain-name}")
private String domainName;

protected abstract ImageRepository<T> getImageRepository();

protected abstract ImageDomain getImageDomain();
Expand All @@ -34,7 +38,7 @@ public T getById(Long id) {
}

protected ImageResponse create(ImageRequest imageRequest) {
return ImageResponse.from(getImageRepository().save(toEntity(imageRequest)));
return ImageResponse.from(getImageRepository().save(toEntity(imageRequest)), domainName);
}

protected void delete(Long id) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,16 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import org.ktc2.cokaen.wouldyouin.Image.api.ImageDomain;
import org.ktc2.cokaen.wouldyouin._common.exception.FailedToDeleteImageException;
import org.ktc2.cokaen.wouldyouin._common.exception.FailedToUploadImageException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestClient;
import org.springframework.web.multipart.MultipartFile;

@Component
Expand All @@ -18,13 +23,38 @@ public class ImageStorage {
private String commonPath;

public String save(MultipartFile image, String subPath) {
String fileName = generateUuidName() + "." + getExtension(image);
Path path = Paths.get(commonPath, subPath, fileName);
String fileName = "";
try {
fileName = generateUuidName() + "." + getExtension(image);
Path path = Paths.get(commonPath, subPath, fileName);
Files.createDirectories(path.getParent());
Files.write(path, image.getBytes());
} catch (IOException ex) {
throw new RuntimeException("failed to upload image.");
throw new FailedToUploadImageException();
}
return subPath + "/" + fileName;
}

// Todo: payment랑 이부분 restclient 유틸로 빼기
public String save(String imageUrl, String subPath) {
RestClient client = RestClient.builder().build();
String fileName = "";
try {
ResponseEntity<byte[]> response = client.get()
.uri(imageUrl)
.retrieve()
.toEntity(byte[].class);

if (Optional.ofNullable(response).isPresent() && response.getStatusCode().is2xxSuccessful()) {
byte[] imageBytes = response.getBody();

fileName = generateUuidName() + "." + getExtension(imageUrl);
Path path = Paths.get(commonPath, subPath, fileName);
Files.createDirectories(path.getParent());
Files.write(path, response.getBody());
}
} catch (IOException ex) {
throw new FailedToUploadImageException();
}
return subPath + "/" + fileName;
}
Expand All @@ -33,14 +63,19 @@ public void delete(String imagePath) {
try {
Files.deleteIfExists(Paths.get(imagePath));
} catch (IOException ex) {
throw new RuntimeException("failed to delete image.");
throw new FailedToDeleteImageException();
}
}

protected static String getExtension(MultipartFile image) {
return Objects.requireNonNull(image.getContentType()).split("/")[1];
}

protected static String getExtension(String imageUrl) {
String[] splitted = imageUrl.split("\\.");
return splitted[splitted.length - 1];
}

private static String generateUuidName() {
return UUID.randomUUID().toString().replace("-", "");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,35 +61,9 @@ public void setBaseMember(MemberImage image, BaseMember member) {
}

// TODO: imageUrl을 MemberImage로 변환하는 로직 추가 필요
// Todo: extension과 size 불러오기
public MemberImage convert(String imageUrl) {

RestClient client = RestClient.builder().build();

try {
ResponseEntity<byte[]> response = client.get()
.uri(imageUrl)
.retrieve()
.toEntity(byte[].class);

// 요청 성공 시 이미지 저장
if (response != null && response.getStatusCode() == HttpStatus.OK) {
byte[] imageBytes = response.getBody();

if (imageBytes != null) {
// 파일 이름과 경로 설정
Path path = Paths.get("src/main/resources/static", subPath, "testFilename");
Files.createDirectories(path.getParent());
Files.write(path, response.getBody());
}
}
} catch (Exception ex) {
throw new RuntimeException("failed to get image.");
}

return MemberImage.builder()
.url("http://example.com/images/MockMemberImageUrl")
.size(10L)
.extension(".jpeg")
.build();
var request = ImageRequest.of(imageStorage.save(imageUrl, subPath), 123123L, "jpg");
return memberImageRepository.save(toEntity(request));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@ public class ParamDefaults {
public static final String PAGE = "0";
public static final String PAGE_SIZE = "10";
public static final String LAST_ID = Long.MAX_VALUE + "";
public static final String START_LATITUDE = "-90.0";
public static final String END_LATITUDE = "90.0";
public static final String START_LONGITUDE = "-180.0";
public static final String END_LONGITUDE = "180.0";
public static final String TITLE = "";
public static final String AREA = "전체";
public static final String CATEGORY = "전체";
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,13 @@ public enum ErrorCode {

UNAUTHORIZED(HttpStatus.UNAUTHORIZED.value(), "-20400", "Unauthorized, %s."),

ENTITY_PARAM_IS_NULL(HttpStatus.BAD_REQUEST.value(), "-20400", "%s is null");
ENTITY_PARAM_IS_NULL(HttpStatus.BAD_REQUEST.value(), "-20400", "%s is null"),

FAIL_TO_UPLOAD_IMAGE(HttpStatus.CONFLICT.value(), "-20400", "Fail to upload image"),

FAIL_TO_DELETE_IMAGE(HttpStatus.CONFLICT.value(), "-20400", "Fail to delete image"),

CURRENT_LOCATION_EMPTY(HttpStatus.BAD_REQUEST.value(), "-20400", "현재 위치 정보를 찾을 수 없습니다.");

private final Integer status;
private final String code;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.ktc2.cokaen.wouldyouin._common.exception;

import org.ktc2.cokaen.wouldyouin._common.error.ErrorCode;

public class CurrentLocationEmptyException extends BusinessException {

public CurrentLocationEmptyException() {
super(ErrorCode.CURRENT_LOCATION_EMPTY, "현재 위치 정보를 찾을 수 없습니다.");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.ktc2.cokaen.wouldyouin._common.exception;

import org.ktc2.cokaen.wouldyouin._common.error.ErrorCode;

public class FailedToDeleteImageException extends BusinessException {

public FailedToDeleteImageException() {
super(ErrorCode.FAIL_TO_DELETE_IMAGE, ErrorCode.FAIL_TO_DELETE_IMAGE.getMessage());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.ktc2.cokaen.wouldyouin._common.exception;

import org.ktc2.cokaen.wouldyouin._common.error.ErrorCode;

public class FailedToUploadImageException extends BusinessException {

public FailedToUploadImageException() {
super(ErrorCode.FAIL_TO_UPLOAD_IMAGE, ErrorCode.FAIL_TO_UPLOAD_IMAGE.getMessage());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,15 @@ public class EventController {
public ResponseEntity<ApiResponseBody<EventSliceResponse>> getEventsByFilterOrderByDistanceAsc(
@ModelAttribute LocationFilter locationFilter,
@ModelAttribute Location currentLocation,
@RequestParam(defaultValue = ParamDefaults.TITLE) String title,
@RequestParam(defaultValue = ParamDefaults.CATEGORY) Category category,
@RequestParam(defaultValue = ParamDefaults.AREA) Area area,
@RequestParam(defaultValue = ParamDefaults.PAGE) Integer page,
@RequestParam(defaultValue = ParamDefaults.PAGE_SIZE) Integer size,
@RequestParam(defaultValue = ParamDefaults.LAST_ID) Long lastId
) {
return ApiResponse.ok(eventService.getAllByFilterOrderByDistanceAsc(
locationFilter, currentLocation, category, area, PageRequest.of(page, size), lastId));
locationFilter, currentLocation, title, category, area, PageRequest.of(page, size), lastId));
}

@GetMapping("/hosts/{hostId}")
Expand All @@ -62,8 +63,6 @@ public ResponseEntity<ApiResponseBody<EventSliceResponse>> getEventsByHostId(
hostId, PageRequest.of(page, size), lastId));
}

// Todo: 이름으로 검색

@GetMapping("/{eventId}")
public ResponseEntity<ApiResponseBody<EventResponse>> getEventByEventId(
@PathVariable("eventId") Long eventId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.ktc2.cokaen.wouldyouin.Image.application.EventImageService;
import org.ktc2.cokaen.wouldyouin._common.exception.CurrentLocationEmptyException;
import org.ktc2.cokaen.wouldyouin._common.exception.EntityNotFoundException;
import org.ktc2.cokaen.wouldyouin._common.exception.UnauthorizedException;
import org.ktc2.cokaen.wouldyouin._common.vo.Area;
Expand Down Expand Up @@ -30,12 +31,19 @@ public class EventService {
private final EventImageService eventImageService;

@Transactional(readOnly = true)
public EventSliceResponse getAllByFilterOrderByDistanceAsc(LocationFilter location, Location currentLocation,
public EventSliceResponse getAllByFilterOrderByDistanceAsc(LocationFilter location, Location currentLocation, String title,
Category category, Area area, Pageable pageable, Long lastId) {
validateCurrentLocation(currentLocation);
return getEventSliceResponse(
eventRepository.findAllByFilterOrderByDistance(location.getStartLatitude(), location.getStartLongitude(),
location.getEndLatitude(), location.getEndLongitude(), currentLocation.getLatitude(), currentLocation.getLongitude(),
category, area, pageable), lastId);
title, category, area, pageable), lastId);
}

private void validateCurrentLocation(Location currentLocation) {
if (currentLocation.getLatitude() == null || currentLocation.getLongitude() == null) {
throw new CurrentLocationEmptyException();
}
}

@Transactional(readOnly = true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,12 @@ public interface EventRepository extends JpaRepository<Event, Long> {
@Query("SELECT E FROM Event E JOIN FETCH E.host "
+ "WHERE (E.location.latitude between :startLatitude AND :endLatitude) "
+ "AND (E.location.longitude between :startLongitude AND :endLongitude) "
+ "AND (E.title LIKE %:title%) "
+ "AND (:category = '전체' OR E.category = :category) "
+ "AND (:area = '전체' OR E.area = :area) "
+ "ORDER BY (:currentLatitude - E.location.latitude) * (:currentLatitude - E.location.latitude) + "
+ "(:currentLongitude - E.location.longitude) * (:currentLongitude - E.location.longitude) ASC")
Slice<Event> findAllByFilterOrderByDistance(
Double startLatitude, Double startLongitude, Double endLatitude, Double endLongitude,
Double currentLatitude, Double currentLongitude, Category category, Area area, Pageable pageable);
Double currentLatitude, Double currentLongitude, String title, Category category, Area area, Pageable pageable);
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ public class ReservationController {

@GetMapping
public ResponseEntity<ApiResponseBody<ReservationSliceResponse>> getReservationsByMemberId(
@Authorize({MemberType.normal, MemberType.curator}) Long memberId,
@Authorize({MemberType.normal, MemberType.curator}) MemberIdentifier member,
@RequestParam(defaultValue = ParamDefaults.PAGE) Integer page,
@RequestParam(defaultValue = ParamDefaults.PAGE_SIZE) Integer size,
@RequestParam(defaultValue = ParamDefaults.LAST_ID) Long lastId) {
return ApiResponse.ok(reservationService.getAllByMemberId(memberId, PageRequest.of(page, size), lastId));
return ApiResponse.ok(reservationService.getAllByMemberId(member.id(), PageRequest.of(page, size), lastId));
}

@GetMapping("/events/{eventId}")
Expand Down
Loading

0 comments on commit 3e8d237

Please sign in to comment.