Skip to content

Commit

Permalink
[디디] API 테스트/문서자동화 미션 제출합니다. (#26)
Browse files Browse the repository at this point in the history
* feat: 실습1, 2 구현

- 문서화 실습 구현
- 인증 실습 구현

* refactor: MemberResponse 기본 생성자 추가

* feat: MethodArgumentResolver에서 password와 password-check가 일치하는 지 검증

* feat: interceptor 와 methodArgumentResolver 에서 발생하는 예외 관리

- 아직 미완성..

* feat: annotation 기반 method argument validate 추가

- 패스워드 일치
- 이메일 중복

* feat: create 예외에 대한 문서화 구현

* feat: get에 대한 서비스, 컨트롤러 테스트와 문서화 구현

* feat: update에 대한 서비스, 컨트롤러 테스트와 문서화 구현

* feat: delete에 대한 서비스, 컨트롤러 테스트와 문서화 구현

* feat: 회원 가입 프론트 연동 구현

* feat: 로그인, 회원 정보 보기 기능 구현

* feat: 업데이트 구현

* feat: 탈퇴 구현

* refactor, feat: 테스트 정상화 및, 문서화 구현

- 전체 테스트 정상화
- 문서화 추가 양식 구현 (필수값 여부, 입력값 foramt)
- 예외 사항 추가

* feat: IsAuth 어노테이션 추가 및 테스트와 문서 수정

* feat: 컨트롤러 테스트 추가

- 토큰이 없는 경우 정보조회 테스트 추가

* feat: 즐겨찾기 서비스 테스트 추가

* feat: 즐겨찾기 관련 도메인 테스트 추가

* feat: 즐겨찾기 관련 도메인 테스트 추가

- 즐겨찾기 삭제 테스트 및 구현

* feat: 즐겨 찾기 프론트 연동 구현

* refactor: 테스트 정상화

* refactor: 패키지 수정

- 패키지 구조 수정

* refactor: 예외 관계 수정

- GlobalExceptionHandler 수정
- exception 상속 관계 수정

* refactor: 테스트 정상화

- 인터셉터 path 추가
- exception handler 수정

* docs: README.md 수정

* feat: Member 관련 인수테스트 추가

- 예외 사항에 대한 인수테스트 추가

* feat: Member 관련 api테스트 추가

- 예외사항에 대한 문서화 완료

* refactor: documentation 메서드 중복 제거

* refactor: favorites 예외 테스트 추가

-문서화 구현 완료

* refactor: 리뷰 반영 1

- Auth enum 제거
- Interceptor 전체 url 적용
- Favorite equals, hashcode 재정의 삭제
- logger 적용
- transaction 적용
- Favorite을 aggregate root로 수정
  • Loading branch information
fucct authored Jun 2, 2020
1 parent 63a6117 commit 40c254d
Show file tree
Hide file tree
Showing 108 changed files with 3,798 additions and 961 deletions.
71 changes: 71 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# 지하철 3단계 - 회원 관리, 즐겨찾기

## 요구 사항

- 회원 정보를 관리하는 기능 구현
- 자신의 정보만 수정 가능하도록 해야하며 **로그인이 선행**되어야 함
- 토큰의 유효성 검사와 본인 여부를 판단하는 로직 추가
- side case에 대한 예외처리
- 인수 테스트와 단위 테스트 작성
- API 문서를 작성하고 문서화를 위한 테스트 작성
- 페이지 연동
- 즐겨찾기 기능을 추가(추가,삭제,조회)
- 자신의 정보만 수정 가능하도록 해야하며 **로그인이 선행**되어야 함
- 토큰의 유효성 검사와 본인 여부를 판단하는 로직 추가(interceptor, argument resolver)
- side case에 대한 예외처리 필수
- 인수 테스트와 단위 테스트 작성
- API 문서를 작성하고 문서화를 위한 테스트 작성
- 페이지 연동

### 기능 목록

회원 정보 관리

- [x] 회원 가입
- [x] 로그인
- [x] 로그인 후 회원 정보 조회/수정/삭제
- [x] 가입 요청에 대한 validation
- [x] 로그인한 회원 정보를 토큰으로 보유
- [x] 회원 정보 토큰을 만료 시간이 존재하는 쿠키내에 저장
- [x] 회원 정보를 요구하는 요청에 대해 토큰을 통해 인증
- [x] 회원과 관련된 요청에 대한 테스트와 자동 문서화 기능

즐겨찾기 관리

- [x] 즐겨찾기 추가
- [x] 즐겨찾기 조회/ 제거
- [x] 즐겨찾기 추가/삭제 시 로그인 검증
- [x] 즐겨찾기 추가 시 중복 검증
- [x] 즐겨찾기 추가 시 역 이름 검증

### 예외 사항

- Member 예외사항
- 회원가입
- 비밀번호 확인 불일치
- Blank
- 이메일 중복
- 로그인
- 비밀번호 불일치
- 존재하지 않는 이메일
- Blank
- 정보조회
- 토큰 없음
- 정보수정
- 토큰 없음
- 비밀번호 불일치
- 비밀번호 확인 불일치
- Blank
- 이메일 중복
- 정보삭제
- 토큰 없음

- Favorite 예외사항
- 즐겨찾기 추가
- 토큰 없음
- 즐겨찾기 중복
- 부적절한 역 이름
- 즐겨찾기 목록 조회
- 토큰 없음
- 즐겨찾기 제거
- 토큰 없음
52 changes: 36 additions & 16 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,31 +1,51 @@
plugins {
id 'org.springframework.boot' version '2.2.5.RELEASE'
id 'io.spring.dependency-management' version '1.0.9.RELEASE'
id 'java'
id 'org.springframework.boot' version '2.2.5.RELEASE'
id 'io.spring.dependency-management' version '1.0.9.RELEASE'
id "org.asciidoctor.convert" version "1.5.9.2"
id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
mavenCentral()
mavenCentral()
}

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
implementation 'net.rakugakibox.spring.boot:logback-access-spring-boot-starter:2.7.1'
implementation 'pl.allegro.tech.boot:handlebars-spring-boot-starter:0.3.0'
implementation 'org.jgrapht:jgrapht-core:1.0.1'
implementation 'io.jsonwebtoken:jjwt:0.9.1'
testImplementation 'io.rest-assured:rest-assured:3.3.0'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
runtimeOnly 'com.h2database:h2'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
implementation 'net.rakugakibox.spring.boot:logback-access-spring-boot-starter:2.7.1'
implementation 'pl.allegro.tech.boot:handlebars-spring-boot-starter:0.3.0'
implementation 'org.jgrapht:jgrapht-core:1.0.1'
implementation 'io.jsonwebtoken:jjwt:0.9.1'
testImplementation 'io.rest-assured:rest-assured:3.3.0'
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc:2.0.4.RELEASE'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
runtimeOnly 'com.h2database:h2'
asciidoctor 'org.springframework.restdocs:spring-restdocs-asciidoctor:2.0.4.RELEASE'
}

