Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[디디] API 테스트/문서자동화 미션 제출합니다. #26

Merged
merged 30 commits into from
Jun 2, 2020
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
dec8c13
feat: 실습1, 2 구현
fucct May 19, 2020
da9fb94
refactor: MemberResponse 기본 생성자 추가
fucct May 20, 2020
06c1b25
feat: MethodArgumentResolver에서 password와 password-check가 일치하는 지 검증
fucct May 20, 2020
470f786
feat: interceptor 와 methodArgumentResolver 에서 발생하는 예외 관리
fucct May 20, 2020
d4d249b
feat: annotation 기반 method argument validate 추가
fucct May 21, 2020
d274244
feat: create 예외에 대한 문서화 구현
fucct May 21, 2020
41a0745
feat: get에 대한 서비스, 컨트롤러 테스트와 문서화 구현
fucct May 21, 2020
e48e16c
feat: update에 대한 서비스, 컨트롤러 테스트와 문서화 구현
fucct May 21, 2020
0d08e62
feat: delete에 대한 서비스, 컨트롤러 테스트와 문서화 구현
fucct May 21, 2020
4d6dff8
feat: 회원 가입 프론트 연동 구현
fucct May 21, 2020
4271621
feat: 로그인, 회원 정보 보기 기능 구현
fucct May 22, 2020
d9f02c6
feat: 업데이트 구현
fucct May 22, 2020
9f4c163
feat: 탈퇴 구현
fucct May 22, 2020
6cf876b
refactor, feat: 테스트 정상화 및, 문서화 구현
fucct May 25, 2020
4747fc6
feat: IsAuth 어노테이션 추가 및 테스트와 문서 수정
fucct May 25, 2020
6f81c9c
feat: 컨트롤러 테스트 추가
fucct May 25, 2020
bac2512
feat: 즐겨찾기 서비스 테스트 추가
fucct May 25, 2020
7d1af94
feat: 즐겨찾기 관련 도메인 테스트 추가
fucct May 26, 2020
f9b8ca0
feat: 즐겨찾기 관련 도메인 테스트 추가
fucct May 26, 2020
fa01d6a
feat: 즐겨 찾기 프론트 연동 구현
fucct May 26, 2020
9809099
refactor: 테스트 정상화
fucct May 26, 2020
0d6365a
refactor: 패키지 수정
fucct May 26, 2020
d6a5246
refactor: 예외 관계 수정
fucct May 26, 2020
b2871ab
refactor: 테스트 정상화
fucct May 26, 2020
fb7ca72
docs: README.md 수정
fucct May 26, 2020
df0279d
feat: Member 관련 인수테스트 추가
fucct May 27, 2020
119b0dc
feat: Member 관련 api테스트 추가
fucct May 27, 2020
a7c239e
refactor: documentation 메서드 중복 제거
fucct May 27, 2020
e6a7b26
refactor: favorites 예외 테스트 추가
fucct May 27, 2020
e8ae43d
refactor: 리뷰 반영 1
fucct Jun 1, 2020
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
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
26 changes: 10 additions & 16 deletions src/main/java/wooteco/subway/config/WebMvcConfig.java
Original file line number Diff line number Diff line change
@@ -1,37 +1,31 @@
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("/me", "/me/detail", "/me/favorites", "/members", "/members/*",
"/favorites", "/favorites/*");
fucct marked this conversation as resolved.
Show resolved Hide resolved
}

@Override
Expand Down
Loading