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

feat : 스터디 그룹 공개 여부 설정 API 추가 #144

Merged
merged 10 commits into from
Nov 5, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import com.gamzabat.algohub.feature.group.studygroup.dto.CheckSolvedProblemResponse;
import com.gamzabat.algohub.feature.group.studygroup.dto.CreateGroupRequest;
import com.gamzabat.algohub.feature.group.studygroup.dto.EditGroupRequest;
import com.gamzabat.algohub.feature.group.studygroup.dto.EditGroupVisibilityRequest;
import com.gamzabat.algohub.feature.group.studygroup.dto.GetGroupMemberResponse;
import com.gamzabat.algohub.feature.group.studygroup.dto.GetGroupResponse;
import com.gamzabat.algohub.feature.group.studygroup.dto.GetStudyGroupListsResponse;
Expand Down Expand Up @@ -156,4 +157,14 @@ public ResponseEntity<String> getGroupRole(@AuthedUser User user, @RequestParam
String response = studyGroupService.getRoleInGroup(user, groupId);
return ResponseEntity.ok().body(response);
}

@PatchMapping(value = "/visibility")
@Operation(summary = "스터디 그룹 공개 여부 수정 API")
public ResponseEntity<Void> editStudyGroupVisibility(@AuthedUser User user,
@Valid @RequestBody EditGroupVisibilityRequest request, Errors errors) {
if (errors.hasErrors())
throw new RequestException("스터디 그룹 공개 수정 요청이 올바르지 않습니다.", errors);
studyGroupService.editStudyGroupVisibility(user, request);
return ResponseEntity.ok().build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,22 @@ public class GroupMember {
private LocalDate joinDate;
@Enumerated(EnumType.STRING)
private RoleOfGroupMember role;
private boolean isPublic;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Boolean + @NotNull 겁시다~
그리고 필드 이름이랑, 엔드포인트 이름이랑 어느정도 통합하게 isVisible 정도 어때요?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isVisible 좋네요! 수정해야겠어요 의견 감삼다

Copy link
Contributor Author

@rladmstn rladmstn Nov 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Boolean + @NotNull 겁시다

갑자기 생각난건데요 왜 엔티티에는 primitive type으로 안쓰나요?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

primitive 타입은 null을 소화할수 없어서 그렇습니다

ex) int age 했는데 db에 널 들어있는 경우

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

null이 불가능하단건 알고있었는데, reference type + NotNull을 하게 되면 결국 primitive type과 같아지는게 아닌건가요??

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵 맞긴합니다~
그냥 표현의 차이긴 합니다. primitive 쓴다고 문제가 생기진 않아서, 작업할때 원하는 방향 가도 되는 부분입니다
다만 notnull은 꼭 걸어줘야겠네용


@Builder
public GroupMember(User user, StudyGroup studyGroup, LocalDate joinDate, RoleOfGroupMember role) {
this.user = user;
this.studyGroup = studyGroup;
this.joinDate = joinDate;
this.role = role;
this.isPublic = true;
}

public void updateRole(RoleOfGroupMember role) {
this.role = role;
}

public void updateVisibility(boolean isPublic) {
this.isPublic = isPublic;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.gamzabat.algohub.feature.group.studygroup.dto;

import jakarta.validation.constraints.NotNull;

public record EditGroupVisibilityRequest(@NotNull(message = "그룹 아이디는 필수 입력입니다.") Long groupId,
boolean isPublic) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ public record GetStudyGroupResponse(Long id,
String introduction,
String ownerNickname,
boolean isOwner,
boolean isBookmarked) {
public static GetStudyGroupResponse toDTO(StudyGroup group, User user, boolean isBookmarked, User owner) {
boolean isBookmarked,
boolean isPublic) {
public static GetStudyGroupResponse toDTO(StudyGroup group, User user, boolean isBookmarked, User owner,
boolean isPublic) {
return new GetStudyGroupResponse(
group.getId(),
group.getName(),
Expand All @@ -23,7 +25,8 @@ public static GetStudyGroupResponse toDTO(StudyGroup group, User user, boolean i
group.getIntroduction(),
owner.getNickname(),
owner.getId().equals(user.getId()),
isBookmarked
isBookmarked,
isPublic
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import com.gamzabat.algohub.feature.group.studygroup.dto.CheckSolvedProblemResponse;
import com.gamzabat.algohub.feature.group.studygroup.dto.CreateGroupRequest;
import com.gamzabat.algohub.feature.group.studygroup.dto.EditGroupRequest;
import com.gamzabat.algohub.feature.group.studygroup.dto.EditGroupVisibilityRequest;
import com.gamzabat.algohub.feature.group.studygroup.dto.GetGroupMemberResponse;
import com.gamzabat.algohub.feature.group.studygroup.dto.GetGroupResponse;
import com.gamzabat.algohub.feature.group.studygroup.dto.GetStudyGroupListsResponse;
Expand Down Expand Up @@ -185,30 +186,26 @@ public GetStudyGroupListsResponse getStudyGroupList(User user) {
List<StudyGroup> groups = groupRepository.findAllByUser(user);

List<GetStudyGroupResponse> bookmarked = bookmarkedStudyGroupRepository.findAllByUser(user).stream()
.map(bookmark -> GetStudyGroupResponse.toDTO(bookmark.getStudyGroup(), user, true,
getStudyGroupOwner(bookmark.getStudyGroup())))
.map(bookmark -> getStudyGroupResponseDTO(user, bookmark.getStudyGroup()))
.toList();

LocalDate today = LocalDate.now();

List<GetStudyGroupResponse> done = groups.stream()
.filter(group -> group.getEndDate() != null && group.getEndDate().isBefore(today))
.map(
group -> GetStudyGroupResponse.toDTO(group, user, isBookmarked(user, group), getStudyGroupOwner(group)))
.map(group -> getStudyGroupResponseDTO(user, group))
.toList();

List<GetStudyGroupResponse> inProgress = groups.stream()
.filter(
group -> !(group.getStartDate() == null || group.getStartDate().isAfter(today))
&& !(group.getEndDate() == null || group.getEndDate().isBefore(today)))
.map(
group -> GetStudyGroupResponse.toDTO(group, user, isBookmarked(user, group), getStudyGroupOwner(group)))
.map(group -> getStudyGroupResponseDTO(user, group))
.toList();

List<GetStudyGroupResponse> queued = groups.stream()
.filter(group -> group.getStartDate() != null && group.getStartDate().isAfter(today))
.map(
group -> GetStudyGroupResponse.toDTO(group, user, isBookmarked(user, group), getStudyGroupOwner(group)))
.map(group -> getStudyGroupResponseDTO(user, group))
.toList();

GetStudyGroupListsResponse response = new GetStudyGroupListsResponse(bookmarked, done, inProgress, queued);
Expand All @@ -217,6 +214,13 @@ public GetStudyGroupListsResponse getStudyGroupList(User user) {
return response;
}

private GetStudyGroupResponse getStudyGroupResponseDTO(User user, StudyGroup group) {
GroupMember member = groupMemberRepository.findByUserAndStudyGroup(user, group)
.orElseThrow(() -> new GroupMemberValidationException(HttpStatus.FORBIDDEN.value(), "참여하지 않은 스터디 그룹입니다."));
return GetStudyGroupResponse.toDTO(group, user, isBookmarked(user, group), getStudyGroupOwner(group),
member.isPublic());
}

private User getStudyGroupOwner(StudyGroup group) {
return groupMemberRepository.findByStudyGroupAndRole(group, RoleOfGroupMember.OWNER).getUser();
}
Expand Down Expand Up @@ -416,4 +420,16 @@ public String getRoleInGroup(User user, Long groupId) {

return member.getRole().getValue();
}

@Transactional
public void editStudyGroupVisibility(User user, EditGroupVisibilityRequest request) {
StudyGroup group = groupRepository.findById(request.groupId())
.orElseThrow(() -> new CannotFoundGroupException("존재하지 않는 그룹입니다."));

GroupMember member = groupMemberRepository.findByUserAndStudyGroup(user, group)
.orElseThrow(() -> new GroupMemberValidationException(HttpStatus.FORBIDDEN.value(), "참여하지 않은 그룹입니다."));

member.updateVisibility(request.isPublic());
log.info("success to update group visibility");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] 로깅에 식별자정도 남기면 좋을 것 같아요~

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import com.gamzabat.algohub.feature.group.studygroup.dto.CheckSolvedProblemResponse;
import com.gamzabat.algohub.feature.group.studygroup.dto.CreateGroupRequest;
import com.gamzabat.algohub.feature.group.studygroup.dto.EditGroupRequest;
import com.gamzabat.algohub.feature.group.studygroup.dto.EditGroupVisibilityRequest;
import com.gamzabat.algohub.feature.group.studygroup.dto.GetGroupMemberResponse;
import com.gamzabat.algohub.feature.group.studygroup.dto.GetStudyGroupListsResponse;
import com.gamzabat.algohub.feature.group.studygroup.dto.GetStudyGroupResponse;
Expand Down Expand Up @@ -215,29 +216,29 @@ void getStudyGroupList() throws Exception {
bookmarked.add(new GetStudyGroupResponse(
(long)i, "name" + i, "groupImage" + 1,
DateFormatUtil.formatDate(LocalDate.now()), DateFormatUtil.formatDate(LocalDate.now().plusDays(i)),
"introduction" + 1, "nickname", true, true
"introduction" + 1, "nickname", true, true, true
));
}

for (int i = 0; i < 10; i++) {
done.add(new GetStudyGroupResponse(
(long)i, "name" + i, "groupImage" + 1,
DateFormatUtil.formatDate(LocalDate.now()), DateFormatUtil.formatDate(LocalDate.now().plusDays(i)),
"introduction" + 1, "nickname", true, true
"introduction" + 1, "nickname", true, true, true
));
}
for (int i = 0; i < 10; i++) {
inProgress.add(new GetStudyGroupResponse(
(long)i, "name" + i, "groupImage" + 1,
DateFormatUtil.formatDate(LocalDate.now()), DateFormatUtil.formatDate(LocalDate.now().plusDays(i)),
"introduction" + 1, "nickname", true, true
"introduction" + 1, "nickname", true, true, true
));
}
for (int i = 0; i < 10; i++) {
queued.add(new GetStudyGroupResponse(
(long)i, "name" + i, "groupImage" + 1,
DateFormatUtil.formatDate(LocalDate.now()), DateFormatUtil.formatDate(LocalDate.now().plusDays(i)),
"introduction" + 1, "nickname", true, true
"introduction" + 1, "nickname", true, true, true
));
}
GetStudyGroupListsResponse response = new GetStudyGroupListsResponse(bookmarked, done, inProgress, queued);
Expand Down Expand Up @@ -813,4 +814,50 @@ void getRoleInGroupFailed_2() throws Exception {
.andExpect(jsonPath("$.error").value("참여하지 않은 그룹입니다."));
verify(studyGroupService, times(1)).getRoleInGroup(user, groupId);
}

@Test
@DisplayName("스터디 그룹 공개 여부 수정 성공")
void editGroupVisibility() throws Exception {
// given
EditGroupVisibilityRequest request = new EditGroupVisibilityRequest(groupId, false);
// when, then
mockMvc.perform(patch("/api/group/visibility")
.header("Authorization", token)
.content(objectMapper.writeValueAsString(request)))
.andExpect(status().isOk());
verify(studyGroupService, times(1)).editStudyGroupVisibility(user, request);
}

@Test
@DisplayName("스터디 그룹 공개 여부 수정 실패 : 존재하지 않는 스터디 그룹")
void editGroupVisibilityFailed_1() throws Exception {
// given
EditGroupVisibilityRequest request = new EditGroupVisibilityRequest(groupId, false);
doThrow(new CannotFoundGroupException("존재하지 않는 그룹입니다.")).when(studyGroupService)
.editStudyGroupVisibility(user, request);
// when, then
mockMvc.perform(patch("/api/group/visibility")
.header("Authorization", token)
.content(objectMapper.writeValueAsString(request)))
.andExpect(status().isNotFound())
.andExpect(jsonPath("$.error").value("존재하지 않는 그룹입니다."));
verify(studyGroupService, times(1)).editStudyGroupVisibility(user, request);
}

@Test
@DisplayName("스터디 그룹 공개 여부 수정 실패 : 참여하지 않은 그룹")
void editGroupVisibilityFailed_2() throws Exception {
// given
EditGroupVisibilityRequest request = new EditGroupVisibilityRequest(groupId, false);
doThrow(new GroupMemberValidationException(HttpStatus.FORBIDDEN.value(), "참여하지 않은 그룹입니다.")).when(
studyGroupService)
.editStudyGroupVisibility(user, request);
// when, then
mockMvc.perform(patch("/api/group/visibility")
.header("Authorization", token)
.content(objectMapper.writeValueAsString(request)))
.andExpect(status().isForbidden())
.andExpect(jsonPath("$.error").value("참여하지 않은 그룹입니다."));
verify(studyGroupService, times(1)).editStudyGroupVisibility(user, request);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import com.gamzabat.algohub.feature.group.studygroup.dto.BookmarkStatus;
import com.gamzabat.algohub.feature.group.studygroup.dto.CreateGroupRequest;
import com.gamzabat.algohub.feature.group.studygroup.dto.EditGroupRequest;
import com.gamzabat.algohub.feature.group.studygroup.dto.EditGroupVisibilityRequest;
import com.gamzabat.algohub.feature.group.studygroup.dto.GetGroupMemberResponse;
import com.gamzabat.algohub.feature.group.studygroup.dto.GetStudyGroupListsResponse;
import com.gamzabat.algohub.feature.group.studygroup.dto.GetStudyGroupResponse;
Expand Down Expand Up @@ -321,6 +322,8 @@ void getGroupList() {
groups.add(group);
when(groupMemberRepository.findByStudyGroupAndRole(group, RoleOfGroupMember.OWNER)).thenReturn(
ownerGroupmember);
when(groupMemberRepository.findByUserAndStudyGroup(user, group)).thenReturn(
Optional.ofNullable(groupMember1));
}
for (int i = 0; i < 10; i++) {
StudyGroup group = StudyGroup.builder()
Expand All @@ -331,6 +334,8 @@ void getGroupList() {
groups.add(group);
when(groupMemberRepository.findByStudyGroupAndRole(group, RoleOfGroupMember.OWNER)).thenReturn(
ownerGroupmember);
when(groupMemberRepository.findByUserAndStudyGroup(user, group)).thenReturn(
Optional.ofNullable(groupMember1));
}
for (int i = 0; i < 10; i++) {
StudyGroup group = StudyGroup.builder()
Expand All @@ -341,6 +346,8 @@ void getGroupList() {
groups.add(group);
when(groupMemberRepository.findByStudyGroupAndRole(group, RoleOfGroupMember.OWNER)).thenReturn(
groupMember2);
when(groupMemberRepository.findByUserAndStudyGroup(user, group)).thenReturn(
Optional.ofNullable(groupMember1));
}
List<BookmarkedStudyGroup> bookmarks = new ArrayList<>(10);
for (int i = 0; i < 10; i++) {
Expand Down Expand Up @@ -662,4 +669,42 @@ void getRoleInGroupFailed_2() {
.hasFieldOrPropertyWithValue("error", "참여하지 않은 그룹입니다.");
}

@Test
@DisplayName("스터디 그룹 공개 여부 수정 성공")
void editGroupVisibility() {
// given
EditGroupVisibilityRequest request = new EditGroupVisibilityRequest(groupId, false);
when(studyGroupRepository.findById(groupId)).thenReturn(Optional.of(group));
when(groupMemberRepository.findByUserAndStudyGroup(user, group)).thenReturn(Optional.of(groupMember1));
// when
studyGroupService.editStudyGroupVisibility(user, request);
// then
assertThat(groupMember1.isPublic()).isFalse();
}

@Test
@DisplayName("스터디 그룹 공개 여부 수정 실패 : 존재하지 않는 그룹")
void editGroupVisibilityFailed_1() {
// given
EditGroupVisibilityRequest request = new EditGroupVisibilityRequest(groupId, false);
when(studyGroupRepository.findById(groupId)).thenReturn(Optional.empty());
// when, then
assertThatThrownBy(() -> studyGroupService.editStudyGroupVisibility(user, request))
.isInstanceOf(CannotFoundGroupException.class)
.hasFieldOrPropertyWithValue("errors", "존재하지 않는 그룹입니다.");
}

@Test
@DisplayName("스터디 그룹 공개 여부 수정 실패 : 참여하지 않은 그룹")
void editGroupVisibilityFailed_2() {
// given
EditGroupVisibilityRequest request = new EditGroupVisibilityRequest(groupId, false);
when(studyGroupRepository.findById(groupId)).thenReturn(Optional.of(group));
when(groupMemberRepository.findByUserAndStudyGroup(user, group)).thenReturn(Optional.empty());
// when, then
assertThatThrownBy(() -> studyGroupService.editStudyGroupVisibility(user, request))
.isInstanceOf(GroupMemberValidationException.class)
.hasFieldOrPropertyWithValue("code", HttpStatus.FORBIDDEN.value())
.hasFieldOrPropertyWithValue("error", "참여하지 않은 그룹입니다.");
}
}
Loading