Skip to content
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
21 changes: 21 additions & 0 deletions src/asciidoc/api/group.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,27 @@ include::{snippets}/group-controller-rest-docs-test/create-group_success/http-re
include::{snippets}/group-controller-rest-docs-test/create-group_success/response-headers.adoc[]
include::{snippets}/group-controller-rest-docs-test/create-group_success/http-response.adoc[]

==== 1-3. 그룹 탈퇴

*Description* +

'''

그룹 탈퇴를 위해 사용합니다. 그룹장은 그룹을 탈퇴할 수 없습니다.

*Request* +

'''

include::{snippets}/group-entry-controller-rest-docs-test/leave-group_success/http-request.adoc[]
include::{snippets}/group-entry-controller-rest-docs-test/leave-group_success/path-parameters.adoc[]

*Response* +

'''

include::{snippets}/group-entry-controller-rest-docs-test/leave-group_success/http-response.adoc[]

=== 2. 그룹 가입 관련 API

==== 2-1. 초대 코드 생성
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
* - POST /groups/{groupId}/entry-code : 그룹 초대 코드 생성
* - GET /groups/summary : 그룹 대표 정보 조회
* - POST /groups/join : 공개 그룹에 가입
* - DELETE /groups/{groupId}/leave : 그룹 탈퇴
* - POST /groups/entry-requests : 비공개 그룹 가입 요청
* - GET /groups/{groupId}/entry-requests : 그룹 가입 요청 목록 조회
* - POST /groups/entry-requests/{requestId}/accept : 그룹 가입 요청 승인
Expand Down Expand Up @@ -82,6 +83,13 @@ public ResponseEntity<Void> joinGroup(@AuthenticationPrincipal Long userId, @Val
.build();
}

@DeleteMapping("/{groupId}/leave")
public ResponseEntity<Void> leaveGroup(@AuthenticationPrincipal Long userId, @PathVariable Long groupId) {
groupEntryService.leaveGroup(userId, groupId);

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

@PostMapping("/entry-requests")
public ResponseEntity<Void> requestGroupParticipant(
@AuthenticationPrincipal Long userId, @Valid @RequestBody GroupEntryReq req) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,14 @@ public interface GroupEntryService {
*/
Long joinGroup(Long userId, GroupEntryReq entryInfo);

/**
* 가입된 그룹에서 탈퇴합니다.
* 그룹장은 탈퇴할 수 없습니다.
* @param userId 그룹 멤버 ID
* @param groupId 탈퇴할 그룹 ID
*/
void leaveGroup(Long userId, Long groupId);

/**
* 비공개 그룹에 가입 요청을 보냅니다.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,16 @@ public Long joinGroup(Long userId, GroupEntryReq entryInfo) {
return internalJoinGroup(member, group);
}

@Override
@Transactional
public void leaveGroup(Long userId, Long groupId){
Group group = groupReader.getById(groupId);
Member member = memberReader.getRef(userId);

chatRoomWriter.leave(group.getChatRoom(), member);
groupMemberWriter.deleteMember(userId, group);
}

@Override
@Transactional
public Long requestParticipant(Long userId, GroupEntryReq entryInfo) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,17 @@ private GroupMember create(Member member, Group group, GroupRole role) {
}
return groupMember;
}

public void deleteMember(Long userId, Group group) {
GroupMember groupMember = groupMemberRepository.findByMemberIdAndGroupId(userId, group.getId())
.orElseThrow(() -> {
String message = String.format(
"[GroupMemberWriter#deleteMember] member %d not found in group %d", userId, group.getId());
return new GroupException(GroupErrorCode.GROUP_MEMBER_NOT_FOUND, message);
});
if(groupMember.isLeader()) {
throw new GroupException(GroupErrorCode.GROUP_LEAVE_FAIL, "leader can't leave the group");
}
groupMemberRepository.delete(groupMember);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,25 @@ void joinGroup_success() throws Exception {
responseHeaders(headerWithName("Location").description("추가된 그룹 멤버 id"))));
}

@Test
@WithMockUser
void leaveGroup_success() throws Exception {
// given
Long groupId = 1L;

// when
ResultActions result = mockMvc.perform(delete("/groups/{groupId}/leave", groupId));

// then
result.andExpect(status().isNoContent())
.andDo(restDocs.document(
httpRequest(),
httpResponse(),
pathParameters(parameterWithName("groupId")
.description("탈퇴할 그룹 ID")
.attributes(constraints("not null")))));
}

@Test
@WithMockUser
void requestParticipant_success() throws Exception {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,33 @@ void joinGroup_fail_groupMemberLimitExceed() {
.isEqualTo(GroupErrorCode.GROUP_JOIN_FAIL);
}

@Test
void leaveGroup_success() {
// given
Long userId = 1L;
Long groupId = 1L;
Group group = Group.builder()
.id(groupId)
.totalMember(10)
.maxMember(10)
.isApprovalRequired(false)
.build();
Member member = Member.builder()
.id(userId)
.build();
group.setChatRoom(mockChatRoom);

given(groupReader.getById(groupId)).willReturn(group);
given(memberReader.getRef(userId)).willReturn(member);

// when
groupEntryService.leaveGroup(userId, groupId);

// then
verify(chatRoomWriter).leave(mockChatRoom, member);
verify(groupMemberWriter).deleteMember(userId, group);
}

@Test
void requestParticipant_success() {
// given
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.verify;

import java.util.Optional;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
Expand Down Expand Up @@ -126,7 +129,6 @@ void createMember_fail_whileSave() {
@Test
void createMember_fail_memberLimitExceed() {
// given
Long memberId = 1L;
Long groupId = 1L;
GroupErrorCode errorCode = GroupErrorCode.GROUP_JOIN_FAIL;

Expand All @@ -139,4 +141,39 @@ void createMember_fail_memberLimitExceed() {
.extracting("errorCode")
.isEqualTo(errorCode);
}

@Test
void deleteMember_success() {
// given
Long userId = 1L;
Long groupId = 1L;

given(mockGroup.getId()).willReturn(groupId);
given(groupMemberRepository.findByMemberIdAndGroupId(userId, groupId)).willReturn(Optional.of(mockGroupMember));
given(mockGroupMember.isLeader()).willReturn(false);

// when
groupMemberWriter.deleteMember(userId, mockGroup);

// then
verify(groupMemberRepository).delete(mockGroupMember);
}

@Test
void deleteMember_fail_leaderCannotLeave() {
// given
Long userId = 1L;
Long groupId = 1L;
GroupErrorCode errorCode = GroupErrorCode.GROUP_LEAVE_FAIL;

given(mockGroup.getId()).willReturn(groupId);
given(groupMemberRepository.findByMemberIdAndGroupId(userId, groupId)).willReturn(Optional.of(mockGroupMember));
given(mockGroupMember.isLeader()).willReturn(true);

// when & then
assertThatThrownBy(() -> groupMemberWriter.deleteMember(userId, mockGroup))
.isInstanceOf(GroupException.class)
.extracting("errorCode")
.isEqualTo(errorCode);
}
}