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 테스트/문서자동화 미션 제출합니다. #11

Merged
merged 24 commits into from
Jun 11, 2020
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
440df6a
feat : 문서 자동화 실습 진행
kimhodol May 19, 2020
6e6fa42
feat : 사용자 인증 실습 진행
kimhodol May 19, 2020
4faf30a
docs : 요구사항 작성
kimhodol May 20, 2020
aa398cc
feat : 회원 정보 관리 인수테스트 작성
kimhodol May 20, 2020
c8b0702
feat : 회원 CRUD 단위 테스트 작성
kimhodol May 21, 2020
763cce5
refactor : RestAssured 테스트 픽스쳐들을 MockMvc 픽스처들로 대체
kimhodol May 21, 2020
0f70c36
feat : 회원관리 컨트롤러 테스트 작성 및 문서 자동화
kimhodol May 21, 2020
9a835e1
refactor : MockMvc 테스트 픽스쳐 리팩토링
kimhodol May 21, 2020
ab2e5bf
etc : 프론트엔드 샘플 코드 추가 및 1단계 요구사항 반영
kimhodol May 22, 2020
cb060f4
feat : Favorite CRD 인수, 단위 테스트 및 구현
kimhodol May 25, 2020
58408b0
feat : Favorite 컨트롤러 테스트 작성 및 문서화
kimhodol May 26, 2020
0fb282c
feat : Favorite 페이지 연동
kimhodol May 26, 2020
f68fe7c
refactor : 컨벤션에 맞게 프로젝트 스코프 리팩토링
kimhodol May 26, 2020
8ad5302
refactor : 컨트롤러에서의 사용자 검증을 LoginMemberMethodArgumentResolver, LoginMe…
kimhodol May 26, 2020
fbadee8
refactor : GlobalExceptionHandler 및 커스텀 예외처리 추가
kimhodol May 26, 2020
0744b13
test : 일부 AcceptanceTest 다이나믹 테스트 적용 및 Fixture 리팩토링
kimhodol May 26, 2020
8b40959
refactor: MemberDto에 기본 생성자 추가
begaonnuri May 28, 2020
2e8a869
test : 토큰 생성 실패 테스트 추가
begaonnuri May 28, 2020
2364bf1
test : LoginMemberControllerTest 추가
begaonnuri May 28, 2020
22182de
refactor : 전반적인 리팩토링
begaonnuri May 30, 2020
8e09e33
refactor: MemberService에서 FavoriteService를 분리
begaonnuri Jun 9, 2020
1c53087
refactor: Favorite을 생성할 때 데이터베이스 접근을 1회로 변경
begaonnuri Jun 9, 2020
6abb5e6
refactor: 하드코딩 값을 상수로 변경
begaonnuri Jun 9, 2020
15b2556
refactor: default Exception Handler 추가
begaonnuri Jun 9, 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
39 changes: 39 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# atdd-subway-favorite

## 1단계 - 회원관리 기능

### 회원정보 수정 기능

### 요구 사항

- [x] 회원 정보를 관리하는 기능 구현
- [x] 자신의 정보만 수정 가능하도록 해야하며 로그인이 선행되어야 함
- [x] 토큰의 유효성 검사와 본인 여부를 판단하는 로직 추가
- [x] side case에 대한 예외처리
- [x] 인수 테스트와 단위 테스트 작성
- [x] API 문서를 작성하고 문서화를 위한 테스트 작성
- [x] 페이지 연동

### 시나리오

```gherkin
Scenario: 로그인
Given 회원가입이 되어있다.
When 사용자는 이메일과 비밀번호로 로그인을 요청한다.
Then 로그인이 된다.
And 기존 페이지로 돌아간다.
```

## 2단계 - 즐겨찾기 기능

### 즐겨찾기 기능

### 요구사항

- [x] 즐겨찾기 기능을 추가(추가,삭제,조회)
- [x] 자신의 정보만 수정 가능하도록 해야하며 로그인이 선행되어야 함
- [x] 토큰의 유효성 검사와 본인 여부를 판단하는 로직 추가(interceptor, argument resolver)
- [ ] side case에 대한 예외처리 필수
- [x] 인수 테스트와 단위 테스트 작성
- [ ] API 문서를 작성하고 문서화를 위한 테스트 작성
- [ ] 페이지 연동
51 changes: 35 additions & 16 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,31 +1,50 @@
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'
asciidoctor 'org.springframework.restdocs:spring-restdocs-asciidoctor'
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
runtimeOnly 'com.h2database:h2'
}

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'
}
}
50 changes: 44 additions & 6 deletions src/docs/asciidoc/api-guide.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,56 @@ endif::[]
:icons: font
:source-highlighter: highlightjs
:toc: left
:toclevels: 2
:toclevels: 3
:sectlinks:
:operation-http-request-title: Example Request
:operation-http-response-title: Example Response
:operation-path-parameters-title: 경로 파라미터
:operation-request-headers-title: 요청 헤더
:operation-request-body-title: 요청 바디
:operation-request-fields-title: 요청 필드
:operation-request-parameters-title: 요청 파라미터
:operation-http-request-title: 요청 예시
:operation-http-response-title: 응답 예시

[[resources]]
= Resources
= 우아한지하철 서비스 어드민

[[resources-members]]
== Member
== 회원 관리

[[resources-members-create]]
=== 회원 가입

