Skip to content

Commit

Permalink
Merge branch 'BE/dev' into koust6u-patch-1
Browse files Browse the repository at this point in the history
  • Loading branch information
koust6u authored Oct 14, 2024
2 parents ac5e6b3 + fcc5e35 commit 9e31582
Show file tree
Hide file tree
Showing 8 changed files with 156 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
import io.swagger.v3.oas.models.servers.Server;

@Configuration
public class SwaggerConfig {
Expand All @@ -18,6 +19,7 @@ public OpenAPI openAPI() {
return new OpenAPI()
.info(apiInfo())
.components(securitySchemeComponents())
.addServersItem(serverItem())
.addSecurityItem(securityRequirement());
}

Expand All @@ -39,6 +41,12 @@ private Components securitySchemeComponents() {
return new Components().addSecuritySchemes(HttpHeaders.AUTHORIZATION, bearerAuth);
}

private Server serverItem() {
return new Server()
.url("https://coduo.site")
.description("클라우드에 배포된 서버");
}

private SecurityRequirement securityRequirement() {
return new SecurityRequirement().addList(HttpHeaders.AUTHORIZATION);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

Expand All @@ -19,11 +20,17 @@ public class MemberController implements MemberControllerDocs {
private final MemberService memberService;

@GetMapping("/member")
public ResponseEntity<MemberReadResponse> getMember(
@CookieValue(SIGN_IN_COOKIE_NAME) final String token
) {
public ResponseEntity<MemberReadResponse> getMember(@CookieValue(SIGN_IN_COOKIE_NAME) final String token) {
final MemberReadResponse response = memberService.findMemberNameByCredential(token);

return ResponseEntity.ok(response);
}

@DeleteMapping("/member")
public ResponseEntity<Void> deleteMember(@CookieValue(SIGN_IN_COOKIE_NAME) final String token) {
memberService.deleteMember(token);

return ResponseEntity.noContent()
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,14 @@ ResponseEntity<MemberReadResponse> getMember(
schema = @Schema(type = "string")
)
String token);

@ApiResponse(responseCode = "204", description = "회원 정보를 삭제한다.",
content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))
ResponseEntity<Void> deleteMember(
@Parameter(
in = ParameterIn.COOKIE,
name = "coduo_whoami",
description = "사용자가 인증에 성공하면 서버에서 발급하는 쿠키",
schema = @Schema(type = "string")
) String token);
}
19 changes: 18 additions & 1 deletion backend/src/main/java/site/coduo/member/domain/Member.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package site.coduo.member.domain;

import java.time.LocalDateTime;
import java.util.Objects;

import jakarta.persistence.Column;
Expand All @@ -9,6 +10,8 @@
import jakarta.persistence.Id;
import jakarta.persistence.Table;

import org.springframework.format.annotation.DateTimeFormat;

import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
Expand Down Expand Up @@ -41,14 +44,19 @@ public class Member extends BaseTimeEntity {
@Column(name = "USER_NAME")
private String username;

@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
@Column(name = "DELETED_AT")
private LocalDateTime deletedAt;

@Builder
private Member(final String accessToken, final String loginId, final String userId, final String profileImage,
final String username) {
final String username, final LocalDateTime deletedAt) {
this.accessToken = accessToken;
this.loginId = loginId;
this.userId = userId;
this.profileImage = profileImage;
this.username = username;
this.deletedAt = deletedAt;
}

public void update(final Member other) {
Expand All @@ -57,6 +65,15 @@ public void update(final Member other) {
this.userId = other.userId;
this.profileImage = other.profileImage;
this.username = other.username;
this.deletedAt = other.deletedAt;
}

public void delete() {
this.deletedAt = LocalDateTime.now();
}

public boolean isDeleted() {
return deletedAt != null;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,33 @@
package site.coduo.member.domain.repository;

import java.util.List;
import java.util.Optional;

import org.springframework.data.jpa.repository.JpaRepository;

import site.coduo.member.domain.Member;
import site.coduo.member.exception.MemberNotFoundException;

public interface MemberRepository extends JpaRepository<Member, Long> {

Optional<Member> findByUserId(String userId);

List<Member> findByDeletedAtIsNull();

@Override
default List<Member> findAll() {
return findByDeletedAtIsNull();
}

default Member fetchByUserId(final String userId) {
final Member member = findByUserId(userId)
.orElseThrow(() -> new MemberNotFoundException(String.format("%s는 찾을 수 없는 회원 아이디입니다.", userId)));
if (member.isDeleted()) {
throw new MemberNotFoundException(String.format("%s는 삭제된 회원입니다.", userId));
}
return member;
}

boolean existsByUserId(String userId);

}
16 changes: 11 additions & 5 deletions backend/src/main/java/site/coduo/member/service/MemberService.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import site.coduo.member.client.dto.GithubUserResponse;
import site.coduo.member.domain.Member;
import site.coduo.member.domain.repository.MemberRepository;
import site.coduo.member.exception.MemberNotFoundException;
import site.coduo.member.infrastructure.http.Bearer;
import site.coduo.member.infrastructure.security.JwtProvider;
import site.coduo.member.service.dto.member.MemberReadResponse;
Expand All @@ -36,15 +35,22 @@ public void createMember(final String username, final String encryptedAccessToke

public MemberReadResponse findMemberNameByCredential(final String token) {
final String userId = jwtProvider.extractSubject(token);
final Member member = memberRepository.findByUserId(userId)
.orElseThrow(() -> new MemberNotFoundException(String.format("%s는 찾을 수 없는 회원 아이디입니다.", userId)));
final Member member = memberRepository.fetchByUserId(userId);

return new MemberReadResponse(member.getUsername());
}

public Member findMemberByCredential(final String token) {
final String userId = jwtProvider.extractSubject(token);
return memberRepository.findByUserId(userId)
.orElseThrow(() -> new MemberNotFoundException(String.format("%s는 찾을 수 없는 회원 아이디입니다.", userId)));

return memberRepository.fetchByUserId(userId);
}

@Transactional
public void deleteMember(final String token) {
final String userId = jwtProvider.extractSubject(token);
final Member member = memberRepository.fetchByUserId(userId);

member.delete();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,19 +50,67 @@ void search_member_info() {
.body("username", is(member.getUsername()));
}

String login(Member member) {
final String sessionId = GithubAcceptanceTest.createAccessTokenCookie();
@Test
@DisplayName("회원을 삭제한다.")
void delete_member() {
//given
final Member member = Member.builder()
.userId("123")
.accessToken("access")
.loginId("login")
.username("username")
.profileImage("some image")
.build();

final String loginToken = jwtProvider.sign(member.getUserId());
memberRepository.save(member);

return RestAssured
//when && then
RestAssured
.given()
.cookie("JSESSIONID", sessionId)
.cookie(SIGN_IN_COOKIE_NAME, loginToken)

.when()
.get("/api/sign-in/callback")
.delete("/api/member")

.thenReturn()
.cookie("coudo_whoami");
.then()
.statusCode(HttpStatus.SC_NO_CONTENT);
}

@Test
@DisplayName("존재하지 않는 회원을 삭제한다.")
void delete_not_member() {
//given
final Member member = Member.builder()
.userId("123")
.accessToken("access")
.loginId("login")
.username("username")
.profileImage("some image")
.build();

final String loginToken = jwtProvider.sign(member.getUserId());
memberRepository.save(member);

//when && then
RestAssured
.given()
.cookie(SIGN_IN_COOKIE_NAME, loginToken)

.when()
.delete("/api/member")

.then()
.statusCode(HttpStatus.SC_NO_CONTENT);

RestAssured
.given()
.cookie(SIGN_IN_COOKIE_NAME, loginToken)

.when()
.delete("/api/member")

.then()
.statusCode(HttpStatus.SC_NOT_FOUND);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import static org.assertj.core.api.Assertions.assertThat;

import java.util.List;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -89,4 +91,28 @@ void search_member_by_login_token() {
// then
assertThat(findMember.getUsername()).isEqualTo(member.getUsername());
}

@Test
@DisplayName("회원을 삭제한다.")
void delete_member() {
// given
final Member member = Member.builder()
.userId("userid")
.accessToken("access")
.loginId("login")
.username("username")
.profileImage("some image")
.build();
final String token = jwtProvider.sign(member.getUserId());

memberRepository.save(member);
final List<Member> beforeDelete = memberRepository.findAll();

// when
memberService.deleteMember(token);

//then
final List<Member> afterDelete = memberRepository.findAll();
assertThat(afterDelete).hasSize(beforeDelete.size() - 1);
}
}

0 comments on commit 9e31582

Please sign in to comment.