Skip to content

Commit

Permalink
[WALWAL-159] 피드 작업 (#104)
Browse files Browse the repository at this point in the history
* [WALWAL-159] 피드 작업

* fix: 약간 수정

* fix: resolve review

* fix: 테스트 케이스 앞에 public 삭제
  • Loading branch information
kwanok authored Aug 12, 2024
1 parent 44a128d commit bf916b3
Show file tree
Hide file tree
Showing 9 changed files with 291 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.depromeet.stonebed.domain.feed.api;

import com.depromeet.stonebed.domain.feed.application.FeedService;
import com.depromeet.stonebed.domain.feed.dto.request.FeedGetRequest;
import com.depromeet.stonebed.domain.feed.dto.response.FeedGetResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

@Tag(name = "7. [피드]", description = "피드 관련 API입니다.")
@RestController
@RequestMapping("/feed")
@RequiredArgsConstructor
public class FeedController {
private final FeedService feedService;

@GetMapping
public FeedGetResponse getFeed(
@RequestParam(required = false) String cursor, @RequestParam int limit) {
FeedGetRequest request = new FeedGetRequest(cursor, limit);
return feedService.getFeed(request);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.depromeet.stonebed.domain.feed.application;

import com.depromeet.stonebed.domain.feed.dao.FeedRepository;
import com.depromeet.stonebed.domain.feed.dto.request.FeedGetRequest;
import com.depromeet.stonebed.domain.feed.dto.response.FeedContentGetResponse;
import com.depromeet.stonebed.domain.feed.dto.response.FeedGetResponse;
import com.depromeet.stonebed.domain.member.domain.Member;
import com.depromeet.stonebed.domain.missionRecord.domain.MissionRecord;
import com.depromeet.stonebed.global.util.MemberUtil;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional
@RequiredArgsConstructor
public class FeedService {
private final FeedRepository feedRepository;
private final MemberUtil memberUtil;

@Transactional(readOnly = true)
public FeedGetResponse getFeed(FeedGetRequest request) {
Member currentMember = memberUtil.getCurrentMember();

List<MissionRecord> missionRecords =
getMissionRecords(request.cursor(), currentMember.getId(), request.limit());

List<FeedContentGetResponse> feedContentList =
missionRecords.stream().map(FeedContentGetResponse::from).toList();

String nextCursor = getNextCursor(missionRecords, request.limit());

return FeedGetResponse.from(feedContentList, nextCursor);
}

private String getNextCursor(List<MissionRecord> records, int limit) {
if (records.size() < limit) {
return null;
}

MissionRecord lastRecord = records.get(records.size() - 1);
Long lastId = lastRecord.getId();
return String.valueOf(lastId);
}

private List<MissionRecord> getMissionRecords(String cursor, Long memberId, int limit) {
if (cursor == null || cursor.isEmpty()) {
return feedRepository.getFeedContents(memberId, limit);
}

return feedRepository.getFeedContentsUsingCursor(Long.parseLong(cursor), memberId, limit);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.depromeet.stonebed.domain.feed.dao;

import com.depromeet.stonebed.domain.missionRecord.domain.MissionRecord;
import org.springframework.data.jpa.repository.JpaRepository;

public interface FeedRepository extends JpaRepository<MissionRecord, Long>, FeedRepositoryCustom {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.depromeet.stonebed.domain.feed.dao;

import com.depromeet.stonebed.domain.missionRecord.domain.MissionRecord;
import java.util.List;

public interface FeedRepositoryCustom {
List<MissionRecord> getFeedContentsUsingCursor(Long missionRecordId, Long memberId, int limit);

List<MissionRecord> getFeedContents(Long memberId, int limit);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.depromeet.stonebed.domain.feed.dao;

import static com.depromeet.stonebed.domain.missionRecord.domain.QMissionRecord.missionRecord;

import com.depromeet.stonebed.domain.missionRecord.domain.MissionRecord;
import com.querydsl.jpa.impl.JPAQueryFactory;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;

@Repository
@RequiredArgsConstructor
public class FeedRepositoryImpl implements FeedRepositoryCustom {
private final JPAQueryFactory queryFactory;

@Override
public List<MissionRecord> getFeedContentsUsingCursor(
Long missionRecordId, Long memberId, int limit) {
return queryFactory
.select(missionRecord)
.from(missionRecord)
.where(
missionRecord
.id
.lt(missionRecordId)
.and(missionRecord.member.id.eq(memberId)))
.orderBy(missionRecord.id.desc())
.limit(limit)
.fetch();
}

@Override
public List<MissionRecord> getFeedContents(Long memberId, int limit) {
return queryFactory
.select(missionRecord)
.from(missionRecord)
.where(missionRecord.member.id.eq(memberId))
.orderBy(missionRecord.id.desc())
.limit(limit)
.fetch();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.depromeet.stonebed.domain.feed.dto.request;

import io.swagger.v3.oas.annotations.media.Schema;

public record FeedGetRequest(
@Schema(description = "커서 위치", example = "1") String cursor,
@Schema(description = "피드 당 항목 수", example = "5") int limit) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.depromeet.stonebed.domain.feed.dto.response;

import com.depromeet.stonebed.domain.missionRecord.domain.MissionRecord;
import io.swagger.v3.oas.annotations.media.Schema;
import java.time.LocalDate;

public record FeedContentGetResponse(
@Schema(description = "미션 ID", example = "1") Long missionId,
@Schema(description = "작성자 ID", example = "1") Long authorId,
@Schema(description = "미션 기록 이미지 URL", example = "example.jpeg")
String missionRecordImageUrl,
@Schema(description = "미션 기록 생성일") LocalDate createdDate) {
public static FeedContentGetResponse from(MissionRecord missionRecord) {
return new FeedContentGetResponse(
missionRecord.getId(),
missionRecord.getMember().getId(),
missionRecord.getImageUrl(),
missionRecord.getCreatedAt().toLocalDate());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.depromeet.stonebed.domain.feed.dto.response;

import io.swagger.v3.oas.annotations.media.Schema;
import java.util.List;

public record FeedGetResponse(
List<FeedContentGetResponse> list,
@Schema(description = "커서 위치", example = "1") String nextCursor) {
public static FeedGetResponse from(List<FeedContentGetResponse> list, String nextCursor) {
return new FeedGetResponse(list, nextCursor);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package com.depromeet.stonebed.domain.feed.application;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.*;

import com.depromeet.stonebed.domain.feed.dao.FeedRepository;
import com.depromeet.stonebed.domain.feed.dto.request.FeedGetRequest;
import com.depromeet.stonebed.domain.feed.dto.response.FeedGetResponse;
import com.depromeet.stonebed.domain.member.domain.Member;
import com.depromeet.stonebed.domain.missionRecord.domain.MissionRecord;
import com.depromeet.stonebed.global.util.MemberUtil;
import com.navercorp.fixturemonkey.FixtureMonkey;
import com.navercorp.fixturemonkey.api.introspector.FieldReflectionArbitraryIntrospector;
import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

public class FeedServiceTest {
@Mock private FeedRepository feedRepository;

@Mock private MemberUtil memberUtil;

@InjectMocks private FeedService feedService;

private FixtureMonkey fixtureMonkey;

@BeforeEach
void setUp() {
fixtureMonkey =
FixtureMonkey.builder()
.objectIntrospector(FieldReflectionArbitraryIntrospector.INSTANCE)
.defaultNotNull(true)
.build();
MockitoAnnotations.openMocks(this);
}

@Test
void 피드_조회_성공() {
// Given
Member member = fixtureMonkey.giveMeOne(Member.class);
List<MissionRecord> missionRecords = new ArrayList<>();

for (int i = 0; i < 5; i++) {
missionRecords.add(fixtureMonkey.giveMeOne(MissionRecord.class));
}

when(memberUtil.getCurrentMember()).thenReturn(member);
when(feedRepository.getFeedContents(member.getId(), 5)).thenReturn(missionRecords);

// When
FeedGetResponse feedGetResponse = feedService.getFeed(new FeedGetRequest(null, 5));

// Then
assertThat(feedGetResponse.list().size()).isEqualTo(5);
String nextCursor = missionRecords.get(missionRecords.size() - 1).getId().toString();
assertThat(feedGetResponse.nextCursor()).isEqualTo(nextCursor);
verify(memberUtil).getCurrentMember();
verify(feedRepository).getFeedContents(member.getId(), 5);
}

@Test
void 피드_조회_커서_사용_성공() {
// Given
Member member = fixtureMonkey.giveMeOne(Member.class);
List<MissionRecord> missionRecords = new ArrayList<>();
String cursor = "5";

for (int i = 0; i < 5; i++) {
missionRecords.add(fixtureMonkey.giveMeOne(MissionRecord.class));
}

when(memberUtil.getCurrentMember()).thenReturn(member);
when(feedRepository.getFeedContentsUsingCursor(Long.parseLong(cursor), member.getId(), 5))
.thenReturn(missionRecords);

// When
FeedGetResponse feedGetResponse = feedService.getFeed(new FeedGetRequest(cursor, 5));

// Then
assertThat(feedGetResponse.list().size()).isEqualTo(5);
String nextCursor = missionRecords.get(missionRecords.size() - 1).getId().toString();
assertThat(feedGetResponse.nextCursor()).isEqualTo(nextCursor);
verify(memberUtil).getCurrentMember();
verify(feedRepository)
.getFeedContentsUsingCursor(Long.parseLong(cursor), member.getId(), 5);
}

@Test
void 피드_조회_커서_사용_마지막_성공() {
// Given
Member member = fixtureMonkey.giveMeOne(Member.class);
List<MissionRecord> missionRecords = new ArrayList<>();
String cursor = "5";

for (int i = 0; i < 3; i++) {
missionRecords.add(fixtureMonkey.giveMeOne(MissionRecord.class));
}

when(memberUtil.getCurrentMember()).thenReturn(member);
when(feedRepository.getFeedContentsUsingCursor(Long.parseLong(cursor), member.getId(), 5))
.thenReturn(missionRecords);

// When
FeedGetResponse feedGetResponse = feedService.getFeed(new FeedGetRequest(cursor, 5));

// Then
assertThat(feedGetResponse.list().size()).isEqualTo(3);
assertThat(feedGetResponse.nextCursor()).isEqualTo(null);
verify(memberUtil).getCurrentMember();
verify(feedRepository)
.getFeedContentsUsingCursor(Long.parseLong(cursor), member.getId(), 5);
}
}

0 comments on commit bf916b3

Please sign in to comment.