ext {
snippetsDir = file('build/generated-snippets')
}

test {
useJUnitPlatform()
useJUnitPlatform()
outputs.dir snippetsDir
}

asciidoctor {
inputs.dir snippetsDir
dependsOn test
}

bootJar {
dependsOn asciidoctor
from("${asciidoctor.outputDir}/html5") {
into 'static/docs'
}
}
121 changes: 119 additions & 2 deletions src/docs/asciidoc/api-guide.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ endif::[]
:icons: font
:source-highlighter: highlightjs
:toc: left
:toclevels: 2
:toclevels: 4
:sectlinks:
:operation-http-request-title: Example Request
:operation-http-response-title: Example Response
Expand All @@ -19,4 +19,121 @@ endif::[]
[[resources-members-create]]
=== 회원 가입

operation::members/create[snippets='http-request,http-response']
[[resources-members-create-success]]
===== 성공
operation::members/create[snippets='http-request,http-response,request-fields']


[[resources-members-create-fail]]
==== 실패

[[resources-members-create-fail-duplicated-email]]
===== 이메일 중복
operation::members/duplicate-create[snippets='http-request,http-response,request-fields']


[[resources-members-create-fail-not-match-password]]
===== 패스워드 불일치
operation::members/not-match-password-create[snippets='http-request,http-response,request-fields']

