Skip to content

Commit

Permalink
[BE] Refactor/#406 토픽 권한을 가진 회원 목록 조회 시 공개 여부를 함께 반환하도록 변경 (#412)
Browse files Browse the repository at this point in the history
* refactor: 사용하지 않는 MemberRepository 메서드 삭제

* refactor: 회원 업데이트 부분 변경으로 시그니처 변경

- 현재 회원 update에서 변경되는 부분만 인자로 남겨둠
- update 시, Member 에서 MemberInfo.getXX을 하는 대신 MemberInfo에서 부분 변경된 객체를 새로 반환하도록 수정

* feat: 회원 정보 수정 API 구현 및 테스트 작성

* test: JwtTokenProviderTest 작성

- 로컬에서 Postman 테스트 시 이 테스트를 사용하면 쉽게 토큰 발급 후 활용 가능

* chore: 로컬 환경용 더미데이터 sql 작성

* chore: 로컬 환경 data.sql을 위한 서브모듈 변경

* docs: 기능 명세서 및 테스트 코드 용어 정리 (유저, 멤버 -> 회원)

* chore: 로그 환경설정 파일 디렉터리 분리

* feat: 닉네임 중복 검증 구현

* refactor: 회원의 이메일 Unique 제약조건 삭제

- 닉네임, OauthId로 회원을 식별할 수 있다.
- 같은 이메일로 네이버, 카카오에 가입한 사람이 소셜 로그인으로 두 계정을 만들 경우, 동일한 이메일이 저장될 수도 있다.

* refactor: 모호한 메서드명 수정

* refactor: Email이 Unique하지 않음에 따라 테스트에서 사용하는 조회 쿼리 변경

- findByEmail 대신 findById
- 기본키가 아닌 유일키로 조회하는 건 테이블 구조 변경 여지가 있으므로 findById 사용

* refactor: 내 정보 수정 API URI 변경

* refactor: 토픽 권한 회원 목록 조회 API를 접근 정보(권한 회원 목록 및 공개 여부) 조회로 명세 변경

- 관련 검토가 필요한 API 설계 및 구현 내용에 대한 TODO 주석 작성

* fix: 디렉터리 분리에 따른 로그 설정 파일 appender 경로 수정

* fix: 디렉터리 분리에 따른 로그 설정 파일 appender 경로 수정

* refactor: 실수로 바꾼 기존 메서드명 원복

* refactor: 불필요한 import문 제거

* refactor: 불필요한 접근제어자, 중복 코드 제거

* docs: Restdocs API 네이밍 반영

* fix: 내 정보 수정 RestDocs 스니펫 누락 추가
  • Loading branch information
