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

[Spring 지하철 경로 조회 3단계] 다니(이다은) 미션 제출합니다. #128

Merged
merged 16 commits into from
Jun 1, 2021
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,14 @@ npm run serve
- [x] 발급한 토큰을 이용하여 로그인이 필요한 기능(회원 정보 수정/삭제) 요청 시 포함하여 보내기
- [x] 이를 이용하여 기능이 동작하도록 리팩터링 하기
- [x] 프론트엔드 코드 중 API를 호출하는 부분을 구현하여 기능이 잘 동작하도록 완성하기
<br>

> 3단계

- [x] 출발역과 도착역 사이의 최단 거리 경로를 구하는 API를 구현하기
- [x] 검색 시 경로와 함께 총 거리를 출력하기(요금은 무시)
- [x] 한 노선에서 경로 찾기 뿐만 아니라 여러 노선의 환승도 고려하기
- [x] 프론트엔드 코드 중 API를 호출하는 부분을 구현하여 기능이 잘 동작하도록 완성하기

<br>

Expand Down
14 changes: 8 additions & 6 deletions frontend/src/pages/line/LinePage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,14 @@ export default {
...mapGetters(["lines"]),
},
async created() {
// TODO 초기 역 데이터를 불러오는 API를 추가해주세요.
// const stations = await fetch("/api/stations")
// this.setStations([...stations])
// TODO 초기 노선 데이터를 불러오는 API를 추가해주세요.
// const lines = await fetch("/api/lines")
// this.setLines([...lines])
// 초기 역 데이터를 불러오는 API를 추가해주세요.
const stationsResponse = await fetch("/api/stations");
const stations = await stationsResponse.json();
this.setStations([...stations]);
// 초기 노선 데이터를 불러오는 API를 추가해주세요.
const linesResponse = await fetch("/api/lines");
const lines = await linesResponse.json();
this.setLines([...lines]);
},
methods: {
...mapMutations([SET_LINES, SET_STATIONS]),
Expand Down
13 changes: 10 additions & 3 deletions frontend/src/pages/line/components/LineCreateButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,16 @@ export default {
return;
}
try {
// TODO 노선을 추가하는 API를 추가해주세요.
// const newLine = await fetch("/api/lines")
// this.setLines([...this.lines, { ...newLine }]); setLines는 데이터를 관리하기 위해 단 1개 존재하는 저장소에 노선 정보를 저장하는 메서드입니다.
// 노선을 추가하는 API를 추가해주세요.
const newLineResponse = await fetch("/api/lines", {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(this.lineForm)
});
const newLine = await newLineResponse.json();
this.setLines([...this.lines, { ...newLine }]); // setLines는 데이터를 관리하기 위해 단 1개 존재하는 저장소에 노선 정보를 저장하는 메서드입니다.
this.initLineForm();
this.closeDialog();
this.showSnackbar(SNACKBAR_MESSAGES.LINE.CREATE.SUCCESS);
Expand Down
13 changes: 8 additions & 5 deletions frontend/src/pages/line/components/LineDeleteButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,14 @@ export default {
...mapMutations([SHOW_SNACKBAR, SET_LINES]),
async onDeleteLine() {
try {
// TODO Line을 삭제하는 API를 추가해주세요.
// await fetch("/api/lines/{id}")
// TODO 전체 Line 데이터를 불러오는 API를 추가해주세요.
// const lines = await fetch("/api/lines")
// this.setLines([...lines])
// Line을 삭제하는 API를 추가해주세요.
await fetch(`/api/lines/${this.line.id}`, {
method: 'DELETE'
});
// 전체 Line 데이터를 불러오는 API를 추가해주세요.
const linesResponse = await fetch("/api/lines");
const lines = await linesResponse.json();
this.setLines([...lines]);
this.showSnackbar(SNACKBAR_MESSAGES.LINE.DELETE.SUCCESS);
} catch (e) {
this.showSnackbar(SNACKBAR_MESSAGES.LINE.DELETE.FAIL);
Expand Down
17 changes: 12 additions & 5 deletions frontend/src/pages/line/components/LineEditButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,18 @@ export default {
},
async onEditLine() {
try {
// TODO Line을 수정하는 API를 추가해주세요.
// await fetch("/api/lines/{id}", { data: this.lineEditForm })
// TODO 전체 Line 데이터를 불러오는 API를 추가해주세요.
// const lines = await fetch("/api/lines")
// this.setLines([...lines])
// Line을 수정하는 API를 추가해주세요.
await fetch(`/api/lines/${this.line.id}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(this.lineEditForm)
});
// 전체 Line 데이터를 불러오는 API를 추가해주세요.
const linesResponse = await fetch("/api/lines");
const lines = await linesResponse.json();
this.setLines([...lines]);
this.closeDialog();
this.showSnackbar(SNACKBAR_MESSAGES.LINE.UPDATE.SUCCESS);
} catch (e) {
Expand Down
17 changes: 10 additions & 7 deletions frontend/src/pages/path/PathPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -159,18 +159,20 @@ export default {
...mapMutations([SHOW_SNACKBAR, SET_STATIONS]),
async onSearchResult() {
try {
// TODO 최단 거리를 검색하는 API를 추가해주세요.
// this.pathResult = await fetch("/paths", {})
// 최단 거리를 검색하는 API를 추가해주세요.
const response = await fetch(`/api/paths?source=${this.path.source}&target=${this.path.target}`);
this.pathResult = await response.json();
} catch (e) {
this.showSnackbar(SNACKBAR_MESSAGES.COMMON.FAIL);
throw new Error(e);
}
},
async initAllStationsView() {
try {
// TODO 모든 역을 불러오는 API를 추가해주세요.
// const stations = await fetch("/stations")
// this.setStations(stations)
// 모든 역을 불러오는 API를 추가해주세요.
const stationsResponse= await fetch("/api/stations");
const stations = await stationsResponse.json();
this.setStations(stations);
if (this.stations.length < 1) {
return;
}
Expand All @@ -187,8 +189,9 @@ export default {
},
async onSearchMinimumDurationType() {
try {
// TODO 최소 시간을 검색하는 API를 추가해주세요.
// this.pathResultByMinimumDuration = await fetch("/paths", {})
// 최소 시간을 검색하는 API를 추가해주세요.
const response = await fetch(`/api/paths?source=${this.path.source}&target=${this.path.target}`);
this.pathResultByMinimumDuration = await response.json();
} catch (e) {
this.showSnackbar(SNACKBAR_MESSAGES.COMMON.FAIL);
throw new Error(e);
Expand Down
19 changes: 11 additions & 8 deletions frontend/src/pages/section/SectionPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,14 @@ export default {
name: "SectionPage",
components: { SectionDeleteButton, SectionCreateButton },
async created() {
// TODO 초기 역 데이터를 불러오는 API를 추가해주세요.
// const stations = await fetch("/api/stations")
// this.setStations([...stations])
// TODO 초기 노선 데이터를 불러오는 API를 추가해주세요.
// const lines = await fetch("/api/lines");
// this.setLines([...lines]);
// 초기 역 데이터를 불러오는 API를 추가해주세요.
const stationsResponse = await fetch("/api/stations");
const stations = await stationsResponse.json();
this.setStations([...stations]);
// 초기 노선 데이터를 불러오는 API를 추가해주세요.
const linesResponse = await fetch("/api/lines");
const lines = await linesResponse.json();
this.setLines([...lines]);
this.initLinesView();
},
computed: {
Expand Down Expand Up @@ -124,8 +126,9 @@ export default {
},
async onChangeLine() {
try {
// TODO 선택한 노선 데이터를 불러오는 API를 추가해주세요.
// this.activeLine = await fetch("/lines/{this.activeLineId}");
// 선택한 노선 데이터를 불러오는 API를 추가해주세요.
const response = await fetch(`/api/lines/${this.activeLineId}`);
this.activeLine = await response.json();
} catch (e) {
this.showSnackbar(SNACKBAR_MESSAGES.COMMON.FAIL);
throw new Error(e);
Expand Down
25 changes: 15 additions & 10 deletions frontend/src/pages/section/components/SectionCreateButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,9 @@ export default {
},
async initLineStationsView() {
try {
// TODO 선택된 노선의 데이터를 불러와주세요.
// this.selectedLine = await fetch('/api/lines/{this.sectionForm.lineId}')
// 선택된 노선의 데이터를 불러와주세요.
const response = await fetch(`/api/lines/${this.sectionForm.lineId}`);
this.selectedLine = await response.json();
if (this.selectedLine.stations?.length < 1) {
return;
}
Expand Down Expand Up @@ -155,14 +156,18 @@ export default {
return;
}
try {
// TODO 구간을 추가하는 API를 작성해주세요.
// await fetch("/api/section", {
// lineId: this.selectedLine.id,
// section: this.sectionForm,
// });
// TODO 전체 line을 불러오는 API를 작성해주세요.
// const lines = await fetch("/api/lines");
// this.setLines(lines)
// 구간을 추가하는 API를 작성해주세요.
await fetch(`/api/lines/${this.sectionForm.lineId}/sections`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(this.sectionForm)
});
// 전체 line을 불러오는 API를 작성해주세요.
const linesResponse = await fetch("/api/lines");
const lines = await linesResponse.json();
this.setLines(lines);
const line = this.lines.find(({ id }) => id === this.selectedLine.id);
this.setLine(line);
this.$refs.sectionForm.resetValidation();
Expand Down
20 changes: 10 additions & 10 deletions frontend/src/pages/section/components/SectionDeleteButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,26 +13,26 @@ export default {
name: "SectionDeleteButton",
props: {
lineId: {
type: String,
type: Number,
required: true,
},
stationId: {
type: String,
type: Number,
required: true,
},
},
methods: {
...mapMutations([SHOW_SNACKBAR, SET_LINE]),
async onDeleteLine() {
try {
// TODO 해당 구간을 삭제하는 api를 작성해주세요.
// await fetch("/api/section/{id}", {
// lineId: this.lineId,
// stationId: this.stationId,
// })
// TODO 현재 active된 line의 데이터를 최신으로 불러와주세요.
// const line = await fetch("/api/line/{lineId}")
// this.setLine({ ...line })
// 해당 구간을 삭제하는 api를 작성해주세요.
await fetch(`/api/lines/${this.lineId}/sections?stationId=${this.stationId}`, {
method: 'DELETE'
});
// 현재 active된 line의 데이터를 최신으로 불러와주세요.
const lineResponse = await fetch(`/api/lines/${this.lineId}`);
const line = await lineResponse.json();
this.setLine({ ...line });
this.showSnackbar(SNACKBAR_MESSAGES.COMMON.SUCCESS);
} catch (e) {
this.showSnackbar(SNACKBAR_MESSAGES.COMMON.FAIL);
Expand Down
14 changes: 8 additions & 6 deletions frontend/src/pages/station/StationPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ export default {
...mapGetters(["stations"]),
},
async created() {
// TODO 초기 역 데이터를 불러오는 API를 추가해주세요.
const response = await fetch("http://localhost:8080/stations");
// 초기 역 데이터를 불러오는 API를 추가해주세요.
const response = await fetch("/api/stations");
if (!response.ok) {
throw new Error(`${response.status}`);
}
Expand All @@ -83,8 +83,8 @@ export default {
return;
}
try {
// TODO 역을 추가하는 API Sample
const response = await fetch("http://localhost:8080/stations", {
// 역을 추가하는 API Sample
const response = await fetch("/api/stations", {
method: "POST",
headers: {
"Content-Type": "application/json",
Expand Down Expand Up @@ -112,8 +112,10 @@ export default {
},
async onDeleteStation(stationId) {
try {
// TODO 역을 삭제하는 API를 추가해주세요.
// await fetch("http://localhost:8080/stations/{id}");
// 역을 삭제하는 API를 추가해주세요.
await fetch(`/api/stations/${stationId}`, {
method: 'DELETE'
});
const idx = this.stations.findIndex(
(station) => station.id === stationId
);
Expand Down
29 changes: 27 additions & 2 deletions src/main/java/wooteco/subway/auth/ui/LoginInterceptor.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package wooteco.subway.auth.ui;

import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import wooteco.subway.auth.infrastructure.AuthorizationExtractor;
import wooteco.subway.auth.infrastructure.JwtTokenProvider;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Objects;

@Component
public class LoginInterceptor implements HandlerInterceptor {
Expand All @@ -17,9 +19,32 @@ public LoginInterceptor(JwtTokenProvider jwtTokenProvider) {
}

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
if (isPreflightRequest(request)) {
return true;
}
String token = AuthorizationExtractor.extract(request);
jwtTokenProvider.validateToken(token);
return HandlerInterceptor.super.preHandle(request, response, handler);
return true;
}

private boolean isPreflightRequest(HttpServletRequest request) {
return isOptions(request) && hasHeaders(request) && hasMethod(request) && hasOrigin(request);
}

private boolean isOptions(HttpServletRequest request) {
return request.getMethod().equalsIgnoreCase(HttpMethod.OPTIONS.toString());
}

private boolean hasHeaders(HttpServletRequest request) {
return Objects.nonNull(request.getHeader("Access-Control-Request-Headers"));
}

private boolean hasMethod(HttpServletRequest request) {
return Objects.nonNull(request.getHeader("Access-Control-Request-Method"));
}

private boolean hasOrigin(HttpServletRequest request) {
return Objects.nonNull(request.getHeader("Origin"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package wooteco.subway.exception;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(HttpStatus.BAD_REQUEST)
public class StationNonexistenceException extends SubwayException {
public StationNonexistenceException() {
super("존재하지 않는 역입니다.");
}
}
20 changes: 12 additions & 8 deletions src/main/java/wooteco/subway/line/dto/LineResponse.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@
import wooteco.subway.station.dto.StationResponse;

import java.util.List;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.toList;

public class LineResponse {
private final Long id;
private final String name;
private final String color;
private final List<StationResponse> stations;
private Long id;
private String name;
private String color;
private List<StationResponse> stations;

public LineResponse() {
}

public LineResponse(Long id, String name, String color, List<StationResponse> stations) {
this.id = id;
Expand All @@ -21,15 +25,15 @@ public LineResponse(Long id, String name, String color, List<StationResponse> st

public static LineResponse of(Line line) {
List<StationResponse> stations = line.getStations().stream()
.map(it -> StationResponse.of(it))
.collect(Collectors.toList());
.map(StationResponse::of)
.collect(toList());
return new LineResponse(line.getId(), line.getName(), line.getColor(), stations);
}

public static List<LineResponse> listOf(List<Line> lines) {
return lines.stream()
.map(LineResponse::of)
.collect(Collectors.toList());
.collect(toList());
}

public Long getId() {
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/wooteco/subway/line/ui/LineController.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import java.util.List;

@RestController
@RequestMapping("/lines")
@RequestMapping("/api/lines")
public class LineController {

private final LineService lineService;
Expand Down