[[resources-members-login]]
=== 로그인

[[resources-members-login-successful]]
===== 성공
operation::members/login[snippets='http-request,http-response,request-fields']

[[resources-members-login-fail]]
==== 실패

[[resources-members-login-fail-not-exist-email]]
===== 존재하지 않는 이메일
operation::members/login-with-not-exist-email[snippets='http-request,http-response,request-fields']

[[resources-members-login-fail-not-match-password]]
===== 비밀번호 불일치
operation::members/login-with-wrong-password[snippets='http-request,http-response,request-fields']

[[resources-members-get]]
=== 회원 정보 조회

[[resources-members-get-successful]]
===== 성공
operation::members/get[snippets='http-request,http-response']

[[resources-members-get-fail]]
==== 실패

[[resources-members-get-fail-not-login]]
===== 토큰이 존재하지 않음
operation::members/not-exist-get[snippets='http-request,http-response']


[[resources-members-update]]
=== 회원 정보 수정

[[resources-members-update-successful]]
===== 성공
operation::members/update[snippets='http-request,http-response,request-fields']

[[resources-members-update-fail]]
==== 실패

[[resources-members-update-fail-not-login]]
===== 토큰이 존재하지 않음
operation::members/not-exist-token-update[snippets='http-request,http-response,request-fields,response-fields']

[[resources-members-update-fail-not-match-password]]
===== 비밀번호가 올바르지 않
operation::members/not-match-password-update[snippets='http-request,http-response,request-fields,response-fields']

[[resources-members-delete]]
=== 회원 탈퇴

[[resources-members-delete-successful]]
===== 성공
operation::members/delete[snippets='http-request,http-response']

[[resources-members-delete-fail]]
==== 실패

[[resources-members-delete-not-login]]
===== 토큰이 존재하지 않음
operation::members/delete-not-exist-token[snippets='http-request,http-response,response-fields']

[[resources-favorites]]
== 즐겨찾기

[[resources-favorites-create]]
=== 즐겨찾기 추가

[[resources-favorites-create-successful]]
===== 성공
operation::favorites/create[snippets='http-request,http-response,request-fields']

[[resources-favorites-create-fail]]
==== 실패

[[resources-favorites-create-fail-no-login]]
===== 토큰이 존재하지 않음
operation::favorites/create-no-login[snippets='http-request,http-response,request-fields,response-fields']

===== 즐겨찾기 중복
operation::favorites/create-duplicated[snippets='http-request,http-response,request-fields,response-fields']

[[resources-favorites-delete]]
=== 즐겨찾기 삭제

[[resources-favorites-delete-successful]]
===== 성공
operation::favorites/delete[snippets='http-request,http-response']

[[resources-favorites-delete-fail]]
==== 실패

[[resources-favorites-delete-fail-not-login]]
===== 토큰이 존재하지 않음
operation::favorites/delete-not-login[snippets='http-request,http-response, response-fields']



2 changes: 0 additions & 2 deletions src/main/java/wooteco/subway/SubwayAdminApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,11 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jdbc.repository.config.EnableJdbcAuditing;

