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] feat: 이미지 업로드 최대사이즈 제한 설정 #96

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
Expand Up @@ -4,10 +4,10 @@
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.dto.ImageRequest;
import org.ktc2.cokaen.wouldyouin._common.exception.FailedToDeleteImageException;
import org.ktc2.cokaen.wouldyouin._common.exception.FailedToUploadImageException;
import org.springframework.beans.factory.annotation.Value;
Expand Down Expand Up @@ -36,27 +36,28 @@ public String save(MultipartFile image, String subPath) {
}

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

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

if (Optional.ofNullable(response.getBody()).isPresent() && response.getStatusCode().is2xxSuccessful()) {
fileName = generateUuidName() + "." + getExtension(imageUrl);
size = (long) response.getBody().length;
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;
String url = subPath + "/" + fileName;
return ImageRequest.of(url, size, getExtension(imageUrl));
}

public void delete(String imagePath) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,7 @@ public void setBaseMember(MemberImage image, BaseMember member) {
image.setBaseMember(member);
}

// TODO: imageUrl을 MemberImage로 변환하는 로직 추가 필요
// Todo: extension과 size 불러오기
public MemberImage convert(String imageUrl) {
var request = ImageRequest.of(imageStorage.save(imageUrl, subPath), 123123L, "jpg");
return memberImageRepository.save(toEntity(request));
return memberImageRepository.save(toEntity(imageStorage.save(imageUrl, subPath)));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.ktc2.cokaen.wouldyouin._common.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ObjectMapperConfig {

@Bean
ObjectMapper objectMapper() {
return new ObjectMapper();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public ResponseEntity<ApiResponseBody<AdvertisementResponse>> getAdvertisementBy
public ResponseEntity<ApiResponseBody<AdvertisementResponse>> createAdvertisement(
@Valid @RequestPart AdvertisementRequest advertisementRequest,
@RequestPart(required = false) MultipartFile image,
@Authorize(MemberType.admin) MemberIdentifier memberIdentifier) {
@Authorize(MemberType.admin) MemberIdentifier admin) {
return ApiResponse.created(advertisementService.create(advertisementRequest, image));
}

Expand All @@ -54,14 +54,14 @@ public ResponseEntity<ApiResponseBody<AdvertisementResponse>> updateAdvertisemen
@PathVariable Long adId,
@Valid @RequestPart AdvertisementRequest advertisementRequest,
@RequestPart(required = false) MultipartFile image,
@Authorize(MemberType.admin) MemberIdentifier memberIdentifier) {
@Authorize(MemberType.admin) MemberIdentifier admin) {
return ApiResponse.ok(advertisementService.update(adId, advertisementRequest, image));
}

@DeleteMapping("/{adId}")
public ResponseEntity<ApiResponseBody<Void>> deleteAdvertisement(
@PathVariable Long adId,
@Authorize(MemberType.admin) MemberIdentifier memberIdentifier) {
@Authorize(MemberType.admin) MemberIdentifier admin) {
advertisementService.delete(adId);
return ApiResponse.noContent();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
package org.ktc2.cokaen.wouldyouin.like.api;

import java.util.List;
import lombok.RequiredArgsConstructor;
import org.ktc2.cokaen.wouldyouin._common.api.ApiResponse;
import org.ktc2.cokaen.wouldyouin._common.api.ApiResponseBody;
import org.ktc2.cokaen.wouldyouin._common.config.ParamDefaults;
import org.ktc2.cokaen.wouldyouin.auth.Authorize;
import org.ktc2.cokaen.wouldyouin.auth.MemberIdentifier;
import org.ktc2.cokaen.wouldyouin.like.application.LikeResponse;
import org.ktc2.cokaen.wouldyouin.like.application.LikeServiceFactory;
import org.ktc2.cokaen.wouldyouin.like.application.dto.LikeResponse;
import org.ktc2.cokaen.wouldyouin.like.application.dto.LikeToggleResponse;
import org.ktc2.cokaen.wouldyouin.member.persist.MemberType;
import org.springframework.http.HttpStatus;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Slice;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
Expand All @@ -26,22 +27,24 @@ public class LikeController {

private final LikeServiceFactory likeServiceFactory;

// Todo: 와일드카드 수정
// Todo: api 요청시 create, delete를 분리하지말고 토글방식으로 하면 어떨지

@GetMapping
public ResponseEntity<ApiResponseBody<List<LikeResponse>>> getLikes(@Authorize(MemberType.normal) MemberIdentifier identifier, @RequestParam("type") MemberType memberType) {
return ApiResponse.ok(likeServiceFactory.getLikeServiceFrom(memberType).getLikes(identifier.id()));
public ResponseEntity<ApiResponseBody<Slice<LikeResponse>>> getLikes(
@Authorize(MemberType.normal) MemberIdentifier identifier,
@RequestParam("type") MemberType memberType,
@RequestParam(defaultValue = ParamDefaults.PAGE) Integer page,
@RequestParam(defaultValue = ParamDefaults.PAGE_SIZE) Integer size,
@RequestParam(defaultValue = ParamDefaults.LAST_ID) Long lastId
) {
return ApiResponse.ok(
likeServiceFactory.getLikeServiceFrom(memberType)
.getLikes(identifier.id(), PageRequest.of(page, size), lastId));
}

@PostMapping("/{targetMemberId}")
public ResponseEntity<ApiResponseBody<LikeResponse>> createLike(@Authorize(MemberType.normal) MemberIdentifier identifier, @PathVariable("targetMemberId") Long targetId) {
return ApiResponse.created(likeServiceFactory.getLikeServiceFrom(targetId).create(identifier.id(), targetId));
}

@DeleteMapping("/{targetMemberId}")
public ResponseEntity<ApiResponseBody<Void>> deleteLike(@Authorize(MemberType.normal) MemberIdentifier identifier, @PathVariable("targetMemberId") Long targetId) {
likeServiceFactory.getLikeServiceFrom(targetId).delete(identifier.id(), targetId);
return ApiResponse.noContent();
public ResponseEntity<ApiResponseBody<LikeToggleResponse>> createOrDeleteLike(
@Authorize(MemberType.normal) MemberIdentifier identifier,
@PathVariable("targetMemberId") Long targetId) {
return ApiResponse.created(
likeServiceFactory.getLikeServiceFrom(targetId).toggleLike(identifier.id(), targetId));
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
package org.ktc2.cokaen.wouldyouin.like.application;

import java.util.List;
import lombok.RequiredArgsConstructor;
import org.ktc2.cokaen.wouldyouin.like.application.dto.LikeResponse;
import org.ktc2.cokaen.wouldyouin.like.application.dto.LikeToggleResponse;
import org.ktc2.cokaen.wouldyouin.like.persist.Like;
import org.ktc2.cokaen.wouldyouin.like.persist.LikeRepository;
import org.ktc2.cokaen.wouldyouin.member.application.LikeableMemberGetterFactory;
import org.ktc2.cokaen.wouldyouin.member.application.MemberService;
import org.ktc2.cokaen.wouldyouin.member.persist.LikeableMember;
import org.ktc2.cokaen.wouldyouin.member.persist.Member;
import org.ktc2.cokaen.wouldyouin.member.persist.MemberType;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

Expand All @@ -20,44 +23,38 @@ public abstract class LikeService<LikeType extends Like<? extends LikeableMember
private final MemberService memberService;

protected abstract LikeRepository<LikeType> getLikeRepository();

protected abstract LikeType toEntity(Member member, LikeableMember targetLikableMember);

public abstract MemberType getTargetLikeableMemberType();

@Transactional(readOnly = true)
public List<LikeResponse> getLikes(Long memberId) {
return getLikeRepository().findAllByMember(memberService.getByIdOrThrow(memberId))
.stream()
.map(Like::getLikeableMember)
.map(LikeResponse::from)
.toList();
public Slice<LikeResponse> getLikes(Long memberId, Pageable pageable, Long lastId) {
return getLikeRepository().findAllByMember(
memberService.getByIdOrThrow(memberId), lastId, pageable)
.map(like -> LikeResponse.from(like.getLikeableMember()));
}

@Transactional
public LikeResponse create(Long memberId, Long targetMemberId) {
public LikeToggleResponse toggleLike(Long memberId, Long targetMemberId) {
Member member = memberService.getByIdOrThrow(memberId);
LikeableMember targetLikeableMember = getLikeableMemberByIdOrThrow(targetMemberId);
getLikeRepository().findByMemberAndLikeableMember(member, targetLikeableMember)
.ifPresent(x -> { throw new RuntimeException("이미 좋아요한 사용자입니다."); });

targetLikeableMember.increaseLikes();
return LikeResponse.from(getLikeRepository()
.save(toEntity(member, targetLikeableMember))
.getLikeableMember());
}

@Transactional
public void delete(Long memberId, Long targetMemberId) {
Member member = memberService.getByIdOrThrow(memberId);
LikeableMember targetLikeableMember = getLikeableMemberByIdOrThrow(targetMemberId);
LikeType like = getLikeRepository().findByMemberAndLikeableMember(member, targetLikeableMember)
.orElseThrow(() -> new RuntimeException("해당 사용자를 좋아요하지 않았습니다."));

targetLikeableMember.decreaseLikes();
getLikeRepository().delete(like);
return getLikeRepository().findByMemberAndLikeableMember(member, targetLikeableMember)
.map(like -> {
targetLikeableMember.decreaseLikes();
getLikeRepository().delete(like);
return LikeToggleResponse.from(false);
})
.orElseGet(() -> {
targetLikeableMember.increaseLikes();
getLikeRepository().save(toEntity(member, targetLikeableMember));
return LikeToggleResponse.from(true);
});
}

@Transactional(readOnly = true)
protected LikeableMember getLikeableMemberByIdOrThrow(Long likeableMemberId) {
return likeableMemberGetterFactory.get(getTargetLikeableMemberType()).getByIdOrThrow(likeableMemberId);
return likeableMemberGetterFactory.get(getTargetLikeableMemberType())
.getByIdOrThrow(likeableMemberId);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.ktc2.cokaen.wouldyouin.like.application;
package org.ktc2.cokaen.wouldyouin.like.application.dto;

import java.util.List;
import lombok.AccessLevel;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.ktc2.cokaen.wouldyouin.like.application.dto;

import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.ktc2.cokaen.wouldyouin.member.persist.LikeableMember;

@Getter
@RequiredArgsConstructor(access = AccessLevel.PROTECTED)
@Builder
public class LikeToggleResponse {
private final boolean isLiked;

public static LikeToggleResponse from(boolean state) {
return LikeToggleResponse.builder()
.isLiked(state)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
package org.ktc2.cokaen.wouldyouin.like.persist;


import java.util.List;
import java.util.Optional;
import org.ktc2.cokaen.wouldyouin.member.persist.LikeableMember;
import org.ktc2.cokaen.wouldyouin.member.persist.Member;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.NoRepositoryBean;

@NoRepositoryBean
public interface LikeRepository <LikeType extends Like<? extends LikeableMember>> extends JpaRepository<LikeType, Long> {

Optional<LikeType> findByMemberAndLikeableMember(Member member, LikeableMember likeableMember);
List<LikeType> findAllByMember(Member member);

@Query("SELECT l FROM #{#entityName} l " +
"JOIN FETCH l.member m " +
"JOIN FETCH l.likeableMember lm " +
"WHERE l.member.id = :member " +
"AND l.id < :lastId " +
"ORDER BY l.id DESC")
Slice<LikeType> findAllByMember(Member member, Long lastId, Pageable pageable);
}
5 changes: 5 additions & 0 deletions src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
spring:
application.name: WouldYouIn

servlet:
multipart:
max-file-size: 10MB
max-request-size: 10MB

# 임시 프로젝트 url
wouldyouin-domain-name: http://52.78.71.136/
profiles:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import java.util.List;
import java.util.Random;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
Expand All @@ -31,7 +30,6 @@
import org.ktc2.cokaen.wouldyouin._global.mockMember.WithMockMember;
import org.ktc2.cokaen.wouldyouin.auth.application.JwtAuthFilter;
import org.ktc2.cokaen.wouldyouin.curation.api.CurationController;
import org.ktc2.cokaen.wouldyouin.curation.api.dto.CurationCardRequest;
import org.ktc2.cokaen.wouldyouin.curation.api.dto.CurationCreateRequest;
import org.ktc2.cokaen.wouldyouin.curation.api.dto.CurationEditRequest;
import org.ktc2.cokaen.wouldyouin.curation.application.CurationService;
Expand All @@ -48,7 +46,8 @@
@WebMvcTest(CurationController.class)
class CurationControllerUnitTest {

private static ObjectMapper objectMapper;
@Autowired
private ObjectMapper objectMapper;

@MockBean
private CurationService curationService;
Expand All @@ -63,15 +62,9 @@ class CurationControllerUnitTest {
private WebApplicationContext context;
private static final long randomId = abs(new Random().nextLong());


@BeforeAll
public static void init() {
objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
}

@BeforeEach
public void setup() throws Exception {
objectMapper.registerModule(new JavaTimeModule());
mockMvc = MockMvcBuilders
.webAppContextSetup(context)
.apply(springSecurity())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,16 +91,18 @@ void getEventsByFilterOrderByDistanceAsc() throws Exception {
// given
LocationFilter locationFilter = new LocationFilter(0.0, 0.0, 10.0, 10.0);
Location currentLocation = new Location(3.0, 2.0);
String title = "testTitle";
Category category = Category.공예;
Area area = Area.광주;
int pageNumber = 1; // 교수님
int pageSize = 10; //
int pageNumber = 1;
int pageSize = 10;
Long lastId = 1L;
Pageable pageable = PageRequest.of(pageNumber, pageSize);

given(eventService.getAllByFilterOrderByDistanceAsc(
locationFilter,
currentLocation,
title,
category,
area,
pageable,
Expand All @@ -115,8 +117,9 @@ void getEventsByFilterOrderByDistanceAsc() throws Exception {
.param("endLongitude", locationFilter.getEndLongitude().toString())
.param("latitude", currentLocation.getLatitude().toString())
.param("longitude", currentLocation.getLongitude().toString())
.param("category", category.toString()) // enum을 문자열로 변환하여 설정
.param("area", area.toString()) // enum을 문자열로 변환하여 설정
.param("title", title)
.param("category", category.toString())
.param("area", area.toString())
.param("page", String.valueOf(pageNumber))
.param("size", String.valueOf(pageSize))
.param("lastId", String.valueOf(lastId))
Expand All @@ -127,6 +130,7 @@ void getEventsByFilterOrderByDistanceAsc() throws Exception {
then(eventService).should(times(1)).getAllByFilterOrderByDistanceAsc(
any(LocationFilter.class),
any(Location.class),
eq(title),
eq(category),
eq(area),
eq(pageable),
Expand Down
Loading
Loading