yoondgu authored Sep 15, 2023
1 parent 5985872 commit 05c73ef
Show file tree
Hide file tree
Showing 21 changed files with 132 additions and 123 deletions.
4 changes: 4 additions & 0 deletions backend/src/docs/asciidoc/member.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,7 @@ operation::member-controller-test/find-all-topics-in-atlas[snippets='http-reques
=== 회원의 즐겨찾기 조회

operation::member-controller-test/find-all-topics-in-bookmark[snippets='http-request,http-response']

=== 회원의 내 정보 수정

operation::member-controller-test/update-my-info[snippets='http-request,http-response']
4 changes: 2 additions & 2 deletions backend/src/docs/asciidoc/permission.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ operation::permission-controller-test/add-permission[snippets='http-request,http

operation::permission-controller-test/delete-permission[snippets='http-request,http-response']

=== 토픽에 권한을 가진 회원 목록 조회
=== 토픽 접근 정보(권한 회원 목록 및 공개 여부) 조회

operation::permission-controller-test/find-all-topic-permissions[snippets='http-request,http-response']
operation::permission-controller-test/find-topic-access-detail-by-topic-id[snippets='http-request,http-response']

=== 토픽에 권한을 가진 회원 단일 조회

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,16 @@

import com.mapbefine.mapbefine.permission.domain.Permission;
import com.mapbefine.mapbefine.permission.domain.PermissionRepository;
import com.mapbefine.mapbefine.permission.dto.response.PermissionDetailResponse;
import com.mapbefine.mapbefine.permission.dto.response.PermissionResponse;
import com.mapbefine.mapbefine.permission.dto.response.PermissionMemberDetailResponse;
import com.mapbefine.mapbefine.permission.dto.response.PermissionedMemberResponse;
import com.mapbefine.mapbefine.permission.dto.response.TopicAccessDetailResponse;
import com.mapbefine.mapbefine.permission.exception.PermissionException.PermissionNotFoundException;
import com.mapbefine.mapbefine.topic.domain.Publicity;
import com.mapbefine.mapbefine.topic.domain.Topic;
import com.mapbefine.mapbefine.topic.domain.TopicRepository;
import com.mapbefine.mapbefine.topic.domain.TopicStatus;
import com.mapbefine.mapbefine.topic.exception.TopicErrorCode;
import com.mapbefine.mapbefine.topic.exception.TopicException.TopicNotFoundException;
import java.util.List;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand All @@ -16,22 +23,35 @@
public class PermissionQueryService {

private final PermissionRepository permissionRepository;
private final TopicRepository topicRepository;

public PermissionQueryService(PermissionRepository permissionRepository) {
public PermissionQueryService(PermissionRepository permissionRepository, TopicRepository topicRepository) {
this.permissionRepository = permissionRepository;
this.topicRepository = topicRepository;
}

public List<PermissionResponse> findAllTopicPermissions(Long topicId) {
return permissionRepository.findAllByTopicId(topicId)
public TopicAccessDetailResponse findTopicAccessDetailById(Long topicId) {
Publicity publicity = findTopicPublicityById(topicId);
/// TODO: 2023/09/15 이럴거면 topic.getPermissions 로 하는 게 나을 수도 있나? TopicController 에서 하는 게 더 자연스러운 것 같기도..
List<PermissionedMemberResponse> permissionedMembers = permissionRepository.findAllByTopicId(topicId)
.stream()
.map(PermissionResponse::from)
.map(PermissionedMemberResponse::from)
.toList();

return new TopicAccessDetailResponse(publicity, permissionedMembers);
}

private Publicity findTopicPublicityById(Long topicId) {
return topicRepository.findById(topicId)
.map(Topic::getTopicStatus)
.map(TopicStatus::getPublicity)
.orElseThrow(() -> new TopicNotFoundException(TopicErrorCode.TOPIC_NOT_FOUND, topicId));
}

public PermissionDetailResponse findPermissionById(Long permissionId) {
public PermissionMemberDetailResponse findPermissionById(Long permissionId) {
Permission permission = permissionRepository.findById(permissionId)
.orElseThrow(() -> new PermissionNotFoundException(PERMISSION_NOT_FOUND, permissionId));

return PermissionDetailResponse.from(permission);
return PermissionMemberDetailResponse.from(permission);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
@Getter
public class Permission extends BaseTimeEntity {

// TODO 매핑 테이블인데 Id를 가져야 할까?
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
import com.mapbefine.mapbefine.permission.domain.Permission;
import java.time.LocalDateTime;

public record PermissionDetailResponse(
public record PermissionMemberDetailResponse(
Long id,
LocalDateTime updatedAt,
MemberDetailResponse memberDetailResponse
) {

public static PermissionDetailResponse from(Permission permission) {
return new PermissionDetailResponse(
public static PermissionMemberDetailResponse from(Permission permission) {
return new PermissionMemberDetailResponse(
permission.getId(),
permission.getUpdatedAt(),
MemberDetailResponse.from(permission.getMember())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
import com.mapbefine.mapbefine.member.dto.response.MemberResponse;
import com.mapbefine.mapbefine.permission.domain.Permission;

public record PermissionResponse(
public record PermissionedMemberResponse(
Long id,
MemberResponse memberResponse
) {

public static PermissionResponse from(Permission permission) {
return new PermissionResponse(
public static PermissionedMemberResponse from(Permission permission) {
return new PermissionedMemberResponse(
permission.getId(),
MemberResponse.from(permission.getMember())
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.mapbefine.mapbefine.permission.dto.response;

import com.mapbefine.mapbefine.topic.domain.Publicity;
import java.util.List;

public record TopicAccessDetailResponse(
Publicity publicity,
List<PermissionedMemberResponse> permissionedMembers
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@
import com.mapbefine.mapbefine.permission.application.PermissionCommandService;
import com.mapbefine.mapbefine.permission.application.PermissionQueryService;
import com.mapbefine.mapbefine.permission.dto.request.PermissionRequest;
import com.mapbefine.mapbefine.permission.dto.response.PermissionDetailResponse;
import com.mapbefine.mapbefine.permission.dto.response.PermissionResponse;
import java.util.List;
import com.mapbefine.mapbefine.permission.dto.response.PermissionMemberDetailResponse;
import com.mapbefine.mapbefine.permission.dto.response.TopicAccessDetailResponse;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
Expand Down Expand Up @@ -51,16 +50,18 @@ public ResponseEntity<Void> deleteMemberTopicPermission(AuthMember authMember, @

@LoginRequired
@GetMapping("/topics/{topicId}")
public ResponseEntity<List<PermissionResponse>> findAllTopicPermissions(@PathVariable Long topicId) {
List<PermissionResponse> responses = permissionQueryService.findAllTopicPermissions(topicId);
public ResponseEntity<TopicAccessDetailResponse> findTopicAccessDetailByTopicId(@PathVariable Long topicId) {
TopicAccessDetailResponse response = permissionQueryService.findTopicAccessDetailById(topicId);

return ResponseEntity.ok(responses);
return ResponseEntity.ok(response);
}

// TODO 이 API를 쓰는 곳이 있나? + 결국 특정 회원을 조회하는 건데 어떤 API인지 알기 어렵다..
// 회원 정보 조회는 /members 에서 하는 걸로 충분하지 않나? 재사용성이 떨어진다. 테스트의 DisplayName도 매칭이 안된다.
@LoginRequired
@GetMapping("/{permissionId}")
public ResponseEntity<PermissionDetailResponse> findPermissionById(@PathVariable Long permissionId) {
PermissionDetailResponse response = permissionQueryService.findPermissionById(permissionId);
public ResponseEntity<PermissionMemberDetailResponse> findPermissionById(@PathVariable Long permissionId) {
PermissionMemberDetailResponse response = permissionQueryService.findPermissionById(permissionId);

return ResponseEntity.ok(response);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,9 @@ public int countBookmarks() {
return bookmarks.size();
}

public Publicity getPublicity() {
return topicStatus.getPublicity();
}
public void removeImage() {
this.topicInfo = topicInfo.removeImage();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.mapbefine.mapbefine.topic.domain;

import static com.mapbefine.mapbefine.topic.exception.TopicErrorCode.ILLEGAL_PERMISSION_NULL;
import static com.mapbefine.mapbefine.topic.exception.TopicErrorCode.ILLEGAL_PERMISSION_FOR_PUBLICITY_PRIVATE;
import static com.mapbefine.mapbefine.topic.exception.TopicErrorCode.ILLEGAL_PERMISSION_NULL;
import static com.mapbefine.mapbefine.topic.exception.TopicErrorCode.ILLEGAL_PERMISSION_UPDATE;
import static com.mapbefine.mapbefine.topic.exception.TopicErrorCode.ILLEGAL_PUBLICITY_FOR_PERMISSION_ALL_MEMBERS;
import static com.mapbefine.mapbefine.topic.exception.TopicErrorCode.ILLEGAL_PUBLICITY_NULL;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ public static class TopicNotFoundException extends NotFoundException {
public TopicNotFoundException(TopicErrorCode errorCode, List<Long> ids) {
super(new ErrorCode<>(errorCode.getCode(), errorCode.getMessage(), ids));
}

public TopicNotFoundException(TopicErrorCode errorCode, Long id) {
super(new ErrorCode<>(errorCode.getCode(), errorCode.getMessage(), id));
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,15 @@
import com.mapbefine.mapbefine.topic.TopicFixture;
import com.mapbefine.mapbefine.topic.domain.Topic;
import com.mapbefine.mapbefine.topic.domain.TopicRepository;
import io.restassured.RestAssured;
import io.restassured.response.ExtractableResponse;
import io.restassured.response.Response;
import io.restassured.*;
import io.restassured.response.*;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;

public class AtlasIntegrationTest extends IntegrationTest {
class AtlasIntegrationTest extends IntegrationTest {

@Autowired
TopicRepository topicRepository;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.mapbefine.mapbefine.bookmark;

import static io.restassured.RestAssured.given;
import static io.restassured.RestAssured.*;
import static org.apache.http.HttpHeaders.AUTHORIZATION;
import static org.assertj.core.api.Assertions.assertThat;

Expand All @@ -14,14 +14,13 @@
import com.mapbefine.mapbefine.topic.TopicFixture;
import com.mapbefine.mapbefine.topic.domain.Topic;
import com.mapbefine.mapbefine.topic.domain.TopicRepository;
import io.restassured.response.ExtractableResponse;
import io.restassured.response.Response;
import io.restassured.response.*;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;

public class BookmarkIntegrationTest extends IntegrationTest {
class BookmarkIntegrationTest extends IntegrationTest {

@Autowired
private MemberRepository memberRepository;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.mapbefine.mapbefine.common;

import com.mapbefine.mapbefine.DatabaseCleanup;
import io.restassured.RestAssured;
import io.restassured.*;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.springframework.beans.factory.annotation.Autowired;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.mapbefine.mapbefine.permission;

import static com.mapbefine.mapbefine.oauth.domain.OauthServerType.KAKAO;
import static io.restassured.RestAssured.given;
import static io.restassured.RestAssured.*;
import static org.apache.http.HttpHeaders.AUTHORIZATION;
import static org.assertj.core.api.Assertions.assertThat;

Expand All @@ -16,14 +16,14 @@
import com.mapbefine.mapbefine.permission.domain.Permission;
import com.mapbefine.mapbefine.permission.domain.PermissionRepository;
import com.mapbefine.mapbefine.permission.dto.request.PermissionRequest;
import com.mapbefine.mapbefine.permission.dto.response.PermissionDetailResponse;
import com.mapbefine.mapbefine.permission.dto.response.PermissionResponse;
import com.mapbefine.mapbefine.permission.dto.response.PermissionMemberDetailResponse;
import com.mapbefine.mapbefine.permission.dto.response.PermissionedMemberResponse;
import com.mapbefine.mapbefine.permission.dto.response.TopicAccessDetailResponse;
import com.mapbefine.mapbefine.topic.TopicFixture;
import com.mapbefine.mapbefine.topic.domain.Topic;
import com.mapbefine.mapbefine.topic.domain.TopicRepository;
import io.restassured.common.mapper.TypeRef;
import io.restassured.response.ExtractableResponse;
import io.restassured.response.Response;
import io.restassured.common.mapper.*;
import io.restassured.response.*;
import java.time.LocalDateTime;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
Expand All @@ -33,7 +33,7 @@
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;

public class PermissionIntegrationTest extends IntegrationTest {
class PermissionIntegrationTest extends IntegrationTest {

@Autowired
private MemberRepository memberRepository;
Expand Down Expand Up @@ -79,7 +79,7 @@ public void setUp() {
}

@Test
@DisplayName("Topic 을 만든자가 특정 회원에게 권한을 준다.")
@DisplayName("Topic 을 만든 회원이 특정 회원에게 해당 Topic 에 대한 권한을 준다.")
void addPermission() {
// given
Topic topic = topicRepository.save(TopicFixture.createByName("topicName", creator));
Expand All @@ -100,7 +100,7 @@ void addPermission() {
}

@Test
@DisplayName("Topic 을 만든자가 특정 회원에게 권한을 삭제한다.")
@DisplayName("Topic 을 만든 회원이 특정 회원이 가진 해당 Topic 에 대한 권한을 삭제한다.")
void deletePermission() {
// given
Topic topic = topicRepository.save(TopicFixture.createByName("topicName", creator));
Expand All @@ -121,8 +121,8 @@ void deletePermission() {
}

@Test
@DisplayName("Topic 에 권한을 가진 자들을 모두 조회한다.")
void findMemberTopicPermissionAll() {
@DisplayName("Topic 의 접근 정보(권한 회원 목록 및 공개 여부)를 조회한다.")
void findTopicAccessDetailById() {
// given
Topic topic = topicRepository.save(TopicFixture.createByName("topicName", creator));
Permission permission1 =
Expand All @@ -138,22 +138,22 @@ void findMemberTopicPermissionAll() {
.when().get("/permissions/topics/" + topic.getId())
.then().log().all()
.extract();
TopicAccessDetailResponse actual = response.as(new TypeRef<>() {});

// then
List<PermissionResponse> permissionResponse = response.as(new TypeRef<>() {
});
assertThat(response.statusCode())
.isEqualTo(HttpStatus.OK.value());
assertThat(permissionResponse)
assertThat(actual.publicity()).isEqualTo(topic.getPublicity());
assertThat(actual.permissionedMembers())
.hasSize(2)
.extracting(PermissionResponse::memberResponse)
.extracting(PermissionedMemberResponse::memberResponse)
.usingRecursiveComparison()
.isEqualTo(List.of(MemberResponse.from(user1), MemberResponse.from(user2)));
}

@Test
@DisplayName("Topic 에 권한을 가진 자를 조회한다.")
void findMemberTopicPermissionById() {
void findPermissionById() {
// given
Topic topic = topicRepository.save(TopicFixture.createByName("topicName", creator));
Permission permission =
Expand All @@ -166,14 +166,13 @@ void findMemberTopicPermissionById() {
.when().get("/permissions/" + permission.getId())
.then().log().all()
.extract();
PermissionMemberDetailResponse actual = response.as(new TypeRef<>() {});

// then
PermissionDetailResponse permissionDetailResponse = response.as(
PermissionDetailResponse.class);
assertThat(response.statusCode())
.isEqualTo(HttpStatus.OK.value());
assertThat(permissionDetailResponse)
.extracting(PermissionDetailResponse::memberDetailResponse)
assertThat(actual)
.extracting(PermissionMemberDetailResponse::memberDetailResponse)
.usingRecursiveComparison()
.ignoringFieldsOfTypes(LocalDateTime.class)
.isEqualTo(MemberDetailResponse.from(user1));
Expand Down
Loading

0 comments on commit 05c73ef

Please sign in to comment.