operation::members/create[snippets='http-request,http-response']
operation::members/create[snippets='request-fields,request-body,http-request,http-response']

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

operation::members/findByEmail[snippets='request-headers,request-parameters,http-request,http-response']

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

operation::members/update[snippets='request-headers,path-parameters,request-fields,request-body,http-request,http-response']

[[resources-member-delete]]
=== 회원 정보 삭제

operation::members/delete[snippets='request-headers,path-parameters,http-request,http-response']

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

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

operation::favorites/add[snippets='request-headers,request-fields,request-body,http-request,http-response']

[[resources-favorites-getAll]]
=== 즐겨찾기 목록 조회

operation::favorites/getAll[snippets='request-headers,http-request,http-response']

[[resources-favorites-remove]]
=== 즐겨찾기 제거

operation::favorites/remove[snippets='request-headers,path-parameters,http-request,http-response']
3 changes: 2 additions & 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,8 @@
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
17 changes: 17 additions & 0 deletions src/main/java/wooteco/subway/config/Utf8EncodingFilter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package wooteco.subway.config;

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.CharacterEncodingFilter;

@Configuration
public class Utf8EncodingFilter {

private static final String ENCODING = "UTF-8";

@Bean
public FilterRegistrationBean<CharacterEncodingFilter> utf8CharacterEncodingFilter() {
return new FilterRegistrationBean<>(new CharacterEncodingFilter(ENCODING, true));
}
}
31 changes: 15 additions & 16 deletions src/main/java/wooteco/subway/config/WebMvcConfig.java
Original file line number Diff line number Diff line change
@@ -1,41 +1,40 @@
package wooteco.subway.config;

import java.util.List;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
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;

@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/bearer")
.addPathPatterns("/members")
.addPathPatterns("/members/*")
.addPathPatterns("/favorites")
.addPathPatterns("/favorites/*");
}

@Override
public void addArgumentResolvers(List argumentResolvers) {
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(loginMemberArgumentResolver);
}
}
41 changes: 19 additions & 22 deletions src/main/java/wooteco/subway/domain/line/LineStations.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
package wooteco.subway.domain.line;

import java.util.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;

public class LineStations {
private Set<LineStation> stations;
Expand Down Expand Up @@ -29,7 +34,7 @@ private void remove(LineStation targetLineStation) {

public void removeById(Long targetStationId) {
extractByStationId(targetStationId)
.ifPresent(this::remove);
.ifPresent(this::remove);
}

public List<Long> getStationIds() {
Expand All @@ -40,37 +45,29 @@ public List<Long> getStationIds() {

private void extractNext(Long preStationId, List<Long> ids) {
stations.stream()
.filter(it -> Objects.equals(it.getPreStationId(), preStationId))
.findFirst()
.ifPresent(it -> {
Long nextStationId = it.getStationId();
ids.add(nextStationId);
extractNext(nextStationId, ids);
});
.filter(it -> Objects.equals(it.getPreStationId(), preStationId))
.findFirst()
.ifPresent(it -> {
Long nextStationId = it.getStationId();
ids.add(nextStationId);
extractNext(nextStationId, ids);
});
}

private void updatePreStationOfNextLineStation(Long targetStationId, Long newPreStationId) {
extractByPreStationId(targetStationId)
.ifPresent(it -> it.updatePreLineStation(newPreStationId));
.ifPresent(it -> it.updatePreLineStation(newPreStationId));
}

private Optional<LineStation> extractByStationId(Long stationId) {
return stations.stream()
.filter(it -> Objects.equals(it.getStationId(), stationId))
.findFirst();
.filter(it -> Objects.equals(it.getStationId(), stationId))
.findFirst();
}

private Optional<LineStation> extractByPreStationId(Long preStationId) {
return stations.stream()
.filter(it -> Objects.equals(it.getPreStationId(), preStationId))
.findFirst();
}

public int getTotalDistance() {
return stations.stream().mapToInt(it -> it.getDistance()).sum();
}

public int getTotalDuration() {
return stations.stream().mapToInt(it -> it.getDuration()).sum();
.filter(it -> Objects.equals(it.getPreStationId(), preStationId))
.findFirst();
}
}
26 changes: 3 additions & 23 deletions src/main/java/wooteco/subway/domain/line/Lines.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,8 @@ public List<Line> getLines() {

public List<Long> getStationIds() {
return lines.stream()
.flatMap(it -> it.getStations().stream())
.map(it -> it.getStationId())
.collect(Collectors.toList());
.flatMap(it -> it.getStations().stream())
.map(it -> it.getStationId())
.collect(Collectors.toList());
}
//
// public LineStations extractLineStationByStationIds(List<Long> stationIds) {
// for (Long stationId : stationIds) {
// List<LineStation> lineStations = findLineStation(null, stationId);
// }
// Set<LineStation> lineStations = stationIds.stream()
// .map(it -> getLineStationByStationId(it))
// .collect(Collectors.toSet());
//
// return new LineStations(lineStations);
// }
//
// private LineStation getLineStationByStationId(Long stationId) {
// return lines.stream()
// .flatMap(it -> it.getStations().stream())
//// .filter(it -> Objects.nonNull(it.getPreStationId()))
// .filter(it -> it.getStationId() == stationId)
// .findFirst()
// .orElseThrow(RuntimeException::new);
// }
}
Loading