From 38570d0781ba1ac8d8ba93981b4f28c1cbcf3491 Mon Sep 17 00:00:00 2001 From: Tommi Leinamo Date: Fri, 1 Dec 2023 17:28:46 +0200 Subject: [PATCH 1/6] Remove some unnecessary line breaks to improve formatting --- .../timetables/repository/VehicleScheduleFrameRepository.kt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/fi/hsl/jore4/timetables/repository/VehicleScheduleFrameRepository.kt b/src/main/kotlin/fi/hsl/jore4/timetables/repository/VehicleScheduleFrameRepository.kt index a6320ef..bfadcb8 100644 --- a/src/main/kotlin/fi/hsl/jore4/timetables/repository/VehicleScheduleFrameRepository.kt +++ b/src/main/kotlin/fi/hsl/jore4/timetables/repository/VehicleScheduleFrameRepository.kt @@ -29,8 +29,7 @@ class VehicleScheduleFrameRepository(private val dsl: DSLContext, config: Defaul val stagingFrameIdName: Name = DSL.name("stagingVehicleScheduleFrameId") val replacedFrameIdName: Name = DSL.name("replacedVehicleScheduleFrameId") val stagingVehicleScheduleFrameIdField = DSL.field(stagingFrameIdName, UUID::class.java) - val replacedVehicleScheduleFrameIdField = - DSL.field(replacedFrameIdName, UUID::class.java) + val replacedVehicleScheduleFrameIdField = DSL.field(replacedFrameIdName, UUID::class.java) val stagingFrame = VEHICLE_SCHEDULE_FRAME.`as`("staging") val replacedFrame = VEHICLE_SCHEDULE_FRAME.`as`("replaced") @@ -102,8 +101,7 @@ class VehicleScheduleFrameRepository(private val dsl: DSLContext, config: Defaul val stagingFrameIdName: Name = DSL.name("stagingVehicleScheduleFrameId") val targetFrameIdName: Name = DSL.name("targetVehicleScheduleFrameId") val stagingVehicleScheduleFrameIdField = DSL.field(stagingFrameIdName, UUID::class.java) - val targetVehicleScheduleFrameIdField = - DSL.field(targetFrameIdName, UUID::class.java) + val targetVehicleScheduleFrameIdField = DSL.field(targetFrameIdName, UUID::class.java) val stagingFrame = VEHICLE_SCHEDULE_FRAME.`as`("staging") val targetFrame = VEHICLE_SCHEDULE_FRAME.`as`("target") From b0967923b046e606cf6f2e99515000cd2b56e388 Mon Sep 17 00:00:00 2001 From: Tommi Leinamo Date: Mon, 4 Dec 2023 08:40:25 +0200 Subject: [PATCH 2/6] Remove placeholder code to reflect updated requirements We won't be preventing these after all. Current plan is to instead show a warning to the user in the UI. See https://github.com/HSLdevcom/jore4/issues/1566 --- .../hsl/jore4/timetables/service/CombineTimetablesService.kt | 3 --- .../jore4/timetables/service/CombineTimetablesServiceTest.kt | 5 +---- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/main/kotlin/fi/hsl/jore4/timetables/service/CombineTimetablesService.kt b/src/main/kotlin/fi/hsl/jore4/timetables/service/CombineTimetablesService.kt index d69106c..a270c64 100644 --- a/src/main/kotlin/fi/hsl/jore4/timetables/service/CombineTimetablesService.kt +++ b/src/main/kotlin/fi/hsl/jore4/timetables/service/CombineTimetablesService.kt @@ -61,9 +61,6 @@ class CombineTimetablesService( ) } - // TODO: ensure that there are no identical journeys in staging and target. - // DB triggers do not ensure this. - LOGGER.info("Moving staging vehicle services to target...") moveStagingVehicleServicesToTarget( stagingFrame = stagingVehicleScheduleFrame, diff --git a/src/test/kotlin/fi/hsl/jore4/timetables/service/CombineTimetablesServiceTest.kt b/src/test/kotlin/fi/hsl/jore4/timetables/service/CombineTimetablesServiceTest.kt index 1259f55..6c4a38f 100644 --- a/src/test/kotlin/fi/hsl/jore4/timetables/service/CombineTimetablesServiceTest.kt +++ b/src/test/kotlin/fi/hsl/jore4/timetables/service/CombineTimetablesServiceTest.kt @@ -303,7 +303,7 @@ class CombineTimetablesServiceTest @Autowired constructor( } @Test - fun `fails when target frame already contains the staging vehicle journey`() { + fun `combines a vehicle journey that already exists in the target frame`() { val testData = TimetablesDataset.createFromResource("datasets/combine.json") val stagingBlock = testData @@ -326,9 +326,6 @@ class CombineTimetablesServiceTest @Autowired constructor( timetablesDataInserterRunner.truncateAndInsertDataset(testData.toJSONString()) val stagingFrameId = UUID.fromString("aa0e95c6-34d1-4d09-8546-3789b04ea494") - // FIXME: this should fail instead. The service currently works incorrectly in this case. - // Also, check correct exception class. - // Might need more robust tests than just this one case (at least for valid corner cases). val result = combineTimetablesService.combineTimetables( listOf(stagingFrameId), TimetablesPriority.STANDARD From f990c0e23f3ac6d4cba2007c7a3b5b5abf8643d0 Mon Sep 17 00:00:00 2001 From: Tommi Leinamo Date: Mon, 4 Dec 2023 09:01:58 +0200 Subject: [PATCH 3/6] Refactor: move target vehicle schedule frame fetching to a function --- .../service/CombineTimetablesService.kt | 32 +++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/src/main/kotlin/fi/hsl/jore4/timetables/service/CombineTimetablesService.kt b/src/main/kotlin/fi/hsl/jore4/timetables/service/CombineTimetablesService.kt index a270c64..b02afd2 100644 --- a/src/main/kotlin/fi/hsl/jore4/timetables/service/CombineTimetablesService.kt +++ b/src/main/kotlin/fi/hsl/jore4/timetables/service/CombineTimetablesService.kt @@ -44,6 +44,27 @@ class CombineTimetablesService( val stagingVehicleScheduleFrame = fetchStagingVehicleScheduleFrame(stagingVehicleScheduleFrameId) + val targetVehicleScheduleFrame = fetchTargetVehicleScheduleFrame(stagingVehicleScheduleFrame, targetPriority) + + LOGGER.info("Moving staging vehicle services to target...") + moveStagingVehicleServicesToTarget( + stagingFrame = stagingVehicleScheduleFrame, + targetFrame = targetVehicleScheduleFrame + ) + + LOGGER.info("Deleting the empty staging frame...") + deleteStagingVehicleScheduleFrame(stagingVehicleScheduleFrameId) + + return targetVehicleScheduleFrame.vehicleScheduleFrameId!! // ID of an existing row, can never be null. + } + + private fun fetchTargetVehicleScheduleFrame( + stagingVehicleScheduleFrame: VehicleScheduleFrame, + targetPriority: TimetablesPriority + ): VehicleScheduleFrame { + // ID of an existing row, can never be null. + val stagingVehicleScheduleFrameId = stagingVehicleScheduleFrame.vehicleScheduleFrameId!! + val targetVehicleScheduleFrames = vehicleScheduleFrameRepository .fetchTargetVehicleScheduleFrames(stagingVehicleScheduleFrame, targetPriority) LOGGER.info { "Found ${targetVehicleScheduleFrames.size} target vehicle schedule frames." } @@ -61,16 +82,7 @@ class CombineTimetablesService( ) } - LOGGER.info("Moving staging vehicle services to target...") - moveStagingVehicleServicesToTarget( - stagingFrame = stagingVehicleScheduleFrame, - targetFrame = targetVehicleScheduleFrame - ) - - LOGGER.info("Deleting the empty staging frame...") - deleteStagingVehicleScheduleFrame(stagingVehicleScheduleFrameId) - - return targetVehicleScheduleFrame.vehicleScheduleFrameId!! // ID of an existing row, can never be null. + return targetVehicleScheduleFrame } private fun fetchStagingVehicleScheduleFrame(stagingVehicleScheduleFrameId: UUID): VehicleScheduleFrame { From 4529229313f454a1ab80f56727852a7af4069bf5 Mon Sep 17 00:00:00 2001 From: Tommi Leinamo Date: Mon, 4 Dec 2023 09:55:04 +0200 Subject: [PATCH 4/6] Add API for fetching /combine target vehicle schedule frame Similar to the /to-replace API (from which this is largely copypasted) that already existed for /replace. --- README.md | 12 ++ .../timetables/api/TimetablesController.kt | 32 +++++ .../timetables/config/WebSecurityConfig.kt | 1 + .../service/CombineTimetablesService.kt | 9 ++ .../api/TimetablesToCombineApiTest.kt | 119 ++++++++++++++++++ 5 files changed, 173 insertions(+) create mode 100644 src/test/kotlin/fi/hsl/jore4/timetables/api/TimetablesToCombineApiTest.kt diff --git a/README.md b/README.md index 5c3ff04..733b4c0 100644 --- a/README.md +++ b/README.md @@ -107,6 +107,18 @@ When the submodule is updated, to get the newest version of inserter you need to ] } ``` +- `GET /timetables/to-combine`: Fetch the vehicle schedule frame ID slated to be the target for combine, + considering the combine action with the staging vehicle schedule frame ID and target priority. + - Request params: + - `stagingVehicleScheduleFrameId` The ID of the staging vehicle schedule frame. Example: `"50f939b0-aac3-453a-b2f5-24c0cdf8ad21"` + - `targetPriority` The priority to which the staging timetables will be promoted. Example: `10` + + Example response body: + ```JSON + { + "toCombineTargetVehicleScheduleFrameId": "d3d0aea6-db3f-4421-b4eb-39cffe8835a8" + } + ``` ## Technical Documentation jore4-timetables-api is a Spring Boot application written in Kotlin, which implements a REST API for accessing the timetables database and creating more complicated updates in one transaction than is possible with the graphQL interface. diff --git a/src/main/kotlin/fi/hsl/jore4/timetables/api/TimetablesController.kt b/src/main/kotlin/fi/hsl/jore4/timetables/api/TimetablesController.kt index 5e631b6..90453c8 100644 --- a/src/main/kotlin/fi/hsl/jore4/timetables/api/TimetablesController.kt +++ b/src/main/kotlin/fi/hsl/jore4/timetables/api/TimetablesController.kt @@ -92,6 +92,10 @@ class TimetablesController( val toReplaceVehicleScheduleFrameIds: List ) + data class ToCombineTimetablesResponseBody( + val toCombineTargetVehicleScheduleFrameId: UUID + ) + class TargetPriorityParsingException(message: String, val targetPriority: Int) : RuntimeException(message) @GetMapping("to-replace") @@ -117,6 +121,34 @@ class TimetablesController( .body(ToReplaceTimetablesResponseBody(toReplaceVehicleScheduleFrameIds = vehicleScheduleFrameIds)) } + @GetMapping("to-combine") + fun getTargetFrameIdsForCombine( + @RequestParam + targetPriority: Int, + @RequestParam + stagingVehicleScheduleFrameId: UUID + ): ResponseEntity { + LOGGER.info { "ToCombine api, stagingVehicleScheduleFrameId: $stagingVehicleScheduleFrameId, targetPriority: $targetPriority" } + + val targetPriorityEnumResult = runCatching { TimetablesPriority.fromInt(targetPriority) } + if (targetPriorityEnumResult.isFailure) { + throw TargetPriorityParsingException("Failed to parse target priority", targetPriority) + } + + val targetVehicleScheduleFrame = combineTimetablesService.fetchTargetVehicleScheduleFrame( + stagingVehicleScheduleFrameId, + targetPriorityEnumResult.getOrThrow() + ) + + return ResponseEntity.status(HttpStatus.OK) + .body( + ToCombineTimetablesResponseBody( + // ID of an existing row, can never be null. + toCombineTargetVehicleScheduleFrameId = targetVehicleScheduleFrame.vehicleScheduleFrameId!! + ) + ) + } + @ExceptionHandler(RuntimeException::class) fun handleRuntimeException(ex: RuntimeException): ResponseEntity { val errorExtensions: JoreErrorExtensions = when (ex) { diff --git a/src/main/kotlin/fi/hsl/jore4/timetables/config/WebSecurityConfig.kt b/src/main/kotlin/fi/hsl/jore4/timetables/config/WebSecurityConfig.kt index a276444..eddc537 100644 --- a/src/main/kotlin/fi/hsl/jore4/timetables/config/WebSecurityConfig.kt +++ b/src/main/kotlin/fi/hsl/jore4/timetables/config/WebSecurityConfig.kt @@ -25,6 +25,7 @@ class WebSecurityConfig { HttpMethod.GET, "/actuator/health", "/error", + "/timetables/to-combine", "/timetables/to-replace" ) .permitAll() diff --git a/src/main/kotlin/fi/hsl/jore4/timetables/service/CombineTimetablesService.kt b/src/main/kotlin/fi/hsl/jore4/timetables/service/CombineTimetablesService.kt index b02afd2..24aa7ae 100644 --- a/src/main/kotlin/fi/hsl/jore4/timetables/service/CombineTimetablesService.kt +++ b/src/main/kotlin/fi/hsl/jore4/timetables/service/CombineTimetablesService.kt @@ -58,6 +58,15 @@ class CombineTimetablesService( return targetVehicleScheduleFrame.vehicleScheduleFrameId!! // ID of an existing row, can never be null. } + @Transactional(readOnly = true) + fun fetchTargetVehicleScheduleFrame( + stagingVehicleScheduleFrameId: UUID, + targetPriority: TimetablesPriority + ): VehicleScheduleFrame { + val stagingVehicleScheduleFrame = fetchStagingVehicleScheduleFrame(stagingVehicleScheduleFrameId) + return fetchTargetVehicleScheduleFrame(stagingVehicleScheduleFrame, targetPriority) + } + private fun fetchTargetVehicleScheduleFrame( stagingVehicleScheduleFrame: VehicleScheduleFrame, targetPriority: TimetablesPriority diff --git a/src/test/kotlin/fi/hsl/jore4/timetables/api/TimetablesToCombineApiTest.kt b/src/test/kotlin/fi/hsl/jore4/timetables/api/TimetablesToCombineApiTest.kt new file mode 100644 index 0000000..a01f22d --- /dev/null +++ b/src/test/kotlin/fi/hsl/jore4/timetables/api/TimetablesToCombineApiTest.kt @@ -0,0 +1,119 @@ +package fi.hsl.jore4.timetables.api + +import com.ninjasquad.springmockk.MockkBean +import fi.hsl.jore.jore4.jooq.vehicle_schedule.tables.pojos.VehicleScheduleFrame +import fi.hsl.jore4.timetables.enumerated.TimetablesPriority +import fi.hsl.jore4.timetables.service.CombineTimetablesService +import io.mockk.every +import io.mockk.junit5.MockKExtension +import io.mockk.verify +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.http.MediaType +import org.springframework.test.context.ActiveProfiles +import org.springframework.test.web.servlet.MockMvc +import org.springframework.test.web.servlet.ResultActions +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.content +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status +import java.time.LocalDate +import java.util.UUID + +@ExtendWith(MockKExtension::class) +@AutoConfigureMockMvc +@SpringBootTest +@ActiveProfiles("test") +class TimetablesToCombineApiTest(@Autowired val mockMvc: MockMvc) { + @MockkBean + private lateinit var combineTimetablesService: CombineTimetablesService + + private val defaultTargetFrame = VehicleScheduleFrame( + vehicleScheduleFrameId = UUID.fromString("379076ee-d595-47e3-8050-2610d594b57c"), + validityStart = LocalDate.now(), + validityEnd = LocalDate.now(), + priority = 20, + label = "label" + ) + private val defaultToCombineTargetId = defaultTargetFrame.vehicleScheduleFrameId + + private fun executeToCombineTimetablesRequest( + stagingFrameId: UUID, + targetPriority: Int + ): ResultActions { + return mockMvc.perform( + MockMvcRequestBuilders.get("/timetables/to-combine") + .contentType(MediaType.APPLICATION_JSON) + .param("stagingVehicleScheduleFrameId", stagingFrameId.toString()) + .param("targetPriority", targetPriority.toString()) + ) + } + + @Test + fun `returns 200 and correct response when called successfully`() { + val stagingVehicleScheduleFrameId = UUID.fromString("81f109d1-dbe2-412a-996e-aa510416b2e4") + val targetPriority = TimetablesPriority.STANDARD + + every { + combineTimetablesService.fetchTargetVehicleScheduleFrame( + stagingVehicleScheduleFrameId, + targetPriority + ) + } answers { defaultTargetFrame } + + executeToCombineTimetablesRequest(stagingVehicleScheduleFrameId, targetPriority.value) + .andExpect(status().isOk) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect( + content().json( + """ + { + "toCombineTargetVehicleScheduleFrameId": $defaultToCombineTargetId + } + """.trimIndent(), + true + ) + ) + + verify(exactly = 1) { + combineTimetablesService.fetchTargetVehicleScheduleFrame( + stagingVehicleScheduleFrameId, + targetPriority + ) + } + } + + @Test + fun `throws a 400 error when parsing target priority fails`() { + val errorMessage = "Failed to parse target priority" + val stagingVehicleScheduleFrameId = UUID.fromString("023281cd-51e9-4544-a2af-7b7e268e3a3a") + val invalidTargetPriorityInput = 9999 + + executeToCombineTimetablesRequest(stagingVehicleScheduleFrameId, invalidTargetPriorityInput) + .andExpect(status().isBadRequest) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect( + content().json( + """ + { + "message": "$errorMessage", + "extensions": { + "code": 400, + "type": "TargetPriorityParsingError", + "targetPriority": $invalidTargetPriorityInput + } + } + """.trimIndent(), + true + ) + ) + verify(exactly = 0) { + combineTimetablesService.fetchTargetVehicleScheduleFrame( + stagingVehicleScheduleFrameId, + any() + ) + } + } +} From 83ffb319c3522ddd3fd6a082701890f8e33583c6 Mon Sep 17 00:00:00 2001 From: Tommi Leinamo Date: Thu, 14 Dec 2023 09:21:13 +0200 Subject: [PATCH 5/6] Refactor to improve code style These were accidentally left out from a previous PR, where the relevant commit was. --- .../util/TransactionSystemExtensionsTest.kt | 5 ++--- .../service/CombineTimetablesServiceTest.kt | 18 +++++++++--------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/test/kotlin/fi/hsl/jore4/timetables/api/util/TransactionSystemExtensionsTest.kt b/src/test/kotlin/fi/hsl/jore4/timetables/api/util/TransactionSystemExtensionsTest.kt index 6af7763..8a714b6 100644 --- a/src/test/kotlin/fi/hsl/jore4/timetables/api/util/TransactionSystemExtensionsTest.kt +++ b/src/test/kotlin/fi/hsl/jore4/timetables/api/util/TransactionSystemExtensionsTest.kt @@ -6,9 +6,8 @@ import kotlin.test.assertEquals class TransactionSystemExtensionsTest { - private fun createTransactionSystemExceptionWithCause(message: String): TransactionSystemException { - return TransactionSystemException("test exception", Exception(message)) - } + private fun createTransactionSystemExceptionWithCause(message: String) = + TransactionSystemException("test exception", Exception(message)) @Test fun `resolves PassingTimeStopPointMatchingOrderError`() { diff --git a/src/test/kotlin/fi/hsl/jore4/timetables/service/CombineTimetablesServiceTest.kt b/src/test/kotlin/fi/hsl/jore4/timetables/service/CombineTimetablesServiceTest.kt index 6c4a38f..6c9b7db 100644 --- a/src/test/kotlin/fi/hsl/jore4/timetables/service/CombineTimetablesServiceTest.kt +++ b/src/test/kotlin/fi/hsl/jore4/timetables/service/CombineTimetablesServiceTest.kt @@ -352,20 +352,20 @@ class CombineTimetablesServiceTest @Autowired constructor( timetablesDataInserterRunner.truncateAndInsertDataset(testData.toJSONString()) val stagingFrameId = UUID.fromString("e8d07c0d-575f-4cbe-bddb-ead5b2943638") - val exception = assertFailsWith { + assertFailsWith { combineTimetablesService.combineTimetables( listOf(stagingFrameId), TimetablesPriority.STANDARD ) + }.also { exception -> + // Check that the error messages are somewhat in the format we expect. TransactionSystemExtensions depends on these. + assertEquals(exception.message, "JDBC commit failed") + val causeMessage = exception?.cause?.message + assertNotNull(causeMessage) + assertContains(causeMessage, "ERROR: conflicting schedules detected: vehicle schedule frame") + assertContains(causeMessage, "Where: PL/pgSQL function vehicle_schedule.validate_queued_schedules_uniqueness()") + assertContains(causeMessage, "SQL statement \"SELECT vehicle_schedule.validate_queued_schedules_uniqueness()") } - - // Check that the error messages are somewhat in the format we expect. TransactionSystemExtensions depends on these. - assertEquals(exception.message, "JDBC commit failed") - val causeMessage = exception?.cause?.message - assertNotNull(causeMessage) - assertContains(causeMessage, "ERROR: conflicting schedules detected: vehicle schedule frame") - assertContains(causeMessage, "Where: PL/pgSQL function vehicle_schedule.validate_queued_schedules_uniqueness()") - assertContains(causeMessage, "SQL statement \"SELECT vehicle_schedule.validate_queued_schedules_uniqueness()") } } From a4540263daeca7abcbcfb81b0bf9e79df5244c85 Mon Sep 17 00:00:00 2001 From: Tommi Leinamo Date: Thu, 14 Dec 2023 12:34:49 +0200 Subject: [PATCH 6/6] Refactor TimetablesPriority.fromInt usage --- .../timetables/api/TimetablesController.kt | 25 ++++++++----------- .../enumerated/TimetablesPriority.kt | 4 +-- .../api/TimetablesToReplaceApiTest.kt | 8 +++--- 3 files changed, 16 insertions(+), 21 deletions(-) diff --git a/src/main/kotlin/fi/hsl/jore4/timetables/api/TimetablesController.kt b/src/main/kotlin/fi/hsl/jore4/timetables/api/TimetablesController.kt index 90453c8..06ca37b 100644 --- a/src/main/kotlin/fi/hsl/jore4/timetables/api/TimetablesController.kt +++ b/src/main/kotlin/fi/hsl/jore4/timetables/api/TimetablesController.kt @@ -45,7 +45,7 @@ class TimetablesController( ) { @AssertTrue(message = "false") - fun isTargetPriorityValid(): Boolean = runCatching { TimetablesPriority.fromInt(targetPriority) }.isSuccess + fun isTargetPriorityValid(): Boolean = TimetablesPriority.fromInt(targetPriority) != null } data class CombineTimetablesResponseBody( @@ -60,7 +60,7 @@ class TimetablesController( LOGGER.debug { "Combine api, request: $requestBody" } val combineResult = combineTimetablesService.combineTimetables( requestBody.stagingVehicleScheduleFrameIds, - TimetablesPriority.fromInt(requestBody.targetPriority) + parseTargetPriority(requestBody.targetPriority) ) return ResponseEntity @@ -80,7 +80,7 @@ class TimetablesController( LOGGER.debug { "Replace api, request: $requestBody" } val replaceResult = replaceTimetablesService.replaceTimetables( requestBody.stagingVehicleScheduleFrameIds, - TimetablesPriority.fromInt(requestBody.targetPriority) + parseTargetPriority(requestBody.targetPriority) ) return ResponseEntity @@ -107,14 +107,9 @@ class TimetablesController( ): ResponseEntity { LOGGER.info { "ToReplace api, stagingVehicleScheduleFrameId: $stagingVehicleScheduleFrameId, targetPriority: $targetPriority" } - val targetPriorityEnumResult = runCatching { TimetablesPriority.fromInt(targetPriority) } - if (targetPriorityEnumResult.isFailure) { - throw TargetPriorityParsingException("Failed to parse target priority", targetPriority) - } - val vehicleScheduleFrameIds = replaceTimetablesService.fetchVehicleScheduleFramesToReplace( stagingVehicleScheduleFrameId, - targetPriorityEnumResult.getOrThrow() + parseTargetPriority(targetPriority) ).mapNotNull { it.vehicleScheduleFrameId } return ResponseEntity.status(HttpStatus.OK) @@ -130,14 +125,9 @@ class TimetablesController( ): ResponseEntity { LOGGER.info { "ToCombine api, stagingVehicleScheduleFrameId: $stagingVehicleScheduleFrameId, targetPriority: $targetPriority" } - val targetPriorityEnumResult = runCatching { TimetablesPriority.fromInt(targetPriority) } - if (targetPriorityEnumResult.isFailure) { - throw TargetPriorityParsingException("Failed to parse target priority", targetPriority) - } - val targetVehicleScheduleFrame = combineTimetablesService.fetchTargetVehicleScheduleFrame( stagingVehicleScheduleFrameId, - targetPriorityEnumResult.getOrThrow() + parseTargetPriority(targetPriority) ) return ResponseEntity.status(HttpStatus.OK) @@ -187,4 +177,9 @@ class TimetablesController( return ResponseEntity(JoreErrorResponse(ex.message, errorExtensions), httpStatus) } + + companion object { + private fun parseTargetPriority(targetPriority: Int) = TimetablesPriority.fromInt(targetPriority) + ?: throw TargetPriorityParsingException("Failed to parse target priority", targetPriority) + } } diff --git a/src/main/kotlin/fi/hsl/jore4/timetables/enumerated/TimetablesPriority.kt b/src/main/kotlin/fi/hsl/jore4/timetables/enumerated/TimetablesPriority.kt index ccf9cce..c184b43 100644 --- a/src/main/kotlin/fi/hsl/jore4/timetables/enumerated/TimetablesPriority.kt +++ b/src/main/kotlin/fi/hsl/jore4/timetables/enumerated/TimetablesPriority.kt @@ -9,8 +9,8 @@ enum class TimetablesPriority(val value: Int) { STAGING(40); companion object { - fun fromInt(value: Int): TimetablesPriority { - return values().first { it.value == value } + fun fromInt(value: Int): TimetablesPriority? { + return values().firstOrNull { it.value == value } } } } diff --git a/src/test/kotlin/fi/hsl/jore4/timetables/api/TimetablesToReplaceApiTest.kt b/src/test/kotlin/fi/hsl/jore4/timetables/api/TimetablesToReplaceApiTest.kt index 8e27054..a5ef95d 100644 --- a/src/test/kotlin/fi/hsl/jore4/timetables/api/TimetablesToReplaceApiTest.kt +++ b/src/test/kotlin/fi/hsl/jore4/timetables/api/TimetablesToReplaceApiTest.kt @@ -62,16 +62,16 @@ class TimetablesToReplaceApiTest(@Autowired val mockMvc: MockMvc) { @Test fun `returns 200 and correct response when called successfully`() { val stagingVehicleScheduleFrameId = UUID.fromString("9e758776-2af1-49c7-8bd0-c0805b833b20") - val targetPriority = 10 + val targetPriority = TimetablesPriority.STANDARD every { replaceTimetablesService.fetchVehicleScheduleFramesToReplace( stagingVehicleScheduleFrameId, - TimetablesPriority.fromInt(targetPriority) + targetPriority ) } answers { defaultVehicleScheduleFrames } - executeToReplaceTimetablesRequest(stagingVehicleScheduleFrameId, targetPriority) + executeToReplaceTimetablesRequest(stagingVehicleScheduleFrameId, targetPriority.value) .andExpect(status().isOk) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect( @@ -88,7 +88,7 @@ class TimetablesToReplaceApiTest(@Autowired val mockMvc: MockMvc) { verify(exactly = 1) { replaceTimetablesService.fetchVehicleScheduleFramesToReplace( stagingVehicleScheduleFrameId, - TimetablesPriority.fromInt(targetPriority) + targetPriority ) } }