Skip to content

Commit

Permalink
feat : 버스 버저닝 V2 updated_at 추가 (#446)
Browse files Browse the repository at this point in the history
* feat : 버스 timetable v2 추가

* feat : 버스 버저닝 timetable/v2 서비스 구현

* test : 버스 시간표 조회 v2 테스트 추가

* chore : 버스 v2 테스트 수정

* chore : 사용하지 않는 import 제거

* chore : log() 제거

* chore : 스웨거 설명 추가

(cherry picked from commit f778f5e)
  • Loading branch information
Choon0414 authored and Choi-JJunho committed May 9, 2024
1 parent adc53f0 commit 9ef08ff
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

import in.koreatech.koin.domain.bus.dto.BusCourseResponse;
import in.koreatech.koin.domain.bus.dto.BusRemainTimeResponse;
import in.koreatech.koin.domain.bus.dto.BusTimetableResponse;
import in.koreatech.koin.domain.bus.dto.SingleBusTimeResponse;
import in.koreatech.koin.domain.bus.model.BusTimetable;
import in.koreatech.koin.domain.bus.model.enums.BusStation;
Expand Down Expand Up @@ -48,6 +49,14 @@ ResponseEntity<List<? extends BusTimetable>> getBusTimetable(
@RequestParam(value = "region") String region
);

@Operation(summary = "버스 시간표 조회")
@GetMapping("/timetable/v2")
ResponseEntity<BusTimetableResponse> getBusTimetableV2(
@RequestParam(value = "bus_type") BusType busType,
@RequestParam(value = "direction") String direction,
@RequestParam(value = "region") String region
);

@ApiResponses(
value = {
@ApiResponse(responseCode = "200"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

import in.koreatech.koin.domain.bus.dto.BusCourseResponse;
import in.koreatech.koin.domain.bus.dto.BusRemainTimeResponse;
import in.koreatech.koin.domain.bus.dto.BusTimetableResponse;
import in.koreatech.koin.domain.bus.dto.SingleBusTimeResponse;
import in.koreatech.koin.domain.bus.model.BusTimetable;
import in.koreatech.koin.domain.bus.model.enums.BusStation;
Expand Down Expand Up @@ -46,6 +47,15 @@ public ResponseEntity<List<? extends BusTimetable>> getBusTimetable(
return ResponseEntity.ok().body(busService.getBusTimetable(busType, direction, region));
}

@GetMapping("/timetable/v2")
public ResponseEntity<BusTimetableResponse> getBusTimetableV2(
@RequestParam(value = "bus_type") BusType busType,
@RequestParam(value = "direction") String direction,
@RequestParam(value = "region") String region
){
return ResponseEntity.ok().body(busService.getBusTimetableWithUpdatedAt(busType, direction, region));
}

@GetMapping("/courses")
public ResponseEntity<List<BusCourseResponse>> getBusCourses() {
return ResponseEntity.ok().body(busService.getBusCourses());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package in.koreatech.koin.domain.bus.dto;

import static com.fasterxml.jackson.databind.PropertyNamingStrategies.SnakeCaseStrategy;
import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.NOT_REQUIRED;

import java.time.LocalDateTime;
import java.util.List;

import com.fasterxml.jackson.databind.annotation.JsonNaming;

import in.koreatech.koin.domain.bus.model.BusTimetable;
import io.swagger.v3.oas.annotations.media.Schema;

@JsonNaming(SnakeCaseStrategy.class)
public record BusTimetableResponse(
@Schema(description = "버스 시간표", example = """
{
"route_name": "주말(14시 35분)",
"arrival_info": {
"nodeName": "터미널(신세계 앞 횡단보도)",
"arrivalTime": "14:35"
}
}
""", requiredMode = NOT_REQUIRED)
List<? extends BusTimetable> busTimetable,

@Schema(description = "업데이트 시각", example = "2024-04-20 18:00:00", requiredMode = NOT_REQUIRED)
LocalDateTime updatedAt
) {
}
15 changes: 15 additions & 0 deletions src/main/java/in/koreatech/koin/domain/bus/service/BusService.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import in.koreatech.koin.domain.bus.dto.BusCourseResponse;
import in.koreatech.koin.domain.bus.dto.BusRemainTimeResponse;
import in.koreatech.koin.domain.bus.dto.BusTimetableResponse;
import in.koreatech.koin.domain.bus.dto.SingleBusTimeResponse;
import in.koreatech.koin.domain.bus.exception.BusIllegalStationException;
import in.koreatech.koin.domain.bus.exception.BusTypeNotFoundException;
Expand All @@ -31,6 +32,8 @@
import in.koreatech.koin.domain.bus.repository.BusRepository;
import in.koreatech.koin.domain.bus.util.CityBusOpenApiClient;
import in.koreatech.koin.domain.bus.util.ExpressBusOpenApiClient;
import in.koreatech.koin.domain.version.dto.VersionResponse;
import in.koreatech.koin.domain.version.service.VersionService;
import lombok.RequiredArgsConstructor;

@Service
Expand All @@ -42,6 +45,7 @@ public class BusService {
private final BusRepository busRepository;
private final CityBusOpenApiClient cityBusOpenApiClient;
private final ExpressBusOpenApiClient expressBusOpenApiClient;
private final VersionService versionService;

@Transactional
public BusRemainTimeResponse getBusRemainTime(BusType busType, BusStation depart, BusStation arrival) {
Expand Down Expand Up @@ -178,6 +182,17 @@ public List<? extends BusTimetable> getBusTimetable(BusType busType, String dire
throw new BusTypeNotFoundException(busType.name());
}

public BusTimetableResponse getBusTimetableWithUpdatedAt(BusType busType, String direction, String region){
List<? extends BusTimetable> BusTimetables = getBusTimetable(busType, direction, region);

if (busType.equals(BusType.COMMUTING)){
busType = BusType.SHUTTLE;
}

VersionResponse version = versionService.getVersion(busType.name().toLowerCase() + "_bus_timetable");
return new BusTimetableResponse(BusTimetables, version.updatedAt());
}

public List<BusCourseResponse> getBusCourses() {
return busRepository.findAll().stream()
.map(BusCourseResponse::from)
Expand Down
78 changes: 69 additions & 9 deletions src/test/java/in/koreatech/koin/acceptance/BusApiTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import in.koreatech.koin.domain.bus.repository.ExpressBusCacheRepository;
import in.koreatech.koin.domain.version.model.Version;
import in.koreatech.koin.domain.version.repository.VersionRepository;
import in.koreatech.koin.domain.version.service.VersionService;
import in.koreatech.koin.support.JsonAssertions;
import io.restassured.RestAssured;
import io.restassured.response.ExtractableResponse;
Expand All @@ -59,6 +60,9 @@ class BusApiTest extends AcceptanceTest {
@Autowired
private ExpressBusCacheRepository expressBusCacheRepository;

@Autowired
private VersionService versionService;

private final Instant UPDATED_AT = ZonedDateTime.parse(
"2024-02-21 18:00:00 KST",
ofPattern("yyyy-MM-dd " + "HH:mm:ss z")
Expand Down Expand Up @@ -137,11 +141,11 @@ void getNextShuttleBusRemainTime() {
softly -> {
softly.assertThat(response.body().jsonPath().getString("bus_type"))
.isEqualTo(busType.name().toLowerCase());
softly.assertThat((Long) response.body().jsonPath().get("now_bus.bus_number")).isNull();
softly.assertThat((Long)response.body().jsonPath().get("now_bus.bus_number")).isNull();
softly.assertThat(response.body().jsonPath().getLong("now_bus.remain_time")).isEqualTo(
BusRemainTime.from(arrivalTime).getRemainSeconds(clock));
softly.assertThat((Long) response.body().jsonPath().get("next_bus.bus_number")).isNull();
softly.assertThat((Long) response.body().jsonPath().get("next_bus.remain_time")).isNull();
softly.assertThat((Long)response.body().jsonPath().get("next_bus.bus_number")).isNull();
softly.assertThat((Long)response.body().jsonPath().get("next_bus.remain_time")).isNull();
}
);
}
Expand Down Expand Up @@ -200,8 +204,8 @@ void getNextCityBusRemainTimeRedis() {
softly -> {
softly.assertThat(response.body().jsonPath().getString("bus_type"))
.isEqualTo(busType.name().toLowerCase());
softly.assertThat((Long) response.body().jsonPath().getLong("now_bus.bus_number")).isEqualTo(busNumber);
softly.assertThat((Long) response.body().jsonPath().getLong("now_bus.remain_time"))
softly.assertThat((Long)response.body().jsonPath().getLong("now_bus.bus_number")).isEqualTo(busNumber);
softly.assertThat((Long)response.body().jsonPath().getLong("now_bus.remain_time"))
.isEqualTo(
BusRemainTime.of(remainTime, version.getUpdatedAt().toLocalTime()).getRemainSeconds(clock));
softly.assertThat(response.body().jsonPath().getObject("next_bus.bus_number", Long.class)).isNull();
Expand Down Expand Up @@ -303,12 +307,12 @@ void getNextCityBusRemainTimeOpenApi() {
softly -> {
softly.assertThat(response.body().jsonPath().getString("bus_type"))
.isEqualTo(busType.name().toLowerCase());
softly.assertThat((Long) response.body().jsonPath().getLong("now_bus.bus_number")).isEqualTo(400);
softly.assertThat((Long) response.body().jsonPath().getLong("now_bus.remain_time"))
softly.assertThat((Long)response.body().jsonPath().getLong("now_bus.bus_number")).isEqualTo(400);
softly.assertThat((Long)response.body().jsonPath().getLong("now_bus.remain_time"))
.isEqualTo(
BusRemainTime.of(600L, version.getUpdatedAt().toLocalTime()).getRemainSeconds(clock));
softly.assertThat((Long) response.body().jsonPath().getLong("next_bus.bus_number")).isEqualTo(405);
softly.assertThat((Long) response.body().jsonPath().getLong("next_bus.remain_time"))
softly.assertThat((Long)response.body().jsonPath().getLong("next_bus.bus_number")).isEqualTo(405);
softly.assertThat((Long)response.body().jsonPath().getLong("next_bus.remain_time"))
.isEqualTo(
BusRemainTime.of(800L, version.getUpdatedAt().toLocalTime()).getRemainSeconds(clock));
}
Expand Down Expand Up @@ -470,4 +474,60 @@ void getShuttleBusTimetable() {
]
""");
}

@Test
@DisplayName("셔틀버스 시간표를 조회한다(업데이트 시각 포함).")
void getShuttleBusTimetableWithUpdatedAt() {
when(dateTimeProvider.getNow()).thenReturn(Optional.of(UPDATED_AT));

Version version = Version.builder()
.version("20240_1712920946")
.type("shuttle_bus_timetable")
.build();
versionRepository.save(version);

BusType busType = BusType.from("shuttle");
String direction = "from";
String region = "천안";

ExtractableResponse<Response> response = RestAssured
.given()
.when()
.param("bus_type", busType.name().toLowerCase())
.param("direction", direction)
.param("region", region)
.get("/bus/timetable/v2")
.then()
.statusCode(HttpStatus.OK.value())
.extract();

JsonAssertions.assertThat(response.asPrettyString())
.isEqualTo(String.format("""
{
"bus_timetable": [
{
"route_name": "주중",
"arrival_info": [
{
"nodeName": "한기대",
"arrivalTime": "18:10"
},
{
"nodeName": "신계초,운전리,연춘리",
"arrivalTime": "정차"
},
{
"nodeName": "천안역(학화호두과자)",
"arrivalTime": "18:50"
},{
"nodeName": "터미널(신세계 앞 횡단보도)",
"arrivalTime": "18:55"
}
]
}
],
"updated_at": %s
}
""", version.getUpdatedAt()));
}
}

0 comments on commit 9ef08ff

Please sign in to comment.