@SpringBootApplication
public class SubwayAdminApplication {

public static void main(String[] args) {
SpringApplication.run(SubwayAdminApplication.class, args);
}

}
2 changes: 1 addition & 1 deletion src/main/java/wooteco/subway/config/ETagHeaderFilter.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
public class ETagHeaderFilter {
@Bean
public FilterRegistrationBean<ShallowEtagHeaderFilter> shallowEtagHeaderFilter() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean<>(new ShallowEtagHeaderFilter());
FilterRegistrationBean<ShallowEtagHeaderFilter> filterRegistrationBean = new FilterRegistrationBean<>(new ShallowEtagHeaderFilter());
filterRegistrationBean.addUrlPatterns("/lines/detail");
filterRegistrationBean.setName("etagFilter");
return filterRegistrationBean;
Expand Down
25 changes: 9 additions & 16 deletions src/main/java/wooteco/subway/config/WebMvcConfig.java
Original file line number Diff line number Diff line change
@@ -1,37 +1,30 @@
package wooteco.subway.config;

import java.util.List;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import wooteco.subway.web.member.LoginMemberMethodArgumentResolver;
import wooteco.subway.web.member.interceptor.BasicAuthInterceptor;
import wooteco.subway.web.member.interceptor.BearerAuthInterceptor;
import wooteco.subway.web.member.interceptor.SessionInterceptor;

import java.util.List;
import wooteco.subway.web.prehandler.BearerAuthInterceptor;
import wooteco.subway.web.prehandler.LoginMemberMethodArgumentResolver;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
private final BasicAuthInterceptor basicAuthInterceptor;
private final SessionInterceptor sessionInterceptor;
private final BearerAuthInterceptor bearerAuthInterceptor;
private final LoginMemberMethodArgumentResolver loginMemberArgumentResolver;

public WebMvcConfig(BasicAuthInterceptor basicAuthInterceptor,
SessionInterceptor sessionInterceptor,
BearerAuthInterceptor bearerAuthInterceptor,
LoginMemberMethodArgumentResolver loginMemberArgumentResolver) {
this.basicAuthInterceptor = basicAuthInterceptor;
this.sessionInterceptor = sessionInterceptor;
public WebMvcConfig(
BearerAuthInterceptor bearerAuthInterceptor,
LoginMemberMethodArgumentResolver loginMemberArgumentResolver) {
this.bearerAuthInterceptor = bearerAuthInterceptor;
this.loginMemberArgumentResolver = loginMemberArgumentResolver;
}

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(basicAuthInterceptor).addPathPatterns("/me/basic");
registry.addInterceptor(sessionInterceptor).addPathPatterns("/me/session");
registry.addInterceptor(bearerAuthInterceptor).addPathPatterns("/me/bearer");
registry.addInterceptor(bearerAuthInterceptor).
addPathPatterns("/**");
}

@Override
Expand Down
51 changes: 51 additions & 0 deletions src/main/java/wooteco/subway/domain/favorite/Favorite.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package wooteco.subway.domain.favorite;

import java.util.Objects;

import org.springframework.data.annotation.Id;

public class Favorite {
@Id
private Long id;
private Long memberId;
private Long sourceId;
private Long targetId;

protected Favorite() {
}

public Favorite(Long id, Long memberId, Long sourceId, Long targetId) {
this.id = id;
this.memberId = memberId;
this.sourceId = sourceId;
this.targetId = targetId;
}

public static Favorite of(Long id, Long memberId, Long sourceId, Long targetId) {
return new Favorite(id, memberId, sourceId, targetId);
}

public static Favorite of(Long memberId, Long sourceId, Long targetId) {
return new Favorite(null, memberId, sourceId, targetId);
}

public Long getId() {
return id;
}

public Long getMemberId() {
return memberId;
}

public Long getSourceId() {
return sourceId;
}

public Long getTargetId() {
return targetId;
}

public boolean isSameId(Long id) {
return Objects.equals(this.id, id);
}
}
Loading

0 comments on commit 40c254d

Please sign in to comment.