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

feat : 버스 버저닝 V2 updated_at 추가 #446

Merged
merged 10 commits into from
Apr 21, 2024
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()));
}
}
Loading