diff --git a/src/main/java/com/postsquad/scoup/web/group/controller/GroupController.java b/src/main/java/com/postsquad/scoup/web/group/controller/GroupController.java index dbe6c1f0..dac12dfe 100644 --- a/src/main/java/com/postsquad/scoup/web/group/controller/GroupController.java +++ b/src/main/java/com/postsquad/scoup/web/group/controller/GroupController.java @@ -4,6 +4,7 @@ import com.postsquad.scoup.web.error.controller.response.ErrorResponse; import com.postsquad.scoup.web.group.controller.request.GroupCreationRequest; import com.postsquad.scoup.web.group.controller.request.GroupModificationRequest; +import com.postsquad.scoup.web.group.controller.response.GroupReadAllResponses; import com.postsquad.scoup.web.group.controller.request.GroupValidationRequest; import com.postsquad.scoup.web.group.controller.response.GroupReadOneResponse; import com.postsquad.scoup.web.group.controller.response.GroupValidationResponse; @@ -71,4 +72,9 @@ public GroupValidationResponse validateGroupName(@Valid GroupValidationRequest g @ResponseStatus(HttpStatus.NO_CONTENT) public void leaveGroup(@PathVariable long groupId) { } + + @GetMapping + public GroupReadAllResponses readAll(@LoggedInUser User user) { + return groupService.readAllByUser(user); + } } diff --git a/src/main/java/com/postsquad/scoup/web/group/controller/response/GroupBaseResponse.java b/src/main/java/com/postsquad/scoup/web/group/controller/response/GroupBaseResponse.java index 9a7797b9..3779ad4e 100644 --- a/src/main/java/com/postsquad/scoup/web/group/controller/response/GroupBaseResponse.java +++ b/src/main/java/com/postsquad/scoup/web/group/controller/response/GroupBaseResponse.java @@ -1,19 +1,17 @@ package com.postsquad.scoup.web.group.controller.response; -import com.postsquad.scoup.web.image.controller.ImageResponse; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +@NoArgsConstructor +@AllArgsConstructor +@Data public class GroupBaseResponse { private Long id; - // TODO: 필요한지 확인 필요 - private ImageResponse image; - - // TODO: 필요한지 확인 필요 - private long memberCount; - private String name; private String description; - } diff --git a/src/main/java/com/postsquad/scoup/web/group/controller/response/GroupReadAllResponse.java b/src/main/java/com/postsquad/scoup/web/group/controller/response/GroupReadAllResponse.java new file mode 100644 index 00000000..b1483fdd --- /dev/null +++ b/src/main/java/com/postsquad/scoup/web/group/controller/response/GroupReadAllResponse.java @@ -0,0 +1,19 @@ +package com.postsquad.scoup.web.group.controller.response; + +import com.postsquad.scoup.web.group.domain.Group; +import lombok.*; + +@EqualsAndHashCode(callSuper = false) +@AllArgsConstructor +@Data +public class GroupReadAllResponse extends GroupBaseResponse { + + @Builder + protected GroupReadAllResponse(Long id, String name, String description) { + super(id, name, description); + } + + public static GroupReadAllResponse from(Group group) { + return GroupReadAllResponse.builder().id(group.getId()).name(group.getName()).description(group.getDescription()).build(); + } +} diff --git a/src/main/java/com/postsquad/scoup/web/group/controller/response/GroupReadAllResponses.java b/src/main/java/com/postsquad/scoup/web/group/controller/response/GroupReadAllResponses.java new file mode 100644 index 00000000..11a8220a --- /dev/null +++ b/src/main/java/com/postsquad/scoup/web/group/controller/response/GroupReadAllResponses.java @@ -0,0 +1,14 @@ +package com.postsquad.scoup.web.group.controller.response; + +import lombok.*; + +import java.util.List; + +@NoArgsConstructor +@AllArgsConstructor(staticName = "from") +@Builder +@Data +public class GroupReadAllResponses { + + private List groupReadAllResponses; +} diff --git a/src/main/java/com/postsquad/scoup/web/group/controller/response/GroupResponse.java b/src/main/java/com/postsquad/scoup/web/group/controller/response/GroupResponse.java deleted file mode 100644 index 80b13dd2..00000000 --- a/src/main/java/com/postsquad/scoup/web/group/controller/response/GroupResponse.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.postsquad.scoup.web.group.controller.response; - -import lombok.Getter; - -@Getter -public class GroupResponse extends GroupBaseResponse { -} diff --git a/src/main/java/com/postsquad/scoup/web/group/controller/response/GroupResponses.java b/src/main/java/com/postsquad/scoup/web/group/controller/response/GroupResponses.java deleted file mode 100644 index 9af9a304..00000000 --- a/src/main/java/com/postsquad/scoup/web/group/controller/response/GroupResponses.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.postsquad.scoup.web.group.controller.response; - -import lombok.Getter; - -import java.util.List; - -@Getter -public class GroupResponses { - - private List groupResponses; -} diff --git a/src/main/java/com/postsquad/scoup/web/group/domain/Group.java b/src/main/java/com/postsquad/scoup/web/group/domain/Group.java index 1c580e25..b2263fc1 100644 --- a/src/main/java/com/postsquad/scoup/web/group/domain/Group.java +++ b/src/main/java/com/postsquad/scoup/web/group/domain/Group.java @@ -2,21 +2,12 @@ import com.postsquad.scoup.web.common.BaseEntity; import com.postsquad.scoup.web.group.controller.request.GroupModificationRequest; -import com.postsquad.scoup.web.schedule.domain.ConfirmedSchedule; import com.postsquad.scoup.web.schedule.domain.Schedule; -import com.postsquad.scoup.web.schedule.domain.ScheduleCandidate; import com.postsquad.scoup.web.user.domain.User; import lombok.*; -import org.hibernate.annotations.Fetch; -import org.hibernate.annotations.FetchMode; -import org.hibernate.annotations.LazyCollection; -import org.hibernate.annotations.LazyCollectionOption; import javax.persistence.*; -import java.util.ArrayList; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Set; +import java.util.*; @NoArgsConstructor(access = AccessLevel.PROTECTED) @Getter @@ -39,6 +30,14 @@ public class Group extends BaseEntity { @OneToMany(mappedBy = "group", cascade = CascadeType.ALL) private final List schedules = new ArrayList<>(); + @ManyToMany + @JoinTable( + name = "group_member", + joinColumns = @JoinColumn(name = "group_id"), + inverseJoinColumns = @JoinColumn(name = "user_id") + ) + private final List members = new ArrayList<>(); + protected Group(String name, String description, User owner) { this.name = name; this.description = description; @@ -72,4 +71,11 @@ public void addSchedule(Schedule schedule) { public void addSchedules(List schedules) { this.schedules.addAll(schedules); } + + public void addMember(User user) { + this.members.add(user); + if (!user.getJoinedGroups().contains(this)) { + user.getJoinedGroups().add(this); + } + } } diff --git a/src/main/java/com/postsquad/scoup/web/group/mapper/GroupMapper.java b/src/main/java/com/postsquad/scoup/web/group/mapper/GroupMapper.java index 7dd1d433..d31e9616 100644 --- a/src/main/java/com/postsquad/scoup/web/group/mapper/GroupMapper.java +++ b/src/main/java/com/postsquad/scoup/web/group/mapper/GroupMapper.java @@ -1,6 +1,7 @@ package com.postsquad.scoup.web.group.mapper; import com.postsquad.scoup.web.group.controller.request.GroupCreationRequest; +import com.postsquad.scoup.web.group.controller.response.GroupReadAllResponse; import com.postsquad.scoup.web.group.domain.Group; import com.postsquad.scoup.web.user.domain.User; import org.mapstruct.Mapper; @@ -14,4 +15,6 @@ public interface GroupMapper { @Mapping(target = "owner", source = "owner") Group map(GroupCreationRequest groupCreationRequest, User owner); + + GroupReadAllResponse map(Group group); } diff --git a/src/main/java/com/postsquad/scoup/web/group/service/GroupService.java b/src/main/java/com/postsquad/scoup/web/group/service/GroupService.java index c1d8666e..6b9a5e61 100644 --- a/src/main/java/com/postsquad/scoup/web/group/service/GroupService.java +++ b/src/main/java/com/postsquad/scoup/web/group/service/GroupService.java @@ -3,6 +3,7 @@ import com.postsquad.scoup.web.common.DefaultPostResponse; import com.postsquad.scoup.web.group.controller.request.GroupCreationRequest; import com.postsquad.scoup.web.group.controller.request.GroupModificationRequest; +import com.postsquad.scoup.web.group.controller.response.GroupReadAllResponses; import com.postsquad.scoup.web.group.domain.Group; import com.postsquad.scoup.web.group.exception.GroupNameAlreadyExistException; import com.postsquad.scoup.web.group.exception.GroupNotFoundException; @@ -10,14 +11,18 @@ import com.postsquad.scoup.web.group.repository.GroupRepository; import com.postsquad.scoup.web.signin.exception.UnauthorizedUserException; import com.postsquad.scoup.web.user.domain.User; +import com.postsquad.scoup.web.user.service.UserService; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import java.util.stream.Collectors; + @RequiredArgsConstructor @Service public class GroupService { private final GroupRepository groupRepository; + private final UserService userService; public DefaultPostResponse create(GroupCreationRequest groupCreationRequest, User user) { @@ -25,7 +30,9 @@ public DefaultPostResponse create(GroupCreationRequest groupCreationRequest, Use throw new GroupNameAlreadyExistException(groupCreationRequest.getName()); } - Group group = GroupMapper.INSTANCE.map(groupCreationRequest, user); + User owner = userService.findById(user.getId()); + Group group = GroupMapper.INSTANCE.map(groupCreationRequest, owner); + group.addMember(owner); return DefaultPostResponse.builder().id(groupRepository.save(group).getId()).build(); } @@ -42,4 +49,15 @@ public Long update(Long groupId, GroupModificationRequest groupModificationReque } return groupRepository.save(group.update(groupModificationRequest)).getId(); } + + public GroupReadAllResponses readAllByUser(User user) { + User loggedInUser = userService.findById(user.getId()); + return GroupReadAllResponses + .builder() + .groupReadAllResponses( + loggedInUser.getJoinedGroups().stream() + .map(GroupMapper.INSTANCE::map) + .collect(Collectors.toList())) + .build(); + } } diff --git a/src/main/java/com/postsquad/scoup/web/user/domain/User.java b/src/main/java/com/postsquad/scoup/web/user/domain/User.java index 6cb62876..0b8aea78 100644 --- a/src/main/java/com/postsquad/scoup/web/user/domain/User.java +++ b/src/main/java/com/postsquad/scoup/web/user/domain/User.java @@ -2,6 +2,7 @@ import com.postsquad.scoup.web.auth.OAuthType; import com.postsquad.scoup.web.common.BaseEntity; +import com.postsquad.scoup.web.group.domain.Group; import lombok.*; import javax.persistence.*; @@ -36,6 +37,9 @@ public class User extends BaseEntity { @CollectionTable(name = "oauth_user", joinColumns = @JoinColumn(name = "user_id")) private List oAuthUsers = new ArrayList<>(); + @ManyToMany(mappedBy = "members") + private List joinedGroups = new ArrayList<>(); + protected User(String nickname, String username, String email, String avatarUrl, String password, List oAuthUsers) { this.nickname = nickname; this.username = username; diff --git a/src/main/java/com/postsquad/scoup/web/user/service/UserService.java b/src/main/java/com/postsquad/scoup/web/user/service/UserService.java index cd3b4e76..dbacc411 100644 --- a/src/main/java/com/postsquad/scoup/web/user/service/UserService.java +++ b/src/main/java/com/postsquad/scoup/web/user/service/UserService.java @@ -1,6 +1,7 @@ package com.postsquad.scoup.web.user.service; import com.postsquad.scoup.web.common.DefaultPostResponse; +import com.postsquad.scoup.web.signin.exception.UserNotFoundException; import com.postsquad.scoup.web.user.controller.request.EmailValidationRequest; import com.postsquad.scoup.web.user.controller.request.NicknameValidationRequest; import com.postsquad.scoup.web.user.controller.request.SignUpRequest; @@ -51,4 +52,8 @@ public NicknameValidationResponse validateNickname(NicknameValidationRequest nic } return NicknameValidationResponse.valueOf(false); } + + public User findById(Long id) { + return userRepository.findById(id).orElseThrow(() -> new UserNotFoundException(id)); + } } diff --git a/src/test/java/com/postsquad/scoup/web/group/GroupAcceptanceTest.java b/src/test/java/com/postsquad/scoup/web/group/GroupAcceptanceTest.java index 0356e578..45ca7412 100644 --- a/src/test/java/com/postsquad/scoup/web/group/GroupAcceptanceTest.java +++ b/src/test/java/com/postsquad/scoup/web/group/GroupAcceptanceTest.java @@ -8,6 +8,7 @@ import com.postsquad.scoup.web.group.controller.request.GroupModificationRequest; import com.postsquad.scoup.web.group.controller.response.GroupReadOneResponse; import com.postsquad.scoup.web.group.controller.response.GroupValidationResponse; +import com.postsquad.scoup.web.group.controller.response.GroupReadAllResponses; import com.postsquad.scoup.web.group.domain.Group; import com.postsquad.scoup.web.group.provider.*; import io.restassured.RestAssured; @@ -53,6 +54,21 @@ public class GroupAcceptanceTest extends AcceptanceTestBase { .description("이미지") ); + private static final Snippet GROUP_READ_ALL_RESPONSE_FIELDS = responseFields( + fieldWithPath("group_read_all_responses[]") + .type(JsonFieldType.ARRAY) + .description("그룹 목록"), + fieldWithPath("group_read_all_responses[].id") + .type(JsonFieldType.NUMBER) + .description("그룹 id"), + fieldWithPath("group_read_all_responses[].name") + .type(JsonFieldType.STRING) + .description("그룹 이름"), + fieldWithPath("group_read_all_responses[].description") + .type(JsonFieldType.STRING) + .description("그룹 설명") + ); + private static final Snippet GROUP_CREATION_REQUEST_FIELDS = requestFields( fieldWithPathAndConstraints("name", GroupCreationRequest.class) .type(JsonFieldType.STRING) @@ -189,11 +205,12 @@ void createGroup(String description, GroupCreationRequest givenGroupCreationRequ .as("그룹 생성: %s", description) .usingRecursiveComparison() .ignoringFields(ignoringFieldsForResponseWithId) - .ignoringFields("owner") + .ignoringFields("owner", "members") .isEqualTo(expectedGroup); then(actualGroup.getOwner()) .usingRecursiveComparison() .ignoringFields(ignoringFieldsForResponseWithId) + .ignoringFields("joinedGroups") .isEqualTo(expectedGroup.getOwner()); } ); @@ -272,6 +289,7 @@ void validateGroupCreationRequest(String description, GroupCreationRequest given void modifyGroup(String description, Long givenGroupId, GroupModificationRequest givenGroupModificationRequest, Group expectedGroup) { // given Group group = Group.builder().name("name").description("").owner(testUser).schedules(new ArrayList<>()).build(); + group.addMember(testUser); testEntityManager.persist(group); String path = "/groups/{groupId}"; RequestSpecification givenRequest = RestAssured.given(this.spec) @@ -303,7 +321,7 @@ void modifyGroup(String description, Long givenGroupId, GroupModificationRequest .as("그룹 수정: %s", description) .usingRecursiveComparison() .ignoringFields(ignoringFieldsForResponseWithId) - .ignoringFields("owner") + .ignoringFields("owner", "members") .isEqualTo(expectedGroup) ); } @@ -413,4 +431,45 @@ void leaveGroup() { // TODO: id로 조회하여 검증 } + + @ParameterizedTest + @ArgumentsSource(GroupReadAllProvider.class) + @DisplayName("사용자가 가입한 모든 그룹을 조회할 수 있다.") + void readGroup(String description, GroupReadAllResponses expectedGroupReadAllResponses) { + // given + Group group = Group.builder().name("name").description("").owner(testUser).schedules(new ArrayList<>()).build(); + Group group2 = Group.builder().name("group2").description("group2").owner(testUser).schedules(new ArrayList<>()).build(); + group.addMember(testUser); + group2.addMember(testUser); + testEntityManager.persist(group); + testEntityManager.persist(group2); + + String path = "/groups"; + RequestSpecification givenRequest = RestAssured.given(this.spec) + .baseUri(BASE_URL) + .port(port) + .basePath("/api") + .contentType(ContentType.JSON) + .header("Accept-Language", "en-US") + .header("Authorization", TEST_TOKEN); + // when + Response actualResponse = givenRequest.when() + .accept(ContentType.JSON) + .filter(document( + DEFAULT_RESTDOCS_PATH, + GROUP_READ_ALL_RESPONSE_FIELDS + )) + .log().all() + .get(path); + + // then + actualResponse.then() + .log().all() + .statusCode(HttpStatus.OK.value()); + then(actualResponse.as(GroupReadAllResponses.class)) + .as("그룹 조회 : %s", description) + .usingRecursiveComparison() + .ignoringFields("groupReadAllResponses.id") + .isEqualTo(expectedGroupReadAllResponses); + } } diff --git a/src/test/java/com/postsquad/scoup/web/group/provider/GroupReadAllProvider.java b/src/test/java/com/postsquad/scoup/web/group/provider/GroupReadAllProvider.java new file mode 100644 index 00000000..b544cda0 --- /dev/null +++ b/src/test/java/com/postsquad/scoup/web/group/provider/GroupReadAllProvider.java @@ -0,0 +1,29 @@ +package com.postsquad.scoup.web.group.provider; + +import com.postsquad.scoup.web.group.controller.response.GroupReadAllResponse; +import com.postsquad.scoup.web.group.controller.response.GroupReadAllResponses; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; + +import java.util.List; +import java.util.stream.Stream; + +public class GroupReadAllProvider implements ArgumentsProvider { + + @Override + public Stream provideArguments(ExtensionContext context) throws Exception { + return Stream.of( + Arguments.of( + "성공", + GroupReadAllResponses.builder() + .groupReadAllResponses( + List.of( + GroupReadAllResponse.builder().name("name").description("").build(), + GroupReadAllResponse.builder().name("group2").description("group2").build()) + ) + .build() + ) + ); + } +} diff --git a/src/test/java/com/postsquad/scoup/web/user/UserAcceptanceTest.java b/src/test/java/com/postsquad/scoup/web/user/UserAcceptanceTest.java index af05d446..3790d889 100644 --- a/src/test/java/com/postsquad/scoup/web/user/UserAcceptanceTest.java +++ b/src/test/java/com/postsquad/scoup/web/user/UserAcceptanceTest.java @@ -1,6 +1,7 @@ package com.postsquad.scoup.web.user; import com.postsquad.scoup.web.AcceptanceTestBase; +import com.postsquad.scoup.web.TestEntityManager; import com.postsquad.scoup.web.auth.OAuthType; import com.postsquad.scoup.web.common.DefaultPostResponse; import com.postsquad.scoup.web.error.controller.response.ErrorResponse; @@ -111,6 +112,9 @@ class UserAcceptanceTest extends AcceptanceTestBase { @Autowired UserRepository userRepository; + @Autowired + TestEntityManager testEntityManager; + @BeforeEach void setUp() { userRepository.save(User.builder() @@ -173,11 +177,14 @@ private void signUp(String description, SignUpRequest givenSocialSignUpRequest, // then actualResponse.then() .statusCode(HttpStatus.CREATED.value()); - then(userRepository.findById(actualResponse.body().as(DefaultPostResponse.class).getId()).orElse(null)) - .as("회원가입 결과 : %s", description) - .usingRecursiveComparison() - .ignoringFields(ignoringFieldsForResponseWithId) - .isEqualTo(expectedUser); + testEntityManager.findAndConsume(User.class, actualResponse.body().as(DefaultPostResponse.class).getId(), + actualUser -> { + then(actualUser) + .as("회원가입 결과 : %s", description) + .usingRecursiveComparison() + .ignoringFields(ignoringFieldsForResponseWithId) + .isEqualTo(expectedUser); + }); } @ParameterizedTest