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

Feature 195: 주 목표 연관 목표 연결/연결 해제 기능 구현 #196

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from
Open
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 @@ -68,6 +68,7 @@ public enum ErrorCode {
INVALID_DIFFERENCE_OF_DATE("ERR_WEEKLY_GOAL_003", "주 목표 기간의 시작일과 종료일은 6일 차이여야 합니다."),
WEEKLY_GOAL_ACCESS_DENIED("ERR_WEEKLY_GOAL_004", "해당 사용자는 접근할 수 없는 주 목표입니다."),
WEEK_NOT_CONTAINS_DATE("ERR_WEEKLY_GOAL_005", "해당 주차에 존재하지 않는 날짜입니다."),
RELATED_GOAL_NOT_FOUND("ERR_WEEKLY_GOAL_006", "해당 주 목표에 연관 목표가 존재하지 않습니다."),

// goal
NULL_GOAL_TITLE("ERR_GOAL_001", "목표 제목은 null일 수 없습니다."),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.sillim.recordit.global.exception.goal;

import com.sillim.recordit.global.exception.ErrorCode;
import com.sillim.recordit.global.exception.common.ApplicationException;

public class InvalidWeeklyGoalException extends ApplicationException {

public InvalidWeeklyGoalException(ErrorCode errorCode) {
super(errorCode);
}

public InvalidWeeklyGoalException(ErrorCode errorCode, String errorMessage) {
super(errorCode, errorMessage);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ public ResponseEntity<MonthlyGoalDetailsResponse> monthlyGoalDetails(
monthlyGoalQueryService.searchByIdAndCheckAuthority(id, member.getId())));
}

@PatchMapping("/{id}")
@PatchMapping("/{id}/achieve")
public ResponseEntity<Void> monthlyGoalChangeAchieveStatus(
@PathVariable final Long id,
@RequestParam final Boolean status,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ public ResponseEntity<Void> modifyWeeklyGoal(
return ResponseEntity.noContent().build();
}

@PatchMapping("/{id}")
@PatchMapping("/{id}/achieve")
public ResponseEntity<Void> changeWeeklyGoalAchieveStatus(
@PathVariable final Long id,
@RequestParam final Boolean status,
Expand All @@ -105,6 +105,26 @@ public ResponseEntity<Void> removeWeeklyGoal(
return ResponseEntity.noContent().build();
}

@PatchMapping("/{id}/link")
public ResponseEntity<Void> linkRelatedMonthlyGoal(
@PathVariable final Long id,
@RequestParam final Long relatedGoalId,
@CurrentMember final Member member) {

weeklyGoalUpdateService.linkRelatedMonthlyGoal(id, relatedGoalId, member.getId());

return ResponseEntity.noContent().build();
}

@PatchMapping("/{id}/unlink")
public ResponseEntity<Void> unlinkRelatedMonthlyGoal(
@PathVariable final Long id, @CurrentMember final Member member) {

weeklyGoalUpdateService.unlinkRelatedMonthlyGoal(id, member.getId());

return ResponseEntity.noContent().build();
}

private Integer changeWeekIfMonthOfStartDateIsNotEqual(
final WeeklyGoal weeklyGoal, final Integer currentMonth) {
if (weeklyGoal.getStartDate().getMonthValue() == currentMonth) {
Expand Down
8 changes: 8 additions & 0 deletions src/main/java/com/sillim/recordit/goal/domain/WeeklyGoal.java
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,14 @@ public void modify(
this.colorHex = new GoalColorHex(colorHex);
}

public void linkRelatedMonthlyGoal(final MonthlyGoal relatedMonthlyGoal) {
this.relatedMonthlyGoal = relatedMonthlyGoal;
}

public void unlinkRelatedMonthlyGoal() {
this.relatedMonthlyGoal = null;
}

public void remove() {
this.deleted = true;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.sillim.recordit.goal.service;

import com.sillim.recordit.global.exception.ErrorCode;
import com.sillim.recordit.global.exception.goal.InvalidWeeklyGoalException;
import com.sillim.recordit.goal.domain.MonthlyGoal;
import com.sillim.recordit.goal.domain.WeeklyGoal;
import com.sillim.recordit.goal.dto.request.WeeklyGoalUpdateRequest;
Expand Down Expand Up @@ -78,4 +80,24 @@ public void remove(final Long weeklyGoalId, final Long memberId) {
weeklyGoalQueryService.searchByIdAndCheckAuthority(weeklyGoalId, memberId);
weeklyGoal.remove();
}

public void linkRelatedMonthlyGoal(
final Long weeklyGoalId, final Long monthlyGoalId, final Long memberId) {

WeeklyGoal weeklyGoal =
weeklyGoalQueryService.searchByIdAndCheckAuthority(weeklyGoalId, memberId);
MonthlyGoal monthlyGoal =
monthlyGoalQueryService.searchByIdAndCheckAuthority(monthlyGoalId, memberId);
weeklyGoal.linkRelatedMonthlyGoal(monthlyGoal);
}

public void unlinkRelatedMonthlyGoal(final Long weeklyGoalId, final Long memberId) {

WeeklyGoal weeklyGoal =
weeklyGoalQueryService.searchByIdAndCheckAuthority(weeklyGoalId, memberId);
if (weeklyGoal.getRelatedMonthlyGoal().isEmpty()) {
throw new InvalidWeeklyGoalException(ErrorCode.RELATED_GOAL_NOT_FOUND);
}
weeklyGoal.unlinkRelatedMonthlyGoal();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ void monthlyGoalChangeAchieveStatusTest() throws Exception {

ResultActions perform =
mockMvc.perform(
patch("/api/v1/goals/months/{id}", 1L)
patch("/api/v1/goals/months/{id}/achieve", 1L)
.headers(authorizationHeader())
.queryParam("status", "true"));

Expand Down Expand Up @@ -291,7 +291,7 @@ void monthlyGoalChangeAchieveStatusNotFoundTest() throws Exception {

ResultActions perform =
mockMvc.perform(
patch("/api/v1/goals/months/{id}", 1L)
patch("/api/v1/goals/months/{id}/achieve", 1L)
.headers(authorizationHeader())
.queryParam("status", "true"));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ void weeklyGoalChangeAchieveStatus() throws Exception {

ResultActions perform =
mockMvc.perform(
patch("/api/v1/goals/weeks/{id}", 1L)
patch("/api/v1/goals/weeks/{id}/achieve", 1L)
.headers(authorizationHeader())
.queryParam("status", "true"));

Expand Down Expand Up @@ -288,4 +288,53 @@ void removeWeeklyGoal() throws Exception {
pathParameters(
parameterWithName("id").description("삭제할 주 목표 id"))));
}

@Test
@DisplayName("id에 해당하는 주 목표를 월 목표와 연결한다.")
void linkRelatedGoal() throws Exception {

ResultActions perform =
mockMvc.perform(
patch("/api/v1/goals/weeks/{id}/link", 1L)
.headers(authorizationHeader())
.queryParam("relatedGoalId", "1"));

perform.andExpect(status().isNoContent());

perform.andDo(print())
.andDo(
document(
"weekly-goal-link-related-goal",
getDocumentRequest(),
getDocumentResponse(),
requestHeaders(authorizationDesc()),
pathParameters(
parameterWithName("id").description("연관 목표를 연결할 주 목표 id")),
queryParameters(
parameterWithName("relatedGoalId")
.description("주 목표와 연결할 월 목표 id"))));
}

@Test
@DisplayName("id에 해당하는 주 목표와 연관 목표의 연결을 해제할 수 있다.")
void unlinkRelatedGoal() throws Exception {

ResultActions perform =
mockMvc.perform(
patch("/api/v1/goals/weeks/{id}/unlink", 1L)
.headers(authorizationHeader()));

perform.andExpect(status().isNoContent());

perform.andDo(print())
.andDo(
document(
"weekly-goal-unlink-related-goal",
getDocumentRequest(),
getDocumentResponse(),
requestHeaders(authorizationDesc()),
pathParameters(
parameterWithName("id")
.description("연관 목표를 연결 해제할 주 목표 id"))));
}
}
28 changes: 28 additions & 0 deletions src/test/java/com/sillim/recordit/goal/domain/WeeklyGoalTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import com.sillim.recordit.global.exception.ErrorCode;
import com.sillim.recordit.global.exception.common.InvalidRequestException;
import com.sillim.recordit.goal.fixture.MonthlyGoalFixture;
import com.sillim.recordit.goal.fixture.WeeklyGoalFixture;
import com.sillim.recordit.member.domain.Member;
import com.sillim.recordit.member.fixture.MemberFixture;
Expand Down Expand Up @@ -94,4 +95,31 @@ void remove() {

assertThat(weeklyGoal.isDeleted()).isTrue();
}

@Test
@DisplayName("주 목표와 월 목표를 연결할 수 있다.")
void linkRelatedMonthlyGoal() {

WeeklyGoal weeklyGoal = WeeklyGoalFixture.DEFAULT.getWithMember(member);
MonthlyGoal relatedMonthlyGoal = MonthlyGoalFixture.DEFAULT.getWithMember(member);
weeklyGoal.linkRelatedMonthlyGoal(relatedMonthlyGoal);

assertThat(weeklyGoal.getRelatedMonthlyGoal()).isNotEmpty();
assertThat(weeklyGoal.getRelatedMonthlyGoal().get())
.usingRecursiveComparison()
.isEqualTo(relatedMonthlyGoal);
}

@Test
@DisplayName("주 목표의 연관 목표를 연결 해제 할 수 있다.")
void unlinkRelatedMonthlyGoal() {

WeeklyGoal weeklyGoal = WeeklyGoalFixture.DEFAULT.getWithMember(member);
MonthlyGoal relatedMonthlyGoal = MonthlyGoalFixture.DEFAULT.getWithMember(member);
weeklyGoal.linkRelatedMonthlyGoal(relatedMonthlyGoal);

weeklyGoal.unlinkRelatedMonthlyGoal();

assertThat(weeklyGoal.getRelatedMonthlyGoal()).isEmpty();
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.sillim.recordit.goal.service;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
Expand All @@ -9,9 +10,12 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;

import com.sillim.recordit.global.exception.ErrorCode;
import com.sillim.recordit.global.exception.goal.InvalidWeeklyGoalException;
import com.sillim.recordit.goal.domain.MonthlyGoal;
import com.sillim.recordit.goal.domain.WeeklyGoal;
import com.sillim.recordit.goal.dto.request.WeeklyGoalUpdateRequest;
import com.sillim.recordit.goal.fixture.MonthlyGoalFixture;
import com.sillim.recordit.goal.fixture.WeeklyGoalFixture;
import com.sillim.recordit.goal.repository.WeeklyGoalRepository;
import com.sillim.recordit.member.domain.Member;
Expand Down Expand Up @@ -168,13 +172,13 @@ void modifyWeeklyGoalWithRelatedMonthlyGoal() {
@DisplayName("id에 해당하는 주 목표의 달성 상태를 변경한다.")
void changeAchieveStatus() {
Long memberId = 1L;
Long monthlyGoalId = 2L;
Long weeklyGoalId = 2L;
Boolean status = true;
WeeklyGoal weeklyGoal = WeeklyGoalFixture.DEFAULT.getWithMember(member);
given(weeklyGoalQueryService.searchByIdAndCheckAuthority(eq(monthlyGoalId), eq(memberId)))
given(weeklyGoalQueryService.searchByIdAndCheckAuthority(eq(weeklyGoalId), eq(memberId)))
.willReturn(weeklyGoal);

weeklyGoalUpdateService.changeAchieveStatus(monthlyGoalId, status, memberId);
weeklyGoalUpdateService.changeAchieveStatus(weeklyGoalId, status, memberId);

assertThat(weeklyGoal.isAchieved()).isTrue();
}
Expand All @@ -183,13 +187,67 @@ void changeAchieveStatus() {
@DisplayName("id에 해당하는 주 목표를 삭제한다.")
void removeTest() {
Long memberId = 1L;
Long monthlyGoalId = 2L;
Long weeklyGoalId = 2L;
WeeklyGoal weeklyGoal = WeeklyGoalFixture.DEFAULT.getWithMember(member);
given(weeklyGoalQueryService.searchByIdAndCheckAuthority(eq(monthlyGoalId), eq(memberId)))
given(weeklyGoalQueryService.searchByIdAndCheckAuthority(eq(weeklyGoalId), eq(memberId)))
.willReturn(weeklyGoal);

weeklyGoalUpdateService.remove(monthlyGoalId, memberId);
weeklyGoalUpdateService.remove(weeklyGoalId, memberId);

assertThat(weeklyGoal.isDeleted()).isTrue();
}

@Test
@DisplayName("id에 해당하는 주 목표를 월 목표와 연결한다.")
void linkRelatedMonthlyGoal() {
Long memberId = 1L;
Long weeklyGoalId = 2L;
Long relatedGoalId = 3L;
WeeklyGoal weeklyGoal = WeeklyGoalFixture.DEFAULT.getWithMember(member);
given(weeklyGoalQueryService.searchByIdAndCheckAuthority(eq(weeklyGoalId), eq(memberId)))
.willReturn(weeklyGoal);
MonthlyGoal relatedMonthlyGoal = MonthlyGoalFixture.DEFAULT.getWithMember(member);
given(monthlyGoalQueryService.searchByIdAndCheckAuthority(eq(relatedGoalId), eq(memberId)))
.willReturn(relatedMonthlyGoal);

weeklyGoalUpdateService.linkRelatedMonthlyGoal(weeklyGoalId, relatedGoalId, memberId);

assertThat(weeklyGoal.getRelatedMonthlyGoal()).isNotEmpty();
assertThat(weeklyGoal.getRelatedMonthlyGoal().get())
.usingRecursiveComparison()
.isEqualTo(relatedMonthlyGoal);
}

@Test
@DisplayName("id에 해당하는 주 목표를 월 목표와 연결한다.")
void unlinkRelatedMonthlyGoal() {
Long memberId = 1L;
Long weeklyGoalId = 2L;
WeeklyGoal weeklyGoal = WeeklyGoalFixture.DEFAULT.getWithMember(member);
weeklyGoal.linkRelatedMonthlyGoal(MonthlyGoalFixture.DEFAULT.getWithMember(member));
given(weeklyGoalQueryService.searchByIdAndCheckAuthority(eq(weeklyGoalId), eq(memberId)))
.willReturn(weeklyGoal);

weeklyGoalUpdateService.unlinkRelatedMonthlyGoal(weeklyGoalId, memberId);

assertThat(weeklyGoal.getRelatedMonthlyGoal()).isEmpty();
}

@Test
@DisplayName("주 목표의 연관 목표가 없는 상태에서 연결 해제를 시도할 경우, InvalidWeeklyGoalException이 발생한다.")
void unlinkRelatedMonthlyGoalThrowsInvalidWeeklyGoalExceptionIfRelatedGoalIsNotExists() {
Long memberId = 1L;
Long weeklyGoalId = 2L;
WeeklyGoal weeklyGoal = WeeklyGoalFixture.DEFAULT.getWithMember(member);
given(weeklyGoalQueryService.searchByIdAndCheckAuthority(eq(weeklyGoalId), eq(memberId)))
.willReturn(weeklyGoal);

assertThatCode(
() -> {
weeklyGoalUpdateService.unlinkRelatedMonthlyGoal(
weeklyGoalId, memberId);
})
.isInstanceOf(InvalidWeeklyGoalException.class)
.hasMessage(ErrorCode.RELATED_GOAL_NOT_FOUND.getDescription());
}
}
Loading