From 7fbd270019a9b242e9e5be7b12e34e75adf984ed Mon Sep 17 00:00:00 2001 From: Jim Martens Date: Sat, 6 Jan 2024 14:06:43 +0100 Subject: [PATCH] feat: Support CRUD functionality (#7) The station search implementation only works via DB and is very slow. For a simple query, the answer takes about 1s or longer. That is 100 times slower than it should be. For now, however, this solution is adequate to achieve a first prototype that includes the core functionality. feat: Add station search --- .../kotlin/twomartens.java-preview.gradle.kts | 4 +- .../main/kotlin/twomartens.java.gradle.kts | 4 +- .../src/main/kotlin/twomartens.jib.gradle.kts | 4 +- .../main/kotlin/twomartens.kotlin.gradle.kts | 2 +- .../twomartens.nebula-release.gradle.kts | 6 +- ...artens.spring-boot-application.gradle.kts} | 19 +- .../twomartens.spring-boot-base.gradle.kts | 25 ++ ....spring-boot-cloud-application.gradle.kts} | 4 +- ...omartens.spring-boot-cloud-base.gradle.kts | 11 + gradle.properties | 1 + module-bahnApi/bahnApi.gradle.kts | 23 ++ .../ThreadPoolTaskExecutorConfig.kt | 0 .../ThreadPoolTaskSchedulerConfig.kt | 2 +- .../events/ScheduledTasksCreatedEvent.kt | 6 +- .../bahnApi/jaxb/LocalDateTimeAdapter.kt | 0 .../bahnApi/mapper/BahnStationMapper.kt | 15 + .../bahnApi/mapper/BahnTimetableMapper.kt | 0 .../twomartens/timetable/bahnApi/model/Eva.kt | 6 +- .../timetable/bahnApi/model/FetchDates.kt | 0 .../timetable/bahnApi/model/TaskFactory.kt | 0 .../timetable/bahnApi/model/db/BahnStation.kt | 0 .../bahnApi/model/db/BahnTimetable.kt | 0 .../bahnApi/model/db/ScheduledFetchTask.kt | 2 +- .../bahnApi/model/dto/BahnStation.kt | 0 .../bahnApi/model/dto/BahnStationStop.kt | 0 .../bahnApi/model/dto/BahnStations.kt | 4 +- .../bahnApi/model/dto/BahnStopEvent.kt | 0 .../bahnApi/model/dto/BahnTimetable.kt | 0 .../bahnApi/model/dto/BahnTripLabel.kt | 0 .../bahnApi/model/dto/BahnTripType.kt | 0 .../bahnApi/property/BahnApiProperties.kt | 0 .../repository/BahnStationRepository.kt | 0 .../repository/BahnTimetableRepository.kt | 0 .../ScheduledFetchTaskRepository.kt | 0 .../bahnApi/service/BahnApiService.kt | 51 ++++ .../bahnApi/service/BahnDatabaseService.kt | 97 +++++++ .../bahnApi/service/FetchTaskScheduler.kt | 2 +- .../bahnApi/service/ScheduledTaskService.kt | 12 +- .../bahnApi/tasks/DeleteScheduledTask.kt | 0 .../bahnApi/tasks/FetchTimetableTask.kt | 4 +- .../bahnApi/tasks/StoreTimetableTask.kt | 0 .../de/twomartens/timetable/types/Email.kt | 11 + .../timetable/types/ZeroOrPositiveInteger.kt | 10 + module-model/model.gradle.kts | 14 + .../timetable/model/common/CoachCapacity.kt | 6 + .../timetable/model/common/CountryCode.kt | 0 .../timetable/model/common/DepotId.kt | 12 + .../timetable/model/common/FormationId.kt | 12 + .../timetable/model/common/LanguageCode.kt | 0 .../twomartens/timetable/model/common/Line.kt | 0 .../timetable/model/common/Platform.kt | 0 .../timetable/model/common/PortalId.kt | 12 + .../timetable/model/common/RotationId.kt | 0 .../timetable/model/common/RouteId.kt | 12 + .../timetable/model/common/Section.kt | 0 .../timetable/model/common/ServiceId.kt | 0 .../timetable/model/common/StationId.kt | 3 +- .../timetable/model/common/TimetableId.kt | 12 + .../timetable/model/common/TrackId.kt | 0 .../timetable/model/common/UserId.kt | 0 .../de/twomartens/timetable/model/db/Depot.kt | 14 + .../timetable/model/db/Formation.kt | 6 +- .../twomartens/timetable/model/db/Portal.kt | 12 + .../twomartens/timetable/model/db/Station.kt | 16 +- .../timetable/model/db/Timetable.kt | 39 +++ .../timetable/model/db/TravelDuration.kt | 8 + .../twomartens/timetable/model/db/TswRoute.kt | 35 +++ .../de/twomartens/timetable/model/db/User.kt | 7 +- .../timetable/model/dto/AdditionalService.kt | 9 +- .../model/dto/AdditionalServiceStop.kt | 3 +- .../twomartens/timetable/model/dto/Country.kt | 6 + .../twomartens/timetable/model/dto/Depot.kt | 9 + .../timetable/model/dto/Direction.kt | 0 .../timetable/model/dto/Formation.kt | 9 + .../twomartens/timetable/model/dto/Portal.kt | 8 + .../timetable/model/dto/Rotation.kt | 11 + .../model/dto/ServiceFormationStage.kt | 11 + .../model/dto/ServiceLinkingStage.kt | 10 +- .../model/dto/ServiceRotationStage.kt | 15 + .../timetable/model/dto/ServiceStop.kt | 4 +- .../twomartens/timetable/model/dto/Station.kt | 9 + .../timetable/model/dto/Timetable.kt | 13 + .../timetable/model/dto/TimetableState.kt | 0 .../twomartens/timetable/model/dto/Track.kt | 7 + .../timetable/model/dto/TravelDuration.kt | 6 + .../timetable/model/dto/TswRoute.kt | 13 + .../de/twomartens/timetable/model/dto/User.kt | 7 + .../timetable/model/mapper/StationMapper.kt | 36 +++ .../model/repository/StationRepository.kt | 30 ++ module-server/server.gradle.kts | 12 +- .../twomartens/timetable/MainApplication.kt | 4 +- .../timetable/auth/UserController.kt | 84 ++++++ .../twomartens/timetable/auth/UserMapper.kt | 34 +++ .../timetable/auth/UserRepository.kt | 11 + .../bahnApi/service/BahnApiService.kt | 58 ---- .../bahnApi/service/BahnDatabaseService.kt | 29 -- .../configuration/BusConfiguration.kt | 4 +- .../configuration/OpenApiConfiguration.kt | 2 +- .../configuration/PropertyConfiguration.kt | 8 +- .../configuration/WebConfiguration.kt | 7 +- .../configuration/WebSecurityConfiguration.kt | 10 +- .../controller/TimetableController.kt | 59 ---- .../formation/FormationController.kt | 261 ++++++++++++++++++ .../timetable/formation/FormationMapper.kt | 49 ++++ .../formation/FormationRepository.kt | 13 + .../timetable/model/common/CoachCapacity.kt | 6 - .../timetable/model/common/Depot.kt | 11 - .../timetable/model/common/FormationId.kt | 14 - .../timetable/model/common/Portal.kt | 10 - .../twomartens/timetable/model/db/Station.kt | 27 -- .../twomartens/timetable/model/dto/Country.kt | 9 - .../timetable/model/dto/Formation.kt | 11 - .../timetable/model/dto/Rotation.kt | 14 - .../model/dto/ServiceFormationStage.kt | 15 - .../model/dto/ServiceRotationStage.kt | 16 -- .../twomartens/timetable/model/dto/Station.kt | 9 - .../timetable/model/dto/Timetable.kt | 13 - .../timetable/model/dto/TswRoute.kt | 13 - .../timetable/property/ServiceProperties.kt | 1 - .../timetable/route/RouteController.kt | 220 +++++++++++++++ .../twomartens/timetable/route/RouteMapper.kt | 114 ++++++++ .../TswRouteRepository.kt | 6 +- .../timetable/station/StationController.kt | 97 +++++++ .../support/model/dto/ErrorMessage.kt | 3 - .../statusprobe/StatusProbeCriticality.kt | 7 - .../timetable/TimetableController.kt | 259 +++++++++++++++++ .../timetable/timetable/TimetableMapper.kt | 46 +++ .../timetable/TimetableRepository.kt | 13 + .../src/main/resources/application.yaml | 7 +- module-server/src/main/resources/log4j2.xml | 1 + .../configuration/ClockConfiguration.kt | 4 +- .../configuration/FilterConfiguration.kt | 2 +- .../configuration/InterceptorConfiguration.kt | 6 +- .../configuration/RestClientConfiguration.kt | 24 ++ .../RestTemplateConfiguration.kt | 8 +- .../configuration/StatusProbeConfiguration.kt | 10 +- .../support/controller/ExceptionController.kt | 6 +- .../controller}/HealthCheckController.kt | 8 +- .../controller/VersionHtmlController.kt | 2 +- .../support/exception/HttpStatusException.kt | 2 +- .../BufferingClientHttpResponseWrapper.kt | 8 +- .../support/interceptor/HeaderInterceptor.kt | 2 +- .../interceptor/HeaderInterceptorRest.kt | 2 +- .../interceptor/LoggingInterceptorRest.kt | 5 +- .../support/model/LeadershipStatus.kt | 2 +- .../support/model/dto/ErrorMessage.kt | 3 + .../actuator/AbstractHealthIndicator.kt | 2 +- .../AbstractStatusProbeHealthIndicator.kt | 4 +- .../support/monitoring/actuator/Preparable.kt | 2 +- .../actuator/RestHealthIndicator.kt | 10 +- .../statusprobe/CountBasedStatusProbe.kt | 2 +- .../statusprobe/PercentageBasedStatusProbe.kt | 2 +- .../statusprobe/ScheduledStatusProbe.kt | 2 +- .../monitoring/statusprobe/StatusProbe.kt | 2 +- .../statusprobe/StatusProbeCriticality.kt | 7 + .../statusprobe/StatusProbeLogger.kt | 2 +- .../statusprobe/TimeBasedStatusProbe.kt | 2 +- .../support/property/HealthCheckProperties.kt | 10 + .../property/RestTemplateTimeoutProperties.kt | 2 +- .../support/property/StatusProbeProperties.kt | 2 +- .../support/property/TimeProperties.kt | 2 +- .../support/security/SpringPolicyEnforcer.kt | 2 +- .../security/SpringPolicyEnforcerFilter.kt | 2 +- .../twomartens}/support/service/BusService.kt | 2 +- module-support/support.gradle.kts | 15 + settings.gradle.kts | 5 +- 166 files changed, 2039 insertions(+), 482 deletions(-) rename buildSrc/src/main/kotlin/{twomartens.spring-boot.gradle.kts => twomartens.spring-boot-application.gradle.kts} (85%) create mode 100644 buildSrc/src/main/kotlin/twomartens.spring-boot-base.gradle.kts rename buildSrc/src/main/kotlin/{twomartens.spring-boot-cloud.gradle.kts => twomartens.spring-boot-cloud-application.gradle.kts} (93%) create mode 100644 buildSrc/src/main/kotlin/twomartens.spring-boot-cloud-base.gradle.kts create mode 100644 module-bahnApi/bahnApi.gradle.kts rename {module-server => module-bahnApi}/src/main/kotlin/de/twomartens/timetable/bahnApi/configuration/ThreadPoolTaskExecutorConfig.kt (100%) rename {module-server => module-bahnApi}/src/main/kotlin/de/twomartens/timetable/bahnApi/configuration/ThreadPoolTaskSchedulerConfig.kt (90%) rename {module-server => module-bahnApi}/src/main/kotlin/de/twomartens/timetable/bahnApi/events/ScheduledTasksCreatedEvent.kt (91%) rename {module-server => module-bahnApi}/src/main/kotlin/de/twomartens/timetable/bahnApi/jaxb/LocalDateTimeAdapter.kt (100%) rename {module-server => module-bahnApi}/src/main/kotlin/de/twomartens/timetable/bahnApi/mapper/BahnStationMapper.kt (57%) rename {module-server => module-bahnApi}/src/main/kotlin/de/twomartens/timetable/bahnApi/mapper/BahnTimetableMapper.kt (100%) rename {module-server => module-bahnApi}/src/main/kotlin/de/twomartens/timetable/bahnApi/model/Eva.kt (70%) rename {module-server => module-bahnApi}/src/main/kotlin/de/twomartens/timetable/bahnApi/model/FetchDates.kt (100%) rename {module-server => module-bahnApi}/src/main/kotlin/de/twomartens/timetable/bahnApi/model/TaskFactory.kt (100%) rename {module-server => module-bahnApi}/src/main/kotlin/de/twomartens/timetable/bahnApi/model/db/BahnStation.kt (100%) rename {module-server => module-bahnApi}/src/main/kotlin/de/twomartens/timetable/bahnApi/model/db/BahnTimetable.kt (100%) rename {module-server => module-bahnApi}/src/main/kotlin/de/twomartens/timetable/bahnApi/model/db/ScheduledFetchTask.kt (92%) rename {module-server => module-bahnApi}/src/main/kotlin/de/twomartens/timetable/bahnApi/model/dto/BahnStation.kt (100%) rename {module-server => module-bahnApi}/src/main/kotlin/de/twomartens/timetable/bahnApi/model/dto/BahnStationStop.kt (100%) rename {module-server => module-bahnApi}/src/main/kotlin/de/twomartens/timetable/bahnApi/model/dto/BahnStations.kt (73%) rename {module-server => module-bahnApi}/src/main/kotlin/de/twomartens/timetable/bahnApi/model/dto/BahnStopEvent.kt (100%) rename {module-server => module-bahnApi}/src/main/kotlin/de/twomartens/timetable/bahnApi/model/dto/BahnTimetable.kt (100%) rename {module-server => module-bahnApi}/src/main/kotlin/de/twomartens/timetable/bahnApi/model/dto/BahnTripLabel.kt (100%) rename {module-server => module-bahnApi}/src/main/kotlin/de/twomartens/timetable/bahnApi/model/dto/BahnTripType.kt (100%) rename {module-server => module-bahnApi}/src/main/kotlin/de/twomartens/timetable/bahnApi/property/BahnApiProperties.kt (100%) rename {module-server => module-bahnApi}/src/main/kotlin/de/twomartens/timetable/bahnApi/repository/BahnStationRepository.kt (100%) rename {module-server => module-bahnApi}/src/main/kotlin/de/twomartens/timetable/bahnApi/repository/BahnTimetableRepository.kt (100%) rename {module-server => module-bahnApi}/src/main/kotlin/de/twomartens/timetable/bahnApi/repository/ScheduledFetchTaskRepository.kt (100%) create mode 100644 module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/service/BahnApiService.kt create mode 100644 module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/service/BahnDatabaseService.kt rename module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/service/TaskScheduler.kt => module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/service/FetchTaskScheduler.kt (99%) rename {module-server => module-bahnApi}/src/main/kotlin/de/twomartens/timetable/bahnApi/service/ScheduledTaskService.kt (94%) rename {module-server => module-bahnApi}/src/main/kotlin/de/twomartens/timetable/bahnApi/tasks/DeleteScheduledTask.kt (100%) rename {module-server => module-bahnApi}/src/main/kotlin/de/twomartens/timetable/bahnApi/tasks/FetchTimetableTask.kt (86%) rename {module-server => module-bahnApi}/src/main/kotlin/de/twomartens/timetable/bahnApi/tasks/StoreTimetableTask.kt (100%) create mode 100644 module-common/src/main/kotlin/de/twomartens/timetable/types/Email.kt create mode 100644 module-common/src/main/kotlin/de/twomartens/timetable/types/ZeroOrPositiveInteger.kt create mode 100644 module-model/model.gradle.kts create mode 100644 module-model/src/main/kotlin/de/twomartens/timetable/model/common/CoachCapacity.kt rename {module-server => module-model}/src/main/kotlin/de/twomartens/timetable/model/common/CountryCode.kt (100%) create mode 100644 module-model/src/main/kotlin/de/twomartens/timetable/model/common/DepotId.kt create mode 100644 module-model/src/main/kotlin/de/twomartens/timetable/model/common/FormationId.kt rename {module-server => module-model}/src/main/kotlin/de/twomartens/timetable/model/common/LanguageCode.kt (100%) rename {module-server => module-model}/src/main/kotlin/de/twomartens/timetable/model/common/Line.kt (100%) rename {module-server => module-model}/src/main/kotlin/de/twomartens/timetable/model/common/Platform.kt (100%) create mode 100644 module-model/src/main/kotlin/de/twomartens/timetable/model/common/PortalId.kt rename {module-server => module-model}/src/main/kotlin/de/twomartens/timetable/model/common/RotationId.kt (100%) create mode 100644 module-model/src/main/kotlin/de/twomartens/timetable/model/common/RouteId.kt rename {module-server => module-model}/src/main/kotlin/de/twomartens/timetable/model/common/Section.kt (100%) rename {module-server => module-model}/src/main/kotlin/de/twomartens/timetable/model/common/ServiceId.kt (100%) rename {module-server => module-model}/src/main/kotlin/de/twomartens/timetable/model/common/StationId.kt (91%) create mode 100644 module-model/src/main/kotlin/de/twomartens/timetable/model/common/TimetableId.kt rename {module-server => module-model}/src/main/kotlin/de/twomartens/timetable/model/common/TrackId.kt (100%) rename {module-server => module-model}/src/main/kotlin/de/twomartens/timetable/model/common/UserId.kt (100%) create mode 100644 module-model/src/main/kotlin/de/twomartens/timetable/model/db/Depot.kt rename {module-server => module-model}/src/main/kotlin/de/twomartens/timetable/model/db/Formation.kt (83%) create mode 100644 module-model/src/main/kotlin/de/twomartens/timetable/model/db/Portal.kt rename module-server/src/main/kotlin/de/twomartens/timetable/model/db/TswRoute.kt => module-model/src/main/kotlin/de/twomartens/timetable/model/db/Station.kt (67%) create mode 100644 module-model/src/main/kotlin/de/twomartens/timetable/model/db/Timetable.kt create mode 100644 module-model/src/main/kotlin/de/twomartens/timetable/model/db/TravelDuration.kt create mode 100644 module-model/src/main/kotlin/de/twomartens/timetable/model/db/TswRoute.kt rename {module-server => module-model}/src/main/kotlin/de/twomartens/timetable/model/db/User.kt (74%) rename {module-server => module-model}/src/main/kotlin/de/twomartens/timetable/model/dto/AdditionalService.kt (53%) rename {module-server => module-model}/src/main/kotlin/de/twomartens/timetable/model/dto/AdditionalServiceStop.kt (70%) create mode 100644 module-model/src/main/kotlin/de/twomartens/timetable/model/dto/Country.kt create mode 100644 module-model/src/main/kotlin/de/twomartens/timetable/model/dto/Depot.kt rename {module-server => module-model}/src/main/kotlin/de/twomartens/timetable/model/dto/Direction.kt (100%) create mode 100644 module-model/src/main/kotlin/de/twomartens/timetable/model/dto/Formation.kt create mode 100644 module-model/src/main/kotlin/de/twomartens/timetable/model/dto/Portal.kt create mode 100644 module-model/src/main/kotlin/de/twomartens/timetable/model/dto/Rotation.kt create mode 100644 module-model/src/main/kotlin/de/twomartens/timetable/model/dto/ServiceFormationStage.kt rename {module-server => module-model}/src/main/kotlin/de/twomartens/timetable/model/dto/ServiceLinkingStage.kt (56%) create mode 100644 module-model/src/main/kotlin/de/twomartens/timetable/model/dto/ServiceRotationStage.kt rename {module-server => module-model}/src/main/kotlin/de/twomartens/timetable/model/dto/ServiceStop.kt (67%) create mode 100644 module-model/src/main/kotlin/de/twomartens/timetable/model/dto/Station.kt create mode 100644 module-model/src/main/kotlin/de/twomartens/timetable/model/dto/Timetable.kt rename {module-server => module-model}/src/main/kotlin/de/twomartens/timetable/model/dto/TimetableState.kt (100%) create mode 100644 module-model/src/main/kotlin/de/twomartens/timetable/model/dto/Track.kt create mode 100644 module-model/src/main/kotlin/de/twomartens/timetable/model/dto/TravelDuration.kt create mode 100644 module-model/src/main/kotlin/de/twomartens/timetable/model/dto/TswRoute.kt create mode 100644 module-model/src/main/kotlin/de/twomartens/timetable/model/dto/User.kt create mode 100644 module-model/src/main/kotlin/de/twomartens/timetable/model/mapper/StationMapper.kt create mode 100644 module-model/src/main/kotlin/de/twomartens/timetable/model/repository/StationRepository.kt create mode 100644 module-server/src/main/kotlin/de/twomartens/timetable/auth/UserController.kt create mode 100644 module-server/src/main/kotlin/de/twomartens/timetable/auth/UserMapper.kt create mode 100644 module-server/src/main/kotlin/de/twomartens/timetable/auth/UserRepository.kt delete mode 100644 module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/service/BahnApiService.kt delete mode 100644 module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/service/BahnDatabaseService.kt rename module-server/src/main/kotlin/de/twomartens/timetable/{support => }/configuration/BusConfiguration.kt (75%) rename module-server/src/main/kotlin/de/twomartens/timetable/{support => }/configuration/OpenApiConfiguration.kt (96%) rename module-server/src/main/kotlin/de/twomartens/timetable/{support => }/configuration/WebConfiguration.kt (82%) rename module-server/src/main/kotlin/de/twomartens/timetable/{support => }/configuration/WebSecurityConfiguration.kt (94%) delete mode 100644 module-server/src/main/kotlin/de/twomartens/timetable/controller/TimetableController.kt create mode 100644 module-server/src/main/kotlin/de/twomartens/timetable/formation/FormationController.kt create mode 100644 module-server/src/main/kotlin/de/twomartens/timetable/formation/FormationMapper.kt create mode 100644 module-server/src/main/kotlin/de/twomartens/timetable/formation/FormationRepository.kt delete mode 100644 module-server/src/main/kotlin/de/twomartens/timetable/model/common/CoachCapacity.kt delete mode 100644 module-server/src/main/kotlin/de/twomartens/timetable/model/common/Depot.kt delete mode 100644 module-server/src/main/kotlin/de/twomartens/timetable/model/common/FormationId.kt delete mode 100644 module-server/src/main/kotlin/de/twomartens/timetable/model/common/Portal.kt delete mode 100644 module-server/src/main/kotlin/de/twomartens/timetable/model/db/Station.kt delete mode 100644 module-server/src/main/kotlin/de/twomartens/timetable/model/dto/Country.kt delete mode 100644 module-server/src/main/kotlin/de/twomartens/timetable/model/dto/Formation.kt delete mode 100644 module-server/src/main/kotlin/de/twomartens/timetable/model/dto/Rotation.kt delete mode 100644 module-server/src/main/kotlin/de/twomartens/timetable/model/dto/ServiceFormationStage.kt delete mode 100644 module-server/src/main/kotlin/de/twomartens/timetable/model/dto/ServiceRotationStage.kt delete mode 100644 module-server/src/main/kotlin/de/twomartens/timetable/model/dto/Station.kt delete mode 100644 module-server/src/main/kotlin/de/twomartens/timetable/model/dto/Timetable.kt delete mode 100644 module-server/src/main/kotlin/de/twomartens/timetable/model/dto/TswRoute.kt create mode 100644 module-server/src/main/kotlin/de/twomartens/timetable/route/RouteController.kt create mode 100644 module-server/src/main/kotlin/de/twomartens/timetable/route/RouteMapper.kt rename module-server/src/main/kotlin/de/twomartens/timetable/{repository => route}/TswRouteRepository.kt (55%) create mode 100644 module-server/src/main/kotlin/de/twomartens/timetable/station/StationController.kt delete mode 100644 module-server/src/main/kotlin/de/twomartens/timetable/support/model/dto/ErrorMessage.kt delete mode 100644 module-server/src/main/kotlin/de/twomartens/timetable/support/monitoring/statusprobe/StatusProbeCriticality.kt create mode 100644 module-server/src/main/kotlin/de/twomartens/timetable/timetable/TimetableController.kt create mode 100644 module-server/src/main/kotlin/de/twomartens/timetable/timetable/TimetableMapper.kt create mode 100644 module-server/src/main/kotlin/de/twomartens/timetable/timetable/TimetableRepository.kt rename {module-server/src/main/kotlin/de/twomartens/timetable => module-support/src/main/kotlin/de/twomartens}/support/configuration/ClockConfiguration.kt (74%) rename {module-server/src/main/kotlin/de/twomartens/timetable => module-support/src/main/kotlin/de/twomartens}/support/configuration/FilterConfiguration.kt (85%) rename {module-server/src/main/kotlin/de/twomartens/timetable => module-support/src/main/kotlin/de/twomartens}/support/configuration/InterceptorConfiguration.kt (68%) create mode 100644 module-support/src/main/kotlin/de/twomartens/support/configuration/RestClientConfiguration.kt rename {module-server/src/main/kotlin/de/twomartens/timetable => module-support/src/main/kotlin/de/twomartens}/support/configuration/RestTemplateConfiguration.kt (84%) rename {module-server/src/main/kotlin/de/twomartens/timetable => module-support/src/main/kotlin/de/twomartens}/support/configuration/StatusProbeConfiguration.kt (61%) rename {module-server/src/main/kotlin/de/twomartens/timetable => module-support/src/main/kotlin/de/twomartens}/support/controller/ExceptionController.kt (87%) rename {module-server/src/main/kotlin/de/twomartens/timetable/support/controller/v1 => module-support/src/main/kotlin/de/twomartens/support/controller}/HealthCheckController.kt (63%) rename {module-server/src/main/kotlin/de/twomartens/timetable => module-support/src/main/kotlin/de/twomartens}/support/controller/VersionHtmlController.kt (98%) rename {module-server/src/main/kotlin/de/twomartens/timetable => module-support/src/main/kotlin/de/twomartens}/support/exception/HttpStatusException.kt (59%) rename {module-server/src/main/kotlin/de/twomartens/timetable => module-support/src/main/kotlin/de/twomartens}/support/interceptor/BufferingClientHttpResponseWrapper.kt (79%) rename {module-server/src/main/kotlin/de/twomartens/timetable => module-support/src/main/kotlin/de/twomartens}/support/interceptor/HeaderInterceptor.kt (98%) rename {module-server/src/main/kotlin/de/twomartens/timetable => module-support/src/main/kotlin/de/twomartens}/support/interceptor/HeaderInterceptorRest.kt (98%) rename {module-server/src/main/kotlin/de/twomartens/timetable => module-support/src/main/kotlin/de/twomartens}/support/interceptor/LoggingInterceptorRest.kt (99%) rename {module-server/src/main/kotlin/de/twomartens/timetable => module-support/src/main/kotlin/de/twomartens}/support/model/LeadershipStatus.kt (71%) create mode 100644 module-support/src/main/kotlin/de/twomartens/support/model/dto/ErrorMessage.kt rename {module-server/src/main/kotlin/de/twomartens/timetable => module-support/src/main/kotlin/de/twomartens}/support/monitoring/actuator/AbstractHealthIndicator.kt (97%) rename {module-server/src/main/kotlin/de/twomartens/timetable => module-support/src/main/kotlin/de/twomartens}/support/monitoring/actuator/AbstractStatusProbeHealthIndicator.kt (88%) rename {module-server/src/main/kotlin/de/twomartens/timetable => module-support/src/main/kotlin/de/twomartens}/support/monitoring/actuator/Preparable.kt (58%) rename {module-server/src/main/kotlin/de/twomartens/timetable => module-support/src/main/kotlin/de/twomartens}/support/monitoring/actuator/RestHealthIndicator.kt (86%) rename {module-server/src/main/kotlin/de/twomartens/timetable => module-support/src/main/kotlin/de/twomartens}/support/monitoring/statusprobe/CountBasedStatusProbe.kt (93%) rename {module-server/src/main/kotlin/de/twomartens/timetable => module-support/src/main/kotlin/de/twomartens}/support/monitoring/statusprobe/PercentageBasedStatusProbe.kt (96%) rename {module-server/src/main/kotlin/de/twomartens/timetable => module-support/src/main/kotlin/de/twomartens}/support/monitoring/statusprobe/ScheduledStatusProbe.kt (91%) rename {module-server/src/main/kotlin/de/twomartens/timetable => module-support/src/main/kotlin/de/twomartens}/support/monitoring/statusprobe/StatusProbe.kt (96%) create mode 100644 module-support/src/main/kotlin/de/twomartens/support/monitoring/statusprobe/StatusProbeCriticality.kt rename {module-server/src/main/kotlin/de/twomartens/timetable => module-support/src/main/kotlin/de/twomartens}/support/monitoring/statusprobe/StatusProbeLogger.kt (98%) rename {module-server/src/main/kotlin/de/twomartens/timetable => module-support/src/main/kotlin/de/twomartens}/support/monitoring/statusprobe/TimeBasedStatusProbe.kt (96%) create mode 100644 module-support/src/main/kotlin/de/twomartens/support/property/HealthCheckProperties.kt rename {module-server/src/main/kotlin/de/twomartens/timetable => module-support/src/main/kotlin/de/twomartens}/support/property/RestTemplateTimeoutProperties.kt (94%) rename {module-server/src/main/kotlin/de/twomartens/timetable => module-support/src/main/kotlin/de/twomartens}/support/property/StatusProbeProperties.kt (95%) rename {module-server/src/main/kotlin/de/twomartens/timetable => module-support/src/main/kotlin/de/twomartens}/support/property/TimeProperties.kt (92%) rename {module-server/src/main/kotlin/de/twomartens/timetable => module-support/src/main/kotlin/de/twomartens}/support/security/SpringPolicyEnforcer.kt (98%) rename {module-server/src/main/kotlin/de/twomartens/timetable => module-support/src/main/kotlin/de/twomartens}/support/security/SpringPolicyEnforcerFilter.kt (98%) rename {module-server/src/main/kotlin/de/twomartens/timetable => module-support/src/main/kotlin/de/twomartens}/support/service/BusService.kt (89%) create mode 100644 module-support/support.gradle.kts diff --git a/buildSrc/src/main/kotlin/twomartens.java-preview.gradle.kts b/buildSrc/src/main/kotlin/twomartens.java-preview.gradle.kts index b44645d..55b0390 100644 --- a/buildSrc/src/main/kotlin/twomartens.java-preview.gradle.kts +++ b/buildSrc/src/main/kotlin/twomartens.java-preview.gradle.kts @@ -7,9 +7,9 @@ tasks.withType().configureEach { } tasks.withType().configureEach { - jvmArgs?.plusAssign("--enable-preview") + jvmArgs.plusAssign("--enable-preview") } tasks.withType().configureEach { - jvmArgs?.plusAssign("--enable-preview") + jvmArgs.plusAssign("--enable-preview") } \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/twomartens.java.gradle.kts b/buildSrc/src/main/kotlin/twomartens.java.gradle.kts index 09bed28..21e4f8c 100644 --- a/buildSrc/src/main/kotlin/twomartens.java.gradle.kts +++ b/buildSrc/src/main/kotlin/twomartens.java.gradle.kts @@ -53,8 +53,10 @@ normalization.runtimeClasspath.metaInf { ignoreAttribute("Build-Timestamp") } + + tasks.register("cleanLibs") { - delete("${buildDir}/libs") + delete("${layout.buildDirectory.get().asFile}/libs") } tasks.build { diff --git a/buildSrc/src/main/kotlin/twomartens.jib.gradle.kts b/buildSrc/src/main/kotlin/twomartens.jib.gradle.kts index db3d096..5b22431 100644 --- a/buildSrc/src/main/kotlin/twomartens.jib.gradle.kts +++ b/buildSrc/src/main/kotlin/twomartens.jib.gradle.kts @@ -16,6 +16,6 @@ tasks.named("build") { } tasks.register("cleanCache") { - delete("${buildDir}/jib-cache") - delete("${buildDir}/libs") + delete("${layout.buildDirectory.get().asFile}/jib-cache") + delete("${layout.buildDirectory.get().asFile}/libs") } diff --git a/buildSrc/src/main/kotlin/twomartens.kotlin.gradle.kts b/buildSrc/src/main/kotlin/twomartens.kotlin.gradle.kts index fe477e2..fbec1a6 100644 --- a/buildSrc/src/main/kotlin/twomartens.kotlin.gradle.kts +++ b/buildSrc/src/main/kotlin/twomartens.kotlin.gradle.kts @@ -16,7 +16,7 @@ val projectSourceCompatibility: String = rootProject.properties["projectSourceCo kotlin { compilerOptions { - freeCompilerArgs.add("-Xjvm-default=all") + freeCompilerArgs.addAll("-Xjvm-default=all", "-Xjsr305=strict") jvmTarget.set(JvmTarget.fromTarget(projectSourceCompatibility)) } } \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/twomartens.nebula-release.gradle.kts b/buildSrc/src/main/kotlin/twomartens.nebula-release.gradle.kts index 6f5f07d..be3b87a 100644 --- a/buildSrc/src/main/kotlin/twomartens.nebula-release.gradle.kts +++ b/buildSrc/src/main/kotlin/twomartens.nebula-release.gradle.kts @@ -9,10 +9,10 @@ apply(plugin="com.netflix.nebula.release") tasks.register("writeVersionProperties") { group = "version" mustRunAfter("release") - outputs.file("$buildDir/version.properties") - val directory = buildDir + outputs.file("${layout.buildDirectory.get().asFile}/version.properties") + val directory = layout.buildDirectory.get().asFile doLast { Files.createDirectories(directory.toPath()) - File("$buildDir/version.properties").writeText("VERSION=${project.version}\n") + File("${layout.buildDirectory.get().asFile}/version.properties").writeText("VERSION=${project.version}\n") } } \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/twomartens.spring-boot.gradle.kts b/buildSrc/src/main/kotlin/twomartens.spring-boot-application.gradle.kts similarity index 85% rename from buildSrc/src/main/kotlin/twomartens.spring-boot.gradle.kts rename to buildSrc/src/main/kotlin/twomartens.spring-boot-application.gradle.kts index 9a2a81b..495b6c5 100644 --- a/buildSrc/src/main/kotlin/twomartens.spring-boot.gradle.kts +++ b/buildSrc/src/main/kotlin/twomartens.spring-boot-application.gradle.kts @@ -1,18 +1,16 @@ import org.gradle.accessors.dm.LibrariesForLibs +import org.springframework.boot.gradle.tasks.bundling.BootJar import java.time.ZonedDateTime import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter.ofPattern plugins { - id("org.springframework.boot") - id("twomartens.java") + id("twomartens.spring-boot-base") } val libs = the() dependencies { - implementation(platform(libs.spring.boot)) - implementation(libs.bundles.spring.boot) testImplementation(libs.spring.boot.test) } @@ -37,13 +35,6 @@ val integrationTestImplementation: Configuration by configurations.getting { extendsFrom(configurations.testImplementation.get()) } -configurations { - configureEach { - exclude(group = "org.springframework.boot", module = "spring-boot-starter-logging") - exclude(group = "org.junit.vintage", module = "junit-vintage-engine") - } -} - tasks.register("integrationTest") { systemProperty("junit.jupiter.execution.parallel.enabled", true) systemProperty("junit.jupiter.execution.parallel.mode.default", "concurrent") @@ -76,5 +67,9 @@ tasks.jar { springBoot { buildInfo() - mainClass.set("de.twomartens.timetable.MainApplicationKt") + mainClass.set(project.properties["mainClass"].toString()) } + +tasks.named("bootJar") { + enabled = true +} \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/twomartens.spring-boot-base.gradle.kts b/buildSrc/src/main/kotlin/twomartens.spring-boot-base.gradle.kts new file mode 100644 index 0000000..c605a8e --- /dev/null +++ b/buildSrc/src/main/kotlin/twomartens.spring-boot-base.gradle.kts @@ -0,0 +1,25 @@ +import org.gradle.accessors.dm.LibrariesForLibs +import org.springframework.boot.gradle.tasks.bundling.BootJar + +plugins { + id("org.springframework.boot") + id("twomartens.java") +} + +val libs = the() + +dependencies { + implementation(platform(libs.spring.boot)) + implementation(libs.spring.boot.log4j) +} + +configurations { + configureEach { + exclude(group = "org.springframework.boot", module = "spring-boot-starter-logging") + exclude(group = "org.junit.vintage", module = "junit-vintage-engine") + } +} + +tasks.named("bootJar") { + enabled = false +} \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/twomartens.spring-boot-cloud.gradle.kts b/buildSrc/src/main/kotlin/twomartens.spring-boot-cloud-application.gradle.kts similarity index 93% rename from buildSrc/src/main/kotlin/twomartens.spring-boot-cloud.gradle.kts rename to buildSrc/src/main/kotlin/twomartens.spring-boot-cloud-application.gradle.kts index bfbf0f4..01a8987 100644 --- a/buildSrc/src/main/kotlin/twomartens.spring-boot-cloud.gradle.kts +++ b/buildSrc/src/main/kotlin/twomartens.spring-boot-cloud-application.gradle.kts @@ -1,13 +1,13 @@ import org.gradle.accessors.dm.LibrariesForLibs plugins { - id("twomartens.spring-boot") + id("twomartens.spring-boot-application") + id("twomartens.spring-boot-cloud-base") } val libs = the() dependencies { - implementation(platform(libs.spring.cloud)) implementation(libs.bundles.spring.boot.server) implementation(libs.spring.openapi) diff --git a/buildSrc/src/main/kotlin/twomartens.spring-boot-cloud-base.gradle.kts b/buildSrc/src/main/kotlin/twomartens.spring-boot-cloud-base.gradle.kts new file mode 100644 index 0000000..6ad6c5a --- /dev/null +++ b/buildSrc/src/main/kotlin/twomartens.spring-boot-cloud-base.gradle.kts @@ -0,0 +1,11 @@ +import org.gradle.accessors.dm.LibrariesForLibs + +plugins { + id("twomartens.spring-boot-base") +} + +val libs = the() + +dependencies { + implementation(platform(libs.spring.cloud)) +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 0e60092..f9651bc 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,7 @@ projectname=timetable projectgroup=de.2martens projectSourceCompatibility=21 +mainClass=de.twomartens.timetable.MainApplicationKt file.encoding=utf-8 org.gradle.parallel=true org.gradle.daemon=true diff --git a/module-bahnApi/bahnApi.gradle.kts b/module-bahnApi/bahnApi.gradle.kts new file mode 100644 index 0000000..86b68cd --- /dev/null +++ b/module-bahnApi/bahnApi.gradle.kts @@ -0,0 +1,23 @@ +plugins { + id("twomartens.spring-boot-cloud-base") + id("twomartens.kotlin") + kotlin("kapt") +} + +dependencies { + implementation(project(":common")) + implementation(project(":model")) + implementation(project(":support")) + + implementation(libs.mapstruct.base) + annotationProcessor(libs.mapstruct.processor) + kapt(libs.mapstruct.processor) + + implementation(libs.jaxb.impl) + implementation(libs.jakarta.xml.binding) + + implementation(libs.spring.openapi) + implementation(libs.spring.boot.mongo) + implementation(libs.spring.cloud.leader.election) + implementation(libs.spring.cloud.starter.bus.kafka) +} \ No newline at end of file diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/configuration/ThreadPoolTaskExecutorConfig.kt b/module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/configuration/ThreadPoolTaskExecutorConfig.kt similarity index 100% rename from module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/configuration/ThreadPoolTaskExecutorConfig.kt rename to module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/configuration/ThreadPoolTaskExecutorConfig.kt diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/configuration/ThreadPoolTaskSchedulerConfig.kt b/module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/configuration/ThreadPoolTaskSchedulerConfig.kt similarity index 90% rename from module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/configuration/ThreadPoolTaskSchedulerConfig.kt rename to module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/configuration/ThreadPoolTaskSchedulerConfig.kt index 6d5f27f..4b43657 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/configuration/ThreadPoolTaskSchedulerConfig.kt +++ b/module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/configuration/ThreadPoolTaskSchedulerConfig.kt @@ -13,7 +13,7 @@ open class ThreadPoolTaskSchedulerConfig { open fun threadPoolTaskScheduler(): ThreadPoolTaskScheduler { val scheduler = ThreadPoolTaskScheduler() scheduler.poolSize = POOL_SIZE - scheduler.threadNamePrefix = THREAD_NAME_PREFIX + scheduler.setThreadNamePrefix(THREAD_NAME_PREFIX) return scheduler } } \ No newline at end of file diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/events/ScheduledTasksCreatedEvent.kt b/module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/events/ScheduledTasksCreatedEvent.kt similarity index 91% rename from module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/events/ScheduledTasksCreatedEvent.kt rename to module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/events/ScheduledTasksCreatedEvent.kt index e83d8b8..33ac24d 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/events/ScheduledTasksCreatedEvent.kt +++ b/module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/events/ScheduledTasksCreatedEvent.kt @@ -11,11 +11,7 @@ class ScheduledTasksCreatedEvent private constructor(source: Instant, originServ destination: Destination) : RemoteApplicationEvent(source, originService, destination) { - private val creationTime: Instant - - init { - creationTime = source - } + private val creationTime: Instant = source override fun getSource(): Instant { return creationTime diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/jaxb/LocalDateTimeAdapter.kt b/module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/jaxb/LocalDateTimeAdapter.kt similarity index 100% rename from module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/jaxb/LocalDateTimeAdapter.kt rename to module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/jaxb/LocalDateTimeAdapter.kt diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/mapper/BahnStationMapper.kt b/module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/mapper/BahnStationMapper.kt similarity index 57% rename from module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/mapper/BahnStationMapper.kt rename to module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/mapper/BahnStationMapper.kt index 9674973..404993e 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/mapper/BahnStationMapper.kt +++ b/module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/mapper/BahnStationMapper.kt @@ -1,6 +1,9 @@ package de.twomartens.timetable.bahnApi.mapper import de.twomartens.timetable.bahnApi.model.db.BahnStation +import de.twomartens.timetable.model.common.CountryCode +import de.twomartens.timetable.model.common.StationId +import de.twomartens.timetable.model.db.Station import de.twomartens.timetable.types.NonEmptyString import org.mapstruct.* @@ -22,4 +25,16 @@ interface BahnStationMapper { dto.db ) } + + @Mapping(target = "id", ignore = true) + @Mapping(target = "created", ignore = true) + @Mapping(target = "lastModified", ignore = true) + fun mapToCommonDB(db: BahnStation, countryCode: String): Station { + return Station( + StationId.of(NonEmptyString(countryCode + "-" + db.eva.value.toString())), + CountryCode(NonEmptyString(countryCode)), + db.name, + listOf() + ) + } } \ No newline at end of file diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/mapper/BahnTimetableMapper.kt b/module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/mapper/BahnTimetableMapper.kt similarity index 100% rename from module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/mapper/BahnTimetableMapper.kt rename to module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/mapper/BahnTimetableMapper.kt diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/model/Eva.kt b/module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/model/Eva.kt similarity index 70% rename from module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/model/Eva.kt rename to module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/model/Eva.kt index 39a9ee8..2bb6c58 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/model/Eva.kt +++ b/module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/model/Eva.kt @@ -1,7 +1,5 @@ package de.twomartens.timetable.bahnApi.model -import de.twomartens.timetable.model.common.StationId - @JvmInline value class Eva(val value: Int) { init { @@ -17,8 +15,8 @@ value class Eva(val value: Int) { companion object { val UNKNOWN = Eva(-1) - fun of(stationId: StationId): Eva { - return Eva(stationId.stationIdWithinCountry.toInt()) + fun of(stationId: String): Eva { + return Eva(stationId.toInt()) } } } diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/model/FetchDates.kt b/module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/model/FetchDates.kt similarity index 100% rename from module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/model/FetchDates.kt rename to module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/model/FetchDates.kt diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/model/TaskFactory.kt b/module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/model/TaskFactory.kt similarity index 100% rename from module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/model/TaskFactory.kt rename to module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/model/TaskFactory.kt diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/model/db/BahnStation.kt b/module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/model/db/BahnStation.kt similarity index 100% rename from module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/model/db/BahnStation.kt rename to module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/model/db/BahnStation.kt diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/model/db/BahnTimetable.kt b/module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/model/db/BahnTimetable.kt similarity index 100% rename from module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/model/db/BahnTimetable.kt rename to module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/model/db/BahnTimetable.kt diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/model/db/ScheduledFetchTask.kt b/module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/model/db/ScheduledFetchTask.kt similarity index 92% rename from module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/model/db/ScheduledFetchTask.kt rename to module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/model/db/ScheduledFetchTask.kt index 1be2ba6..07bcf70 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/model/db/ScheduledFetchTask.kt +++ b/module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/model/db/ScheduledFetchTask.kt @@ -12,7 +12,7 @@ import java.time.Instant import java.time.LocalDateTime @Document -@CompoundIndex(def = "{'eva': 1, 'fetchedDateTime': 1", unique = true) +@CompoundIndex(def = "{'eva': 1, 'fetchedDateTime': 1}", unique = true) data class ScheduledFetchTask( var eva: Eva, var fetchedDateTime: HourAtDay, diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/model/dto/BahnStation.kt b/module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/model/dto/BahnStation.kt similarity index 100% rename from module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/model/dto/BahnStation.kt rename to module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/model/dto/BahnStation.kt diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/model/dto/BahnStationStop.kt b/module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/model/dto/BahnStationStop.kt similarity index 100% rename from module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/model/dto/BahnStationStop.kt rename to module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/model/dto/BahnStationStop.kt diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/model/dto/BahnStations.kt b/module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/model/dto/BahnStations.kt similarity index 73% rename from module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/model/dto/BahnStations.kt rename to module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/model/dto/BahnStations.kt index e864822..dbdb1b5 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/model/dto/BahnStations.kt +++ b/module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/model/dto/BahnStations.kt @@ -8,7 +8,7 @@ import jakarta.xml.bind.annotation.XmlRootElement @XmlRootElement(name = "stations") @XmlAccessorType(XmlAccessType.FIELD) data class BahnStations( - @field:XmlElement(name = "station") var stations: List + @field:XmlElement(name = "station") var stations: MutableList ) { - constructor() : this(listOf()) + constructor() : this(mutableListOf()) } diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/model/dto/BahnStopEvent.kt b/module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/model/dto/BahnStopEvent.kt similarity index 100% rename from module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/model/dto/BahnStopEvent.kt rename to module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/model/dto/BahnStopEvent.kt diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/model/dto/BahnTimetable.kt b/module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/model/dto/BahnTimetable.kt similarity index 100% rename from module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/model/dto/BahnTimetable.kt rename to module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/model/dto/BahnTimetable.kt diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/model/dto/BahnTripLabel.kt b/module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/model/dto/BahnTripLabel.kt similarity index 100% rename from module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/model/dto/BahnTripLabel.kt rename to module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/model/dto/BahnTripLabel.kt diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/model/dto/BahnTripType.kt b/module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/model/dto/BahnTripType.kt similarity index 100% rename from module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/model/dto/BahnTripType.kt rename to module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/model/dto/BahnTripType.kt diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/property/BahnApiProperties.kt b/module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/property/BahnApiProperties.kt similarity index 100% rename from module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/property/BahnApiProperties.kt rename to module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/property/BahnApiProperties.kt diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/repository/BahnStationRepository.kt b/module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/repository/BahnStationRepository.kt similarity index 100% rename from module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/repository/BahnStationRepository.kt rename to module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/repository/BahnStationRepository.kt diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/repository/BahnTimetableRepository.kt b/module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/repository/BahnTimetableRepository.kt similarity index 100% rename from module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/repository/BahnTimetableRepository.kt rename to module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/repository/BahnTimetableRepository.kt diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/repository/ScheduledFetchTaskRepository.kt b/module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/repository/ScheduledFetchTaskRepository.kt similarity index 100% rename from module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/repository/ScheduledFetchTaskRepository.kt rename to module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/repository/ScheduledFetchTaskRepository.kt diff --git a/module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/service/BahnApiService.kt b/module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/service/BahnApiService.kt new file mode 100644 index 0000000..fa3489d --- /dev/null +++ b/module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/service/BahnApiService.kt @@ -0,0 +1,51 @@ +package de.twomartens.timetable.bahnApi.service + +import de.twomartens.timetable.bahnApi.model.Eva +import de.twomartens.timetable.bahnApi.model.dto.BahnStation +import de.twomartens.timetable.bahnApi.model.dto.BahnStations +import de.twomartens.timetable.bahnApi.model.dto.BahnTimetable +import de.twomartens.timetable.bahnApi.property.BahnApiProperties +import de.twomartens.timetable.types.HourAtDay +import org.springframework.http.MediaType +import org.springframework.stereotype.Service +import org.springframework.web.client.RestClient +import java.time.LocalTime +import java.time.format.DateTimeFormatter + +@Service +class BahnApiService( + private val restClient: RestClient, + private val properties: BahnApiProperties +) { + fun fetchStations(pattern: String): List { + val body = restClient.get() + .uri("https://apis.deutschebahn.com/db-api-marketplace/apis/timetables/v1/station/${pattern}") + .headers { + it.accept = mutableListOf(MediaType.APPLICATION_XML) + it.contentType = MediaType.APPLICATION_XML + it.set("DB-Client-Id", properties.clientId) + it.set("DB-Api-Key", properties.clientSecret) + } + .retrieve() + .body(BahnStations::class.java) + return body?.stations ?: listOf() + } + + fun fetchTimetable(eva: Eva, hourAtDay: HourAtDay): BahnTimetable { + val dateFormatter = DateTimeFormatter.ofPattern("yyMMdd") + val timeFormatter = DateTimeFormatter.ofPattern("HH") + val time = LocalTime.of(hourAtDay.hour.value, 0) + val body = restClient.get() + .uri("https://apis.deutschebahn.com/db-api-marketplace/apis/timetables/v1/plan/" + + "${eva}/${hourAtDay.date.format(dateFormatter)}/${time.format(timeFormatter)}") + .headers { + it.accept = mutableListOf(MediaType.APPLICATION_XML) + it.contentType = MediaType.APPLICATION_XML + it.set("DB-Client-Id", properties.clientId) + it.set("DB-Api-Key", properties.clientSecret) + } + .retrieve() + .body(BahnTimetable::class.java) + return body!! + } +} \ No newline at end of file diff --git a/module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/service/BahnDatabaseService.kt b/module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/service/BahnDatabaseService.kt new file mode 100644 index 0000000..c87bd89 --- /dev/null +++ b/module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/service/BahnDatabaseService.kt @@ -0,0 +1,97 @@ +package de.twomartens.timetable.bahnApi.service + +import de.twomartens.timetable.bahnApi.mapper.BahnStationMapper +import de.twomartens.timetable.bahnApi.mapper.BahnTimetableMapper +import de.twomartens.timetable.bahnApi.model.dto.BahnStation +import de.twomartens.timetable.bahnApi.model.dto.BahnTimetable +import de.twomartens.timetable.bahnApi.repository.BahnStationRepository +import de.twomartens.timetable.bahnApi.repository.BahnTimetableRepository +import de.twomartens.timetable.model.db.Station +import de.twomartens.timetable.model.repository.StationRepository +import de.twomartens.timetable.types.HourAtDay +import org.mapstruct.factory.Mappers +import org.springframework.data.mongodb.core.BulkOperations +import org.springframework.data.mongodb.core.MongoTemplate +import org.springframework.stereotype.Service + +@Service +open class BahnDatabaseService( + private val bahnStationRepository: BahnStationRepository, + private val stationRepository: StationRepository, + private val bahnTimetableRepository: BahnTimetableRepository, + private val mongoTemplate: MongoTemplate +) { + private val bahnStationMapper = Mappers.getMapper(BahnStationMapper::class.java) + private val bahnTimetableMapper = Mappers.getMapper(BahnTimetableMapper::class.java) + + fun storeStations(stations: List) { + + val existingStations = stationRepository.findAllByCountryCode(COUNTRY_CODE) + val commonStationMap = existingStations + .associateBy { it.stationId.stationIdWithinCountry } + val bahnStationMap = stations.asSequence() + .map(bahnStationMapper::mapToDB) + .associateBy { it.eva.toString() } + + updateBahnStations(bahnStationMap) + deleteRemovedStations(existingStations, bahnStationMap) + updateStations(existingStations, bahnStationMap) + addNewStations(bahnStationMap, commonStationMap) + } + + private fun updateBahnStations(bahnStationMap: Map) { + bahnStationRepository.deleteAll() + val bahnStations: List = buildList { + this.addAll(bahnStationMap.values) + } + + mongoTemplate.bulkOps(BulkOperations.BulkMode.UNORDERED, + de.twomartens.timetable.bahnApi.model.db.BahnStation::class.java) + .insert(bahnStations) + .execute() + } + + private fun deleteRemovedStations( + existingStations: List, + bahnStationMap: Map + ) { + val deletedStations = existingStations + .filterNot { bahnStationMap.containsKey(it.stationId.stationIdWithinCountry) } + stationRepository.deleteAll(deletedStations) + } + + private fun updateStations( + existingStations: List, + bahnStationMap: Map + ) { + val updatedStations = existingStations + .filter { bahnStationMap.containsKey(it.stationId.stationIdWithinCountry) } + updatedStations.map { + it.name = bahnStationMap[it.stationId.stationIdWithinCountry]!!.name + } + stationRepository.saveAll(updatedStations) + } + + private fun addNewStations( + bahnStations: Map, + commonStationMap: Map + ) { + val newStations = bahnStations.asSequence() + .filterNot { commonStationMap.containsKey(it.key) } + .map { bahnStationMapper.mapToCommonDB(it.value, COUNTRY_CODE) } + .toList() + + mongoTemplate.bulkOps(BulkOperations.BulkMode.UNORDERED, + Station::class.java) + .insert(newStations) + .execute() + } + + fun storeTimetable(timetable: BahnTimetable, hourAtDay: HourAtDay) { + bahnTimetableRepository.save(bahnTimetableMapper.mapToDB(timetable, hourAtDay)) + } + + companion object { + private const val COUNTRY_CODE = "de" + } +} \ No newline at end of file diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/service/TaskScheduler.kt b/module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/service/FetchTaskScheduler.kt similarity index 99% rename from module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/service/TaskScheduler.kt rename to module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/service/FetchTaskScheduler.kt index 1374ab1..660afba 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/service/TaskScheduler.kt +++ b/module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/service/FetchTaskScheduler.kt @@ -16,7 +16,7 @@ import java.time.ZonedDateTime private const val PAST_TASK_EXECUTION_OFFSET = 1 @Service -class TaskScheduler( +class FetchTaskScheduler( private val clock: Clock, private val threadPoolTaskScheduler: ThreadPoolTaskScheduler, private val threadPoolTaskExecutor: ThreadPoolTaskExecutor, diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/service/ScheduledTaskService.kt b/module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/service/ScheduledTaskService.kt similarity index 94% rename from module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/service/ScheduledTaskService.kt rename to module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/service/ScheduledTaskService.kt index f0ed954..64e2c6c 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/service/ScheduledTaskService.kt +++ b/module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/service/ScheduledTaskService.kt @@ -1,5 +1,7 @@ package de.twomartens.timetable.bahnApi.service +import de.twomartens.support.model.LeadershipStatus +import de.twomartens.support.service.BusService import de.twomartens.timetable.bahnApi.events.ScheduledTasksCreatedEvent import de.twomartens.timetable.bahnApi.model.Eva import de.twomartens.timetable.bahnApi.model.FetchDates @@ -7,8 +9,6 @@ import de.twomartens.timetable.bahnApi.model.TaskFactory import de.twomartens.timetable.bahnApi.model.db.ScheduledFetchTask import de.twomartens.timetable.bahnApi.repository.ScheduledFetchTaskRepository import de.twomartens.timetable.model.db.TswRoute -import de.twomartens.timetable.support.model.LeadershipStatus -import de.twomartens.timetable.support.service.BusService import de.twomartens.timetable.types.Hour import de.twomartens.timetable.types.HourAtDay import mu.KotlinLogging @@ -27,7 +27,7 @@ class ScheduledTaskService( private val leaderProperties: LeaderProperties, private val scheduledFetchTaskRepository: ScheduledFetchTaskRepository, private val taskFactory: TaskFactory, - private val taskScheduler: TaskScheduler + private val fetchTaskScheduler: FetchTaskScheduler ) { private var createdTime: Instant = Instant.EPOCH private var lastUpdate: Instant = Instant.EPOCH @@ -81,8 +81,8 @@ class ScheduledTaskService( fetchDates: FetchDates ): List { val newTasks = mutableListOf() - tswRoute.stationIds.forEach { - val stationId = it + tswRoute.stations.forEach { + val stationId = it.id val eva = Eva.of(stationId) var hourAtDay = HourAtDay.of(Hour.of(23), fetchDates.previousDay) var newTask = taskFactory.createTaskAndUpdateCounter(eva, hourAtDay) @@ -103,7 +103,7 @@ class ScheduledTaskService( private fun scheduleTasksIfLeader(tasksToSchedule: List) { if (leadershipStatus.isLeader) { - taskScheduler.scheduleFetchTasks(tasksToSchedule) + fetchTaskScheduler.scheduleFetchTasks(tasksToSchedule) } } diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/tasks/DeleteScheduledTask.kt b/module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/tasks/DeleteScheduledTask.kt similarity index 100% rename from module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/tasks/DeleteScheduledTask.kt rename to module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/tasks/DeleteScheduledTask.kt diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/tasks/FetchTimetableTask.kt b/module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/tasks/FetchTimetableTask.kt similarity index 86% rename from module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/tasks/FetchTimetableTask.kt rename to module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/tasks/FetchTimetableTask.kt index 7f68b6f..dcb8d96 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/tasks/FetchTimetableTask.kt +++ b/module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/tasks/FetchTimetableTask.kt @@ -2,7 +2,7 @@ package de.twomartens.timetable.bahnApi.tasks import de.twomartens.timetable.bahnApi.model.Eva import de.twomartens.timetable.bahnApi.service.BahnApiService -import de.twomartens.timetable.bahnApi.service.TaskScheduler +import de.twomartens.timetable.bahnApi.service.FetchTaskScheduler import de.twomartens.timetable.types.HourAtDay import mu.KotlinLogging import org.springframework.scheduling.annotation.Async @@ -12,7 +12,7 @@ open class FetchTimetableTask( private val eva: Eva, private val hourAtDay: HourAtDay, private val bahnApiService: BahnApiService, - private val scheduler: TaskScheduler) : Runnable { + private val scheduler: FetchTaskScheduler) : Runnable { override fun run() { log.info { "Fetch timetable: [eva: $eva], [date: ${hourAtDay.date}], [hour: ${hourAtDay.hour}]" diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/tasks/StoreTimetableTask.kt b/module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/tasks/StoreTimetableTask.kt similarity index 100% rename from module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/tasks/StoreTimetableTask.kt rename to module-bahnApi/src/main/kotlin/de/twomartens/timetable/bahnApi/tasks/StoreTimetableTask.kt diff --git a/module-common/src/main/kotlin/de/twomartens/timetable/types/Email.kt b/module-common/src/main/kotlin/de/twomartens/timetable/types/Email.kt new file mode 100644 index 0000000..34aa811 --- /dev/null +++ b/module-common/src/main/kotlin/de/twomartens/timetable/types/Email.kt @@ -0,0 +1,11 @@ +package de.twomartens.timetable.types + +@JvmInline +value class Email private constructor(val value: String) { + companion object { + fun of(email: NonEmptyString): Email { + require(email.value.contains("@")) { "Invalid email format" } + return Email(email.value) + } + } +} \ No newline at end of file diff --git a/module-common/src/main/kotlin/de/twomartens/timetable/types/ZeroOrPositiveInteger.kt b/module-common/src/main/kotlin/de/twomartens/timetable/types/ZeroOrPositiveInteger.kt new file mode 100644 index 0000000..4b6bd9e --- /dev/null +++ b/module-common/src/main/kotlin/de/twomartens/timetable/types/ZeroOrPositiveInteger.kt @@ -0,0 +1,10 @@ +package de.twomartens.timetable.types + +@JvmInline +value class ZeroOrPositiveInteger(val value: Int) { + init { + require(value >= 0) { + "Value must be zero or positive integer" + } + } +} diff --git a/module-model/model.gradle.kts b/module-model/model.gradle.kts new file mode 100644 index 0000000..3ccf168 --- /dev/null +++ b/module-model/model.gradle.kts @@ -0,0 +1,14 @@ +plugins { + id("twomartens.spring-boot-base") + id("twomartens.kotlin") + kotlin("kapt") +} + +dependencies { + implementation(project(":common")) + implementation(libs.spring.boot.mongo) + + implementation(libs.mapstruct.base) + annotationProcessor(libs.mapstruct.processor) + kapt(libs.mapstruct.processor) +} \ No newline at end of file diff --git a/module-model/src/main/kotlin/de/twomartens/timetable/model/common/CoachCapacity.kt b/module-model/src/main/kotlin/de/twomartens/timetable/model/common/CoachCapacity.kt new file mode 100644 index 0000000..f34bff0 --- /dev/null +++ b/module-model/src/main/kotlin/de/twomartens/timetable/model/common/CoachCapacity.kt @@ -0,0 +1,6 @@ +package de.twomartens.timetable.model.common + +import de.twomartens.timetable.types.ZeroOrPositiveInteger + +@JvmInline +value class CoachCapacity(val capacity: ZeroOrPositiveInteger) diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/model/common/CountryCode.kt b/module-model/src/main/kotlin/de/twomartens/timetable/model/common/CountryCode.kt similarity index 100% rename from module-server/src/main/kotlin/de/twomartens/timetable/model/common/CountryCode.kt rename to module-model/src/main/kotlin/de/twomartens/timetable/model/common/CountryCode.kt diff --git a/module-model/src/main/kotlin/de/twomartens/timetable/model/common/DepotId.kt b/module-model/src/main/kotlin/de/twomartens/timetable/model/common/DepotId.kt new file mode 100644 index 0000000..e85166a --- /dev/null +++ b/module-model/src/main/kotlin/de/twomartens/timetable/model/common/DepotId.kt @@ -0,0 +1,12 @@ +package de.twomartens.timetable.model.common + +import de.twomartens.timetable.types.NonEmptyString + +@JvmInline +value class DepotId private constructor(val id: String) { + companion object { + fun of(id: NonEmptyString): DepotId { + return DepotId(id.value) + } + } +} \ No newline at end of file diff --git a/module-model/src/main/kotlin/de/twomartens/timetable/model/common/FormationId.kt b/module-model/src/main/kotlin/de/twomartens/timetable/model/common/FormationId.kt new file mode 100644 index 0000000..1ecf257 --- /dev/null +++ b/module-model/src/main/kotlin/de/twomartens/timetable/model/common/FormationId.kt @@ -0,0 +1,12 @@ +package de.twomartens.timetable.model.common + +import de.twomartens.timetable.types.NonEmptyString + +@JvmInline +value class FormationId private constructor(val id: String) { + companion object { + fun of(id: NonEmptyString): FormationId { + return FormationId(id.value) + } + } +} \ No newline at end of file diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/model/common/LanguageCode.kt b/module-model/src/main/kotlin/de/twomartens/timetable/model/common/LanguageCode.kt similarity index 100% rename from module-server/src/main/kotlin/de/twomartens/timetable/model/common/LanguageCode.kt rename to module-model/src/main/kotlin/de/twomartens/timetable/model/common/LanguageCode.kt diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/model/common/Line.kt b/module-model/src/main/kotlin/de/twomartens/timetable/model/common/Line.kt similarity index 100% rename from module-server/src/main/kotlin/de/twomartens/timetable/model/common/Line.kt rename to module-model/src/main/kotlin/de/twomartens/timetable/model/common/Line.kt diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/model/common/Platform.kt b/module-model/src/main/kotlin/de/twomartens/timetable/model/common/Platform.kt similarity index 100% rename from module-server/src/main/kotlin/de/twomartens/timetable/model/common/Platform.kt rename to module-model/src/main/kotlin/de/twomartens/timetable/model/common/Platform.kt diff --git a/module-model/src/main/kotlin/de/twomartens/timetable/model/common/PortalId.kt b/module-model/src/main/kotlin/de/twomartens/timetable/model/common/PortalId.kt new file mode 100644 index 0000000..61997df --- /dev/null +++ b/module-model/src/main/kotlin/de/twomartens/timetable/model/common/PortalId.kt @@ -0,0 +1,12 @@ +package de.twomartens.timetable.model.common + +import de.twomartens.timetable.types.NonEmptyString + +@JvmInline +value class PortalId private constructor(val id: String) { + companion object { + fun of(id: NonEmptyString): PortalId { + return PortalId(id.value) + } + } +} \ No newline at end of file diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/model/common/RotationId.kt b/module-model/src/main/kotlin/de/twomartens/timetable/model/common/RotationId.kt similarity index 100% rename from module-server/src/main/kotlin/de/twomartens/timetable/model/common/RotationId.kt rename to module-model/src/main/kotlin/de/twomartens/timetable/model/common/RotationId.kt diff --git a/module-model/src/main/kotlin/de/twomartens/timetable/model/common/RouteId.kt b/module-model/src/main/kotlin/de/twomartens/timetable/model/common/RouteId.kt new file mode 100644 index 0000000..d91228e --- /dev/null +++ b/module-model/src/main/kotlin/de/twomartens/timetable/model/common/RouteId.kt @@ -0,0 +1,12 @@ +package de.twomartens.timetable.model.common + +import de.twomartens.timetable.types.NonEmptyString + +@JvmInline +value class RouteId private constructor(val id: String) { + companion object { + fun of(id: NonEmptyString): RouteId { + return RouteId(id.value) + } + } +} \ No newline at end of file diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/model/common/Section.kt b/module-model/src/main/kotlin/de/twomartens/timetable/model/common/Section.kt similarity index 100% rename from module-server/src/main/kotlin/de/twomartens/timetable/model/common/Section.kt rename to module-model/src/main/kotlin/de/twomartens/timetable/model/common/Section.kt diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/model/common/ServiceId.kt b/module-model/src/main/kotlin/de/twomartens/timetable/model/common/ServiceId.kt similarity index 100% rename from module-server/src/main/kotlin/de/twomartens/timetable/model/common/ServiceId.kt rename to module-model/src/main/kotlin/de/twomartens/timetable/model/common/ServiceId.kt diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/model/common/StationId.kt b/module-model/src/main/kotlin/de/twomartens/timetable/model/common/StationId.kt similarity index 91% rename from module-server/src/main/kotlin/de/twomartens/timetable/model/common/StationId.kt rename to module-model/src/main/kotlin/de/twomartens/timetable/model/common/StationId.kt index 9e80ce6..dcc04b4 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/model/common/StationId.kt +++ b/module-model/src/main/kotlin/de/twomartens/timetable/model/common/StationId.kt @@ -2,8 +2,7 @@ package de.twomartens.timetable.model.common import de.twomartens.timetable.types.NonEmptyString -@JvmInline -value class StationId private constructor(val value: String) { +class StationId private constructor(val value: String) { companion object { private val idPattern = Regex("^\\w{2}-(?\\w.*)") diff --git a/module-model/src/main/kotlin/de/twomartens/timetable/model/common/TimetableId.kt b/module-model/src/main/kotlin/de/twomartens/timetable/model/common/TimetableId.kt new file mode 100644 index 0000000..edae561 --- /dev/null +++ b/module-model/src/main/kotlin/de/twomartens/timetable/model/common/TimetableId.kt @@ -0,0 +1,12 @@ +package de.twomartens.timetable.model.common + +import de.twomartens.timetable.types.NonEmptyString + +@JvmInline +value class TimetableId private constructor(val value: String) { + companion object { + fun of(id: NonEmptyString): TimetableId { + return TimetableId(id.value) + } + } +} \ No newline at end of file diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/model/common/TrackId.kt b/module-model/src/main/kotlin/de/twomartens/timetable/model/common/TrackId.kt similarity index 100% rename from module-server/src/main/kotlin/de/twomartens/timetable/model/common/TrackId.kt rename to module-model/src/main/kotlin/de/twomartens/timetable/model/common/TrackId.kt diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/model/common/UserId.kt b/module-model/src/main/kotlin/de/twomartens/timetable/model/common/UserId.kt similarity index 100% rename from module-server/src/main/kotlin/de/twomartens/timetable/model/common/UserId.kt rename to module-model/src/main/kotlin/de/twomartens/timetable/model/common/UserId.kt diff --git a/module-model/src/main/kotlin/de/twomartens/timetable/model/db/Depot.kt b/module-model/src/main/kotlin/de/twomartens/timetable/model/db/Depot.kt new file mode 100644 index 0000000..c01023e --- /dev/null +++ b/module-model/src/main/kotlin/de/twomartens/timetable/model/db/Depot.kt @@ -0,0 +1,14 @@ +package de.twomartens.timetable.model.db + +import de.twomartens.timetable.model.common.DepotId +import de.twomartens.timetable.model.dto.Station +import de.twomartens.timetable.model.dto.Track +import de.twomartens.timetable.types.NonEmptyString + +data class Depot( + val id: DepotId, + val name: NonEmptyString, + val nearestStation: Station, + val tracks: List, + val travelDurations: List +) diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/model/db/Formation.kt b/module-model/src/main/kotlin/de/twomartens/timetable/model/db/Formation.kt similarity index 83% rename from module-server/src/main/kotlin/de/twomartens/timetable/model/db/Formation.kt rename to module-model/src/main/kotlin/de/twomartens/timetable/model/db/Formation.kt index c253729..cceb5f5 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/model/db/Formation.kt +++ b/module-model/src/main/kotlin/de/twomartens/timetable/model/db/Formation.kt @@ -3,6 +3,7 @@ package de.twomartens.timetable.model.db import de.twomartens.timetable.model.common.FormationId import de.twomartens.timetable.model.common.UserId import de.twomartens.timetable.types.NonEmptyString +import de.twomartens.timetable.types.ZeroOrPositiveInteger import org.bson.types.ObjectId import org.springframework.data.annotation.CreatedDate import org.springframework.data.annotation.Id @@ -17,8 +18,9 @@ data class Formation( var userId: UserId, var formationId: FormationId, var name: NonEmptyString, - var trainSimWorldFormationId: FormationId, - var coaches: List + var trainSimWorldFormationId: FormationId?, + var formation: String, + var length: ZeroOrPositiveInteger ) { @Id var id: ObjectId = ObjectId() diff --git a/module-model/src/main/kotlin/de/twomartens/timetable/model/db/Portal.kt b/module-model/src/main/kotlin/de/twomartens/timetable/model/db/Portal.kt new file mode 100644 index 0000000..ae5092d --- /dev/null +++ b/module-model/src/main/kotlin/de/twomartens/timetable/model/db/Portal.kt @@ -0,0 +1,12 @@ +package de.twomartens.timetable.model.db + +import de.twomartens.timetable.model.common.PortalId +import de.twomartens.timetable.model.dto.Station +import de.twomartens.timetable.types.NonEmptyString + +data class Portal( + val id: PortalId, + val name: NonEmptyString, + val nearestStation: Station, + val travelDurations: List +) diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/model/db/TswRoute.kt b/module-model/src/main/kotlin/de/twomartens/timetable/model/db/Station.kt similarity index 67% rename from module-server/src/main/kotlin/de/twomartens/timetable/model/db/TswRoute.kt rename to module-model/src/main/kotlin/de/twomartens/timetable/model/db/Station.kt index 8ce25b4..ccefdbc 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/model/db/TswRoute.kt +++ b/module-model/src/main/kotlin/de/twomartens/timetable/model/db/Station.kt @@ -1,6 +1,8 @@ package de.twomartens.timetable.model.db -import de.twomartens.timetable.model.common.* +import de.twomartens.timetable.model.common.CountryCode +import de.twomartens.timetable.model.common.Platform +import de.twomartens.timetable.model.common.StationId import de.twomartens.timetable.types.NonEmptyString import org.bson.types.ObjectId import org.springframework.data.annotation.CreatedDate @@ -11,14 +13,12 @@ import org.springframework.data.mongodb.core.mapping.Document import java.time.Instant @Document -@CompoundIndex(def = "{'userId': 1, 'name': 1}", unique = true) -data class TswRoute( - var userId: UserId, - var name: NonEmptyString, +@CompoundIndex(def = "{stationId: 1, countryCode: 1}", unique = true) +data class Station( + var stationId: StationId, var countryCode: CountryCode, - var stationIds: List, - var portals: List, - var depots: List + var name: NonEmptyString, + var platforms: List ) { @Id var id: ObjectId = ObjectId() diff --git a/module-model/src/main/kotlin/de/twomartens/timetable/model/db/Timetable.kt b/module-model/src/main/kotlin/de/twomartens/timetable/model/db/Timetable.kt new file mode 100644 index 0000000..629f06a --- /dev/null +++ b/module-model/src/main/kotlin/de/twomartens/timetable/model/db/Timetable.kt @@ -0,0 +1,39 @@ +package de.twomartens.timetable.model.db + +import de.twomartens.timetable.model.common.RouteId +import de.twomartens.timetable.model.common.TimetableId +import de.twomartens.timetable.model.common.UserId +import de.twomartens.timetable.model.dto.TimetableState +import de.twomartens.timetable.types.NonEmptyString +import de.twomartens.timetable.types.ZeroOrPositiveInteger +import org.bson.types.ObjectId +import org.springframework.data.annotation.CreatedDate +import org.springframework.data.annotation.Id +import org.springframework.data.annotation.LastModifiedDate +import org.springframework.data.mongodb.core.index.CompoundIndex +import org.springframework.data.mongodb.core.mapping.Document +import java.time.Instant +import java.time.LocalDate + +@Document +@CompoundIndex(def = "{userId: 1, timetableId: 1}", unique = true) +@CompoundIndex(def = "{userId: 1, routeId: 1}") +data class Timetable( + var userId: UserId, + var routeId: RouteId, + var routeName: String, + var timetableId: TimetableId, + var name: NonEmptyString, + var fetchDate: LocalDate, + var timetableState: TimetableState, + var numberOfServices: ZeroOrPositiveInteger +) { + @Id + var id: ObjectId = ObjectId() + + @CreatedDate + lateinit var created: Instant + + @LastModifiedDate + lateinit var lastModified: Instant +} diff --git a/module-model/src/main/kotlin/de/twomartens/timetable/model/db/TravelDuration.kt b/module-model/src/main/kotlin/de/twomartens/timetable/model/db/TravelDuration.kt new file mode 100644 index 0000000..57afdb3 --- /dev/null +++ b/module-model/src/main/kotlin/de/twomartens/timetable/model/db/TravelDuration.kt @@ -0,0 +1,8 @@ +package de.twomartens.timetable.model.db + +import de.twomartens.timetable.model.dto.Formation + +data class TravelDuration( + val formation: Formation, + val time: Long +) diff --git a/module-model/src/main/kotlin/de/twomartens/timetable/model/db/TswRoute.kt b/module-model/src/main/kotlin/de/twomartens/timetable/model/db/TswRoute.kt new file mode 100644 index 0000000..1127b83 --- /dev/null +++ b/module-model/src/main/kotlin/de/twomartens/timetable/model/db/TswRoute.kt @@ -0,0 +1,35 @@ +package de.twomartens.timetable.model.db + +import de.twomartens.timetable.model.common.RouteId +import de.twomartens.timetable.model.common.UserId +import de.twomartens.timetable.model.dto.Country +import de.twomartens.timetable.model.dto.Station +import de.twomartens.timetable.types.NonEmptyString +import org.bson.types.ObjectId +import org.springframework.data.annotation.CreatedDate +import org.springframework.data.annotation.Id +import org.springframework.data.annotation.LastModifiedDate +import org.springframework.data.mongodb.core.index.CompoundIndex +import org.springframework.data.mongodb.core.mapping.Document +import java.time.Instant + +@Document +@CompoundIndex(def = "{'userId': 1, 'name': 1}", unique = true) +data class TswRoute( + var userId: UserId, + var routeId: RouteId, + var name: NonEmptyString, + var country: Country, + var stations: List, + var depots: List, + var portals: List +) { + @Id + var id: ObjectId = ObjectId() + + @CreatedDate + lateinit var created: Instant + + @LastModifiedDate + lateinit var lastModified: Instant +} diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/model/db/User.kt b/module-model/src/main/kotlin/de/twomartens/timetable/model/db/User.kt similarity index 74% rename from module-server/src/main/kotlin/de/twomartens/timetable/model/db/User.kt rename to module-model/src/main/kotlin/de/twomartens/timetable/model/db/User.kt index 1e79f3a..deef3e2 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/model/db/User.kt +++ b/module-model/src/main/kotlin/de/twomartens/timetable/model/db/User.kt @@ -1,18 +1,21 @@ package de.twomartens.timetable.model.db import de.twomartens.timetable.model.common.UserId +import de.twomartens.timetable.types.Email import de.twomartens.timetable.types.NonEmptyString import org.bson.types.ObjectId import org.springframework.data.annotation.CreatedDate import org.springframework.data.annotation.Id import org.springframework.data.annotation.LastModifiedDate +import org.springframework.data.mongodb.core.index.Indexed import org.springframework.data.mongodb.core.mapping.Document import java.time.Instant @Document data class User( - var userId: UserId, - var name: NonEmptyString + @Indexed(unique = true) var userId: UserId, + var name: NonEmptyString, + var email: Email ) { @Id var id: ObjectId = ObjectId() diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/model/dto/AdditionalService.kt b/module-model/src/main/kotlin/de/twomartens/timetable/model/dto/AdditionalService.kt similarity index 53% rename from module-server/src/main/kotlin/de/twomartens/timetable/model/dto/AdditionalService.kt rename to module-model/src/main/kotlin/de/twomartens/timetable/model/dto/AdditionalService.kt index 5b9c946..0dd169f 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/model/dto/AdditionalService.kt +++ b/module-model/src/main/kotlin/de/twomartens/timetable/model/dto/AdditionalService.kt @@ -1,14 +1,11 @@ package de.twomartens.timetable.model.dto -import de.twomartens.timetable.model.common.FormationId -import de.twomartens.timetable.model.common.Line -import de.twomartens.timetable.model.common.ServiceId import java.time.LocalTime data class AdditionalService( - val id: ServiceId, - val line: Line, - val formationId: FormationId, + val id: String, + val line: String, + val formationId: String, val direction: Direction, val start: Station, val destination: Station, diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/model/dto/AdditionalServiceStop.kt b/module-model/src/main/kotlin/de/twomartens/timetable/model/dto/AdditionalServiceStop.kt similarity index 70% rename from module-server/src/main/kotlin/de/twomartens/timetable/model/dto/AdditionalServiceStop.kt rename to module-model/src/main/kotlin/de/twomartens/timetable/model/dto/AdditionalServiceStop.kt index 52626f3..8b2b090 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/model/dto/AdditionalServiceStop.kt +++ b/module-model/src/main/kotlin/de/twomartens/timetable/model/dto/AdditionalServiceStop.kt @@ -1,12 +1,11 @@ package de.twomartens.timetable.model.dto -import de.twomartens.timetable.types.NonEmptyString import java.time.LocalTime data class AdditionalServiceStop( val stop: Station, val arrivalTime: LocalTime, val departureTime: LocalTime, - val platformAndSection: NonEmptyString, + val platformAndSection: String, val loading: Boolean ) diff --git a/module-model/src/main/kotlin/de/twomartens/timetable/model/dto/Country.kt b/module-model/src/main/kotlin/de/twomartens/timetable/model/dto/Country.kt new file mode 100644 index 0000000..09cee8d --- /dev/null +++ b/module-model/src/main/kotlin/de/twomartens/timetable/model/dto/Country.kt @@ -0,0 +1,6 @@ +package de.twomartens.timetable.model.dto + +data class Country( + val code: String, + val name: String +) diff --git a/module-model/src/main/kotlin/de/twomartens/timetable/model/dto/Depot.kt b/module-model/src/main/kotlin/de/twomartens/timetable/model/dto/Depot.kt new file mode 100644 index 0000000..0b88b66 --- /dev/null +++ b/module-model/src/main/kotlin/de/twomartens/timetable/model/dto/Depot.kt @@ -0,0 +1,9 @@ +package de.twomartens.timetable.model.dto + +data class Depot( + val id: String, + val name: String, + val nearestStation: Station, + val tracks: List, + val travelDurations: List +) diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/model/dto/Direction.kt b/module-model/src/main/kotlin/de/twomartens/timetable/model/dto/Direction.kt similarity index 100% rename from module-server/src/main/kotlin/de/twomartens/timetable/model/dto/Direction.kt rename to module-model/src/main/kotlin/de/twomartens/timetable/model/dto/Direction.kt diff --git a/module-model/src/main/kotlin/de/twomartens/timetable/model/dto/Formation.kt b/module-model/src/main/kotlin/de/twomartens/timetable/model/dto/Formation.kt new file mode 100644 index 0000000..90bf42a --- /dev/null +++ b/module-model/src/main/kotlin/de/twomartens/timetable/model/dto/Formation.kt @@ -0,0 +1,9 @@ +package de.twomartens.timetable.model.dto + +data class Formation( + val id: String, + val name: String, + val trainSimWorldFormation: Formation?, + val formation: String, + val length: Int +) diff --git a/module-model/src/main/kotlin/de/twomartens/timetable/model/dto/Portal.kt b/module-model/src/main/kotlin/de/twomartens/timetable/model/dto/Portal.kt new file mode 100644 index 0000000..b66b5c9 --- /dev/null +++ b/module-model/src/main/kotlin/de/twomartens/timetable/model/dto/Portal.kt @@ -0,0 +1,8 @@ +package de.twomartens.timetable.model.dto + +data class Portal( + val id: String, + val name: String, + val nearestStation: Station, + val travelDurations: List +) diff --git a/module-model/src/main/kotlin/de/twomartens/timetable/model/dto/Rotation.kt b/module-model/src/main/kotlin/de/twomartens/timetable/model/dto/Rotation.kt new file mode 100644 index 0000000..0970446 --- /dev/null +++ b/module-model/src/main/kotlin/de/twomartens/timetable/model/dto/Rotation.kt @@ -0,0 +1,11 @@ +package de.twomartens.timetable.model.dto + +import java.time.LocalTime + +data class Rotation( + val id: String, + val formationId: String, + val firstServiceStartTime: LocalTime, + val lastServiceEndTime: LocalTime, + val startsInVault: Boolean +) diff --git a/module-model/src/main/kotlin/de/twomartens/timetable/model/dto/ServiceFormationStage.kt b/module-model/src/main/kotlin/de/twomartens/timetable/model/dto/ServiceFormationStage.kt new file mode 100644 index 0000000..c708dff --- /dev/null +++ b/module-model/src/main/kotlin/de/twomartens/timetable/model/dto/ServiceFormationStage.kt @@ -0,0 +1,11 @@ +package de.twomartens.timetable.model.dto + +data class ServiceFormationStage( + val id: String, + val line: String, + val direction: Direction, + val formation: String, + val formationReversed: Boolean, + val startStop: ServiceStop, + val destinationStop: ServiceStop +) diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/model/dto/ServiceLinkingStage.kt b/module-model/src/main/kotlin/de/twomartens/timetable/model/dto/ServiceLinkingStage.kt similarity index 56% rename from module-server/src/main/kotlin/de/twomartens/timetable/model/dto/ServiceLinkingStage.kt rename to module-model/src/main/kotlin/de/twomartens/timetable/model/dto/ServiceLinkingStage.kt index ca3f1ba..4f0b1cc 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/model/dto/ServiceLinkingStage.kt +++ b/module-model/src/main/kotlin/de/twomartens/timetable/model/dto/ServiceLinkingStage.kt @@ -1,13 +1,9 @@ package de.twomartens.timetable.model.dto -import de.twomartens.timetable.model.common.FormationId -import de.twomartens.timetable.model.common.Line -import de.twomartens.timetable.model.common.ServiceId - data class ServiceLinkingStage( - val id: ServiceId, - val formationId: FormationId, - val line: Line, + val id: String, + val formationId: String, + val line: String, val direction: Direction, val formationReversed: Boolean, val startStop: ServiceStop, diff --git a/module-model/src/main/kotlin/de/twomartens/timetable/model/dto/ServiceRotationStage.kt b/module-model/src/main/kotlin/de/twomartens/timetable/model/dto/ServiceRotationStage.kt new file mode 100644 index 0000000..69b6f2c --- /dev/null +++ b/module-model/src/main/kotlin/de/twomartens/timetable/model/dto/ServiceRotationStage.kt @@ -0,0 +1,15 @@ +package de.twomartens.timetable.model.dto + +import de.twomartens.timetable.model.common.Line +import de.twomartens.timetable.model.common.ServiceId + +data class ServiceRotationStage( + val id: ServiceId, + val line: Line, + val originStop: String, + val virtualDestinations: List, + val startingFrom: String, + val endingIn: String, + val serviceStartTime: String, + val stops: List +) diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/model/dto/ServiceStop.kt b/module-model/src/main/kotlin/de/twomartens/timetable/model/dto/ServiceStop.kt similarity index 67% rename from module-server/src/main/kotlin/de/twomartens/timetable/model/dto/ServiceStop.kt rename to module-model/src/main/kotlin/de/twomartens/timetable/model/dto/ServiceStop.kt index 66b693b..142642b 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/model/dto/ServiceStop.kt +++ b/module-model/src/main/kotlin/de/twomartens/timetable/model/dto/ServiceStop.kt @@ -1,11 +1,9 @@ package de.twomartens.timetable.model.dto -import de.twomartens.timetable.types.NonEmptyString - data class ServiceStop( val stop: Station, val arrivalTime: String, val departureTime: String, - val platform: NonEmptyString, + val platform: String, val section: String ) diff --git a/module-model/src/main/kotlin/de/twomartens/timetable/model/dto/Station.kt b/module-model/src/main/kotlin/de/twomartens/timetable/model/dto/Station.kt new file mode 100644 index 0000000..77f919c --- /dev/null +++ b/module-model/src/main/kotlin/de/twomartens/timetable/model/dto/Station.kt @@ -0,0 +1,9 @@ +package de.twomartens.timetable.model.dto + +import de.twomartens.timetable.model.common.Platform + +data class Station( + val id: String, + val name: String, + val platforms: List +) diff --git a/module-model/src/main/kotlin/de/twomartens/timetable/model/dto/Timetable.kt b/module-model/src/main/kotlin/de/twomartens/timetable/model/dto/Timetable.kt new file mode 100644 index 0000000..704b840 --- /dev/null +++ b/module-model/src/main/kotlin/de/twomartens/timetable/model/dto/Timetable.kt @@ -0,0 +1,13 @@ +package de.twomartens.timetable.model.dto + +import java.time.LocalDate + +data class Timetable( + val id: String, + val name: String, + val routeId: String, + val routeName: String, + val date: LocalDate, + val state: TimetableState, + val numberOfServices: Int +) diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/model/dto/TimetableState.kt b/module-model/src/main/kotlin/de/twomartens/timetable/model/dto/TimetableState.kt similarity index 100% rename from module-server/src/main/kotlin/de/twomartens/timetable/model/dto/TimetableState.kt rename to module-model/src/main/kotlin/de/twomartens/timetable/model/dto/TimetableState.kt diff --git a/module-model/src/main/kotlin/de/twomartens/timetable/model/dto/Track.kt b/module-model/src/main/kotlin/de/twomartens/timetable/model/dto/Track.kt new file mode 100644 index 0000000..4c9fbc7 --- /dev/null +++ b/module-model/src/main/kotlin/de/twomartens/timetable/model/dto/Track.kt @@ -0,0 +1,7 @@ +package de.twomartens.timetable.model.dto + +data class Track( + val id: Int, + val name: String, + val capacity: Int +) diff --git a/module-model/src/main/kotlin/de/twomartens/timetable/model/dto/TravelDuration.kt b/module-model/src/main/kotlin/de/twomartens/timetable/model/dto/TravelDuration.kt new file mode 100644 index 0000000..b43fff4 --- /dev/null +++ b/module-model/src/main/kotlin/de/twomartens/timetable/model/dto/TravelDuration.kt @@ -0,0 +1,6 @@ +package de.twomartens.timetable.model.dto + +data class TravelDuration( + val formation: Formation, + val time: Long +) diff --git a/module-model/src/main/kotlin/de/twomartens/timetable/model/dto/TswRoute.kt b/module-model/src/main/kotlin/de/twomartens/timetable/model/dto/TswRoute.kt new file mode 100644 index 0000000..23bb9aa --- /dev/null +++ b/module-model/src/main/kotlin/de/twomartens/timetable/model/dto/TswRoute.kt @@ -0,0 +1,13 @@ +package de.twomartens.timetable.model.dto + +data class TswRoute( + val id: String, + val name: String, + val country: Country, + val stations: List, + val firstStation: Station, + val lastStation: Station, + val numberOfStations: Int, + val depots: List, + val portals: List +) diff --git a/module-model/src/main/kotlin/de/twomartens/timetable/model/dto/User.kt b/module-model/src/main/kotlin/de/twomartens/timetable/model/dto/User.kt new file mode 100644 index 0000000..66e0d59 --- /dev/null +++ b/module-model/src/main/kotlin/de/twomartens/timetable/model/dto/User.kt @@ -0,0 +1,7 @@ +package de.twomartens.timetable.model.dto + +data class User( + val id: String, + val name: String, + val email: String +) diff --git a/module-model/src/main/kotlin/de/twomartens/timetable/model/mapper/StationMapper.kt b/module-model/src/main/kotlin/de/twomartens/timetable/model/mapper/StationMapper.kt new file mode 100644 index 0000000..4ef640a --- /dev/null +++ b/module-model/src/main/kotlin/de/twomartens/timetable/model/mapper/StationMapper.kt @@ -0,0 +1,36 @@ +package de.twomartens.timetable.model.mapper + +import de.twomartens.timetable.model.common.CountryCode +import de.twomartens.timetable.model.common.StationId +import de.twomartens.timetable.model.dto.Station +import de.twomartens.timetable.types.NonEmptyString +import org.mapstruct.* + +@Mapper( + collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED, + nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS, + unmappedTargetPolicy = ReportingPolicy.IGNORE +) +interface StationMapper { + @Mapping(target = "id", ignore = true) + @Mapping(target = "created", ignore = true) + @Mapping(target = "lastModified", ignore = true) + fun mapToDB(countryCode: CountryCode, dto: Station): de.twomartens.timetable.model.db.Station { + return de.twomartens.timetable.model.db.Station( + StationId.of(NonEmptyString(countryCode.countryCode.value + "-" + dto.id)), + countryCode, + NonEmptyString(dto.name), + dto.platforms + ) + } + + fun mapStationsToDto(stations: List): List + + fun mapToDto(db: de.twomartens.timetable.model.db.Station): Station { + return Station( + db.stationId.stationIdWithinCountry, + db.name.value, + db.platforms + ) + } +} \ No newline at end of file diff --git a/module-model/src/main/kotlin/de/twomartens/timetable/model/repository/StationRepository.kt b/module-model/src/main/kotlin/de/twomartens/timetable/model/repository/StationRepository.kt new file mode 100644 index 0000000..1683f55 --- /dev/null +++ b/module-model/src/main/kotlin/de/twomartens/timetable/model/repository/StationRepository.kt @@ -0,0 +1,30 @@ +package de.twomartens.timetable.model.repository + +import de.twomartens.timetable.model.common.StationId +import de.twomartens.timetable.model.db.Station +import org.bson.types.ObjectId +import org.springframework.data.mongodb.repository.Aggregation +import org.springframework.data.mongodb.repository.MongoRepository + +interface StationRepository : MongoRepository { + fun findByCountryCodeAndStationId(countryCode: String, stationId: StationId): Station? + fun findAllByCountryCode(countryCode: String): List + + @Aggregation(pipeline = [ + "{\$search: { index: \"stations\", returnStoredSource: true, compound: {must: [{phrase: {query: ?0, path: \"countryCode\"}},{autocomplete: {query: ?1,path: \"name\",tokenOrder: \"sequential\"}}]}}}", + "{\$limit: 10}", + "{\$lookup: { from: \"station\", localField: \"_id\", foreignField: \"_id\", as: \"document\" }}", + "{\$unwind: \"\$document\"}", + "{\$replaceWith: \"\$document\"}" + ]) + fun findAllByCountryCodeAndNameContainingIgnoreCase(countryCode: String, name: String): List + + @Aggregation(pipeline = [ + "{\$search: { index: \"stations\", returnStoredSource: true, autocomplete: {query: ?0, path: \"name\",tokenOrder: \"sequential\"}}}", + "{\$limit: 10}", + "{\$lookup: { from: \"station\", localField: \"_id\", foreignField: \"_id\", as: \"document\" }}", + "{\$unwind: \"\$document\"}", + "{\$replaceWith: \"\$document\"}" + ]) + fun findAllByNameContainingIgnoreCase(name: String): List +} \ No newline at end of file diff --git a/module-server/server.gradle.kts b/module-server/server.gradle.kts index 8a9fdae..8ef96b4 100644 --- a/module-server/server.gradle.kts +++ b/module-server/server.gradle.kts @@ -1,17 +1,23 @@ plugins { - id("twomartens.spring-boot-cloud") + id("twomartens.spring-boot-cloud-application") id("twomartens.kotlin") kotlin("kapt") } dependencies { + implementation(project(":bahnApi")) implementation(project(":common")) - implementation(libs.mapstruct.base) - implementation(libs.bundles.spring.boot.security) + implementation(project(":model")) + implementation(project(":support")) + implementation(libs.jaxb.impl) implementation(libs.jakarta.xml.binding) + + implementation(libs.mapstruct.base) annotationProcessor(libs.mapstruct.processor) kapt(libs.mapstruct.processor) + + implementation(libs.bundles.spring.boot.security) implementation(libs.spring.cloud.starter.config) implementation(libs.spring.cloud.leader.election) implementation(libs.spring.cloud.starter.bus.kafka) diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/MainApplication.kt b/module-server/src/main/kotlin/de/twomartens/timetable/MainApplication.kt index d2469bd..a999313 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/MainApplication.kt +++ b/module-server/src/main/kotlin/de/twomartens/timetable/MainApplication.kt @@ -1,6 +1,6 @@ package de.twomartens.timetable -import de.twomartens.timetable.support.model.LeadershipStatus +import de.twomartens.support.model.LeadershipStatus import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.context.event.ApplicationReadyEvent import org.springframework.boot.runApplication @@ -13,7 +13,7 @@ import org.springframework.scheduling.annotation.EnableScheduling @EnableMongoAuditing @EnableScheduling -@SpringBootApplication +@SpringBootApplication(scanBasePackages = ["de.twomartens.support", "de.twomartens.timetable"]) open class MainApplication( private val leadershipStatus: LeadershipStatus, private val leaderProperties: LeaderProperties diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/auth/UserController.kt b/module-server/src/main/kotlin/de/twomartens/timetable/auth/UserController.kt new file mode 100644 index 0000000..da453c7 --- /dev/null +++ b/module-server/src/main/kotlin/de/twomartens/timetable/auth/UserController.kt @@ -0,0 +1,84 @@ +package de.twomartens.timetable.auth + +import de.twomartens.timetable.model.common.UserId +import de.twomartens.timetable.model.dto.User +import de.twomartens.timetable.types.Email +import de.twomartens.timetable.types.NonEmptyString +import io.swagger.v3.oas.annotations.Operation +import io.swagger.v3.oas.annotations.Parameter +import io.swagger.v3.oas.annotations.media.Content +import io.swagger.v3.oas.annotations.media.Schema +import io.swagger.v3.oas.annotations.responses.ApiResponse +import io.swagger.v3.oas.annotations.security.SecurityRequirement +import io.swagger.v3.oas.annotations.tags.Tag +import org.mapstruct.factory.Mappers +import org.springframework.http.HttpStatus +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.* + +@RestController +@RequestMapping(value = ["/v1/users"]) +@Tag(name = "Users", description = "all requests relating to user resources") +class UserController( + private val userRepository: UserRepository +) { + + private val mapper = Mappers.getMapper(UserMapper::class.java) + + @Operation( + summary = "Store user", + responses = [ApiResponse( + responseCode = "200", + description = "User was updated", + content = [Content( + schema = Schema(implementation = User::class) + )] + ), ApiResponse( + responseCode = "201", + description = "User was created", + content = [Content( + schema = Schema(implementation = User::class) + )] + ), ApiResponse( + responseCode = "403", + description = "Access forbidden for user", + content = [Content(mediaType = "text/plain")] + )] + ) + @SecurityRequirement(name = "bearer") + @SecurityRequirement(name = "oauth2") + @PutMapping("/{userId}") + fun putUser( + @PathVariable @Parameter(description = "The id of the user", + example = "1", + required = true) userId: String, + @RequestBody(required = true) @io.swagger.v3.oas.annotations.parameters.RequestBody( + required = true, + content = [ + Content( + schema = Schema(implementation = User::class) + ) + ]) body: User + ): ResponseEntity { + var created = false + + val userIdConverted = UserId.of(NonEmptyString(userId)) + var user = userRepository.findByUserId(userIdConverted) + if (user == null) { + created = true + user = mapper.mapToDB(body) + } else { + user.name = NonEmptyString(body.name) + user.email = Email.of(NonEmptyString(body.email)) + } + + userRepository.save(user) + + val updatedUser = mapper.mapToDto(user) + return if (created) { + ResponseEntity.status(HttpStatus.CREATED).body(updatedUser) + } else { + ResponseEntity.ok(updatedUser) + } + } +} \ No newline at end of file diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/auth/UserMapper.kt b/module-server/src/main/kotlin/de/twomartens/timetable/auth/UserMapper.kt new file mode 100644 index 0000000..ee7228c --- /dev/null +++ b/module-server/src/main/kotlin/de/twomartens/timetable/auth/UserMapper.kt @@ -0,0 +1,34 @@ +package de.twomartens.timetable.auth + +import de.twomartens.timetable.model.common.UserId +import de.twomartens.timetable.model.dto.User +import de.twomartens.timetable.types.Email +import de.twomartens.timetable.types.NonEmptyString +import org.mapstruct.* + +@Mapper( + collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED, + nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS, + unmappedTargetPolicy = ReportingPolicy.IGNORE +) +interface UserMapper { + + @Mapping(target = "id", ignore = true) + @Mapping(target = "created", ignore = true) + @Mapping(target = "lastModified", ignore = true) + fun mapToDB(dto: User): de.twomartens.timetable.model.db.User { + return de.twomartens.timetable.model.db.User( + UserId.of(NonEmptyString(dto.id)), + NonEmptyString(dto.name), + Email.of(NonEmptyString(dto.email)) + ) + } + + fun mapToDto(db: de.twomartens.timetable.model.db.User): User { + return User( + db.userId.value, + db.name.value, + db.email.value + ) + } +} \ No newline at end of file diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/auth/UserRepository.kt b/module-server/src/main/kotlin/de/twomartens/timetable/auth/UserRepository.kt new file mode 100644 index 0000000..eecf6f3 --- /dev/null +++ b/module-server/src/main/kotlin/de/twomartens/timetable/auth/UserRepository.kt @@ -0,0 +1,11 @@ +package de.twomartens.timetable.auth + +import de.twomartens.timetable.model.common.UserId +import de.twomartens.timetable.model.db.User +import org.bson.types.ObjectId +import org.springframework.data.mongodb.repository.MongoRepository + +interface UserRepository : MongoRepository { + fun existsByUserId(userId: UserId): Boolean + fun findByUserId(userId: UserId): User? +} \ No newline at end of file diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/service/BahnApiService.kt b/module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/service/BahnApiService.kt deleted file mode 100644 index ca6dd58..0000000 --- a/module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/service/BahnApiService.kt +++ /dev/null @@ -1,58 +0,0 @@ -package de.twomartens.timetable.bahnApi.service - -import de.twomartens.timetable.bahnApi.model.Eva -import de.twomartens.timetable.bahnApi.model.dto.BahnStation -import de.twomartens.timetable.bahnApi.model.dto.BahnStations -import de.twomartens.timetable.bahnApi.model.dto.BahnTimetable -import de.twomartens.timetable.bahnApi.property.BahnApiProperties -import de.twomartens.timetable.types.HourAtDay -import org.springframework.http.HttpEntity -import org.springframework.http.HttpHeaders -import org.springframework.http.HttpMethod -import org.springframework.http.MediaType -import org.springframework.stereotype.Service -import org.springframework.web.client.RestTemplate -import java.time.LocalTime -import java.time.format.DateTimeFormatter - -@Service -class BahnApiService( - private val restTemplate: RestTemplate, - private val properties: BahnApiProperties -) { - fun fetchStations(pattern: String): List { - val requestEntity = buildRequestEntity() - val response = restTemplate.exchange( - "https://apis.deutschebahn.com/db-api-marketplace/apis/timetables/v1/station/${pattern}", - HttpMethod.GET, - requestEntity, - BahnStations::class.java - ) - val body = response.body - return body?.stations ?: listOf() - } - - fun fetchTimetable(eva: Eva, hourAtDay: HourAtDay): BahnTimetable { - val requestEntity = buildRequestEntity() - val dateFormatter = DateTimeFormatter.ofPattern("yyMMdd") - val timeFormatter = DateTimeFormatter.ofPattern("HH") - val time = LocalTime.of(hourAtDay.hour.value, 0) - val response = restTemplate.exchange( - "https://apis.deutschebahn.com/db-api-marketplace/apis/timetables/v1/plan/" + - "${eva}/${hourAtDay.date.format(dateFormatter)}/${time.format(timeFormatter)}", - HttpMethod.GET, - requestEntity, - BahnTimetable::class.java - ) - return response.body!! - } - - private fun buildRequestEntity(): HttpEntity { - val headers = HttpHeaders() - headers.accept = mutableListOf(MediaType.APPLICATION_XML) - headers.contentType = MediaType.APPLICATION_XML - headers.set("DB-Client-Id", properties.clientId) - headers.set("DB-Api-Key", properties.clientSecret) - return HttpEntity(headers) - } -} \ No newline at end of file diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/service/BahnDatabaseService.kt b/module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/service/BahnDatabaseService.kt deleted file mode 100644 index 2d2eebe..0000000 --- a/module-server/src/main/kotlin/de/twomartens/timetable/bahnApi/service/BahnDatabaseService.kt +++ /dev/null @@ -1,29 +0,0 @@ -package de.twomartens.timetable.bahnApi.service - -import de.twomartens.timetable.bahnApi.mapper.BahnStationMapper -import de.twomartens.timetable.bahnApi.mapper.BahnTimetableMapper -import de.twomartens.timetable.bahnApi.model.dto.BahnStation -import de.twomartens.timetable.bahnApi.model.dto.BahnTimetable -import de.twomartens.timetable.bahnApi.repository.BahnStationRepository -import de.twomartens.timetable.bahnApi.repository.BahnTimetableRepository -import de.twomartens.timetable.types.HourAtDay -import org.mapstruct.factory.Mappers -import org.springframework.stereotype.Service - -@Service -class BahnDatabaseService( - private val bahnStationRepository: BahnStationRepository, - private val bahnTimetableRepository: BahnTimetableRepository -) { - private val bahnStationMapper = Mappers.getMapper(BahnStationMapper::class.java) - private val bahnTimetableMapper = Mappers.getMapper(BahnTimetableMapper::class.java) - - fun storeStations(stations: List) { - bahnStationRepository.saveAll(stations - .map { bahnStationMapper.mapToDB(it) }) - } - - fun storeTimetable(timetable: BahnTimetable, hourAtDay: HourAtDay) { - bahnTimetableRepository.save(bahnTimetableMapper.mapToDB(timetable, hourAtDay)) - } -} \ No newline at end of file diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/support/configuration/BusConfiguration.kt b/module-server/src/main/kotlin/de/twomartens/timetable/configuration/BusConfiguration.kt similarity index 75% rename from module-server/src/main/kotlin/de/twomartens/timetable/support/configuration/BusConfiguration.kt rename to module-server/src/main/kotlin/de/twomartens/timetable/configuration/BusConfiguration.kt index eed7088..aa5c847 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/support/configuration/BusConfiguration.kt +++ b/module-server/src/main/kotlin/de/twomartens/timetable/configuration/BusConfiguration.kt @@ -1,8 +1,8 @@ -package de.twomartens.timetable.support.configuration +package de.twomartens.timetable.configuration import org.springframework.cloud.bus.jackson.RemoteApplicationEventScan import org.springframework.context.annotation.Configuration @Configuration -@RemoteApplicationEventScan(basePackages = ["de.twomartens.timetable"]) +@RemoteApplicationEventScan(basePackages = ["de.twomartens.timetable", "de.twomartens.support"]) open class BusConfiguration diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/support/configuration/OpenApiConfiguration.kt b/module-server/src/main/kotlin/de/twomartens/timetable/configuration/OpenApiConfiguration.kt similarity index 96% rename from module-server/src/main/kotlin/de/twomartens/timetable/support/configuration/OpenApiConfiguration.kt rename to module-server/src/main/kotlin/de/twomartens/timetable/configuration/OpenApiConfiguration.kt index 995c587..4e43b38 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/support/configuration/OpenApiConfiguration.kt +++ b/module-server/src/main/kotlin/de/twomartens/timetable/configuration/OpenApiConfiguration.kt @@ -1,4 +1,4 @@ -package de.twomartens.timetable.support.configuration +package de.twomartens.timetable.configuration import io.swagger.v3.oas.annotations.enums.SecuritySchemeType import io.swagger.v3.oas.annotations.security.OAuthFlow diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/configuration/PropertyConfiguration.kt b/module-server/src/main/kotlin/de/twomartens/timetable/configuration/PropertyConfiguration.kt index aee3b4a..749e6a5 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/configuration/PropertyConfiguration.kt +++ b/module-server/src/main/kotlin/de/twomartens/timetable/configuration/PropertyConfiguration.kt @@ -1,10 +1,11 @@ package de.twomartens.timetable.configuration +import de.twomartens.support.property.HealthCheckProperties +import de.twomartens.support.property.RestTemplateTimeoutProperties +import de.twomartens.support.property.StatusProbeProperties +import de.twomartens.support.property.TimeProperties import de.twomartens.timetable.bahnApi.property.BahnApiProperties -import de.twomartens.timetable.property.RestTemplateTimeoutProperties import de.twomartens.timetable.property.ServiceProperties -import de.twomartens.timetable.property.StatusProbeProperties -import de.twomartens.timetable.property.TimeProperties import org.springframework.boot.context.properties.EnableConfigurationProperties import org.springframework.cloud.kubernetes.commons.leader.LeaderProperties import org.springframework.context.annotation.Configuration @@ -12,5 +13,6 @@ import org.springframework.context.annotation.Configuration @Configuration @EnableConfigurationProperties(RestTemplateTimeoutProperties::class, ServiceProperties::class, StatusProbeProperties::class, TimeProperties::class, BahnApiProperties::class, + HealthCheckProperties::class, LeaderProperties::class) open class PropertyConfiguration diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/support/configuration/WebConfiguration.kt b/module-server/src/main/kotlin/de/twomartens/timetable/configuration/WebConfiguration.kt similarity index 82% rename from module-server/src/main/kotlin/de/twomartens/timetable/support/configuration/WebConfiguration.kt rename to module-server/src/main/kotlin/de/twomartens/timetable/configuration/WebConfiguration.kt index 24b88bf..ae782f7 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/support/configuration/WebConfiguration.kt +++ b/module-server/src/main/kotlin/de/twomartens/timetable/configuration/WebConfiguration.kt @@ -1,6 +1,6 @@ -package de.twomartens.timetable.support.configuration +package de.twomartens.timetable.configuration -import de.twomartens.timetable.support.interceptor.HeaderInterceptorRest +import de.twomartens.support.interceptor.HeaderInterceptorRest import org.springframework.context.annotation.Configuration import org.springframework.http.HttpMethod import org.springframework.web.servlet.config.annotation.CorsRegistry @@ -17,7 +17,8 @@ open class WebConfiguration(private val headerInterceptorRest: HeaderInterceptor val registration = registry.addMapping("/**") registration.allowedMethods( HttpMethod.GET.name(), HttpMethod.POST.name(), - HttpMethod.PUT.name(), HttpMethod.HEAD.name() + HttpMethod.PUT.name(), HttpMethod.HEAD.name(), + HttpMethod.DELETE.name() ) registration.allowCredentials(true) registration.allowedOrigins( diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/support/configuration/WebSecurityConfiguration.kt b/module-server/src/main/kotlin/de/twomartens/timetable/configuration/WebSecurityConfiguration.kt similarity index 94% rename from module-server/src/main/kotlin/de/twomartens/timetable/support/configuration/WebSecurityConfiguration.kt rename to module-server/src/main/kotlin/de/twomartens/timetable/configuration/WebSecurityConfiguration.kt index 0263eec..dd19cd3 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/support/configuration/WebSecurityConfiguration.kt +++ b/module-server/src/main/kotlin/de/twomartens/timetable/configuration/WebSecurityConfiguration.kt @@ -1,6 +1,6 @@ -package de.twomartens.timetable.support.configuration +package de.twomartens.timetable.configuration -import de.twomartens.timetable.support.security.SpringPolicyEnforcerFilter +import de.twomartens.support.security.SpringPolicyEnforcerFilter import org.keycloak.adapters.authorization.spi.ConfigurationResolver import org.keycloak.adapters.authorization.spi.HttpRequest import org.keycloak.representations.adapters.config.PolicyEnforcerConfig @@ -72,10 +72,10 @@ open class WebSecurityConfiguration { companion object { private val PERMITTED_PATHS: Collection = listOf( - "/timetable/v1/healthCheck", + "/timetable/healthCheck", "/actuator/**", - "/timetable/v1/doc/**", - "/timetable/v1/api-docs/**", + "/doc/v1/timetable/**", + "/api-docs/v1/timetable/**", "/error", "/timetable/version", ) diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/controller/TimetableController.kt b/module-server/src/main/kotlin/de/twomartens/timetable/controller/TimetableController.kt deleted file mode 100644 index d7db7e8..0000000 --- a/module-server/src/main/kotlin/de/twomartens/timetable/controller/TimetableController.kt +++ /dev/null @@ -1,59 +0,0 @@ -package de.twomartens.timetable.controller - -import de.twomartens.timetable.bahnApi.service.ScheduledTaskService -import de.twomartens.timetable.model.common.UserId -import de.twomartens.timetable.repository.TswRouteRepository -import de.twomartens.timetable.types.NonEmptyString -import io.swagger.v3.oas.annotations.Operation -import io.swagger.v3.oas.annotations.Parameter -import io.swagger.v3.oas.annotations.responses.ApiResponse -import io.swagger.v3.oas.annotations.tags.Tag -import org.springframework.http.HttpStatus -import org.springframework.web.bind.annotation.PathVariable -import org.springframework.web.bind.annotation.PostMapping -import org.springframework.web.bind.annotation.RequestMapping -import org.springframework.web.bind.annotation.RestController -import org.springframework.web.server.ResponseStatusException -import java.time.Clock -import java.time.LocalDate - -@RestController -@RequestMapping(value = ["/timetable/v1"]) -@Tag(name = "Timetables", description = "all requests relating to timetables") -class TimetableController( - private val clock: Clock, - private val routeRepository: TswRouteRepository, - private val scheduledTaskService: ScheduledTaskService -) { - @Operation( - summary = "Task the backend with fetching a timetable for specified route and date", - responses = [ApiResponse( - responseCode = "200", - description = "Timetable will be fetched as requested" - ), ApiResponse( - responseCode = "400", - description = "Request does not follow specification" - )] - ) - @PostMapping("/{userId}/{routeName}/fetchTimetable/{date}") - fun scheduleFetch( - @PathVariable @Parameter(description = "The id of the user", - example = "1", - required = true) userId: UserId, - @PathVariable @Parameter(description = "The name of the TSW route.", - example = "CologneAachen", - required = true) routeName: NonEmptyString, - @PathVariable @Parameter( - description = "The date to fetch timetable for. Must be in the future.", - example = "2023-09-12", - required = true) date: LocalDate - ) { - if (!date.isAfter(LocalDate.now(clock))) { - throw ResponseStatusException(HttpStatus.BAD_REQUEST, "Date must be in the future") - } - val route = routeRepository.findByUserIdAndName(userId, routeName) - ?: throw ResponseStatusException(HttpStatus.BAD_REQUEST, - "Route name must belong to existing route") - scheduledTaskService.triggerTimetableFetch(route, date) - } -} \ No newline at end of file diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/formation/FormationController.kt b/module-server/src/main/kotlin/de/twomartens/timetable/formation/FormationController.kt new file mode 100644 index 0000000..280d4fc --- /dev/null +++ b/module-server/src/main/kotlin/de/twomartens/timetable/formation/FormationController.kt @@ -0,0 +1,261 @@ +package de.twomartens.timetable.formation + +import de.twomartens.timetable.auth.UserRepository +import de.twomartens.timetable.model.common.FormationId +import de.twomartens.timetable.model.common.UserId +import de.twomartens.timetable.model.dto.Formation +import de.twomartens.timetable.types.NonEmptyString +import de.twomartens.timetable.types.ZeroOrPositiveInteger +import io.swagger.v3.oas.annotations.Operation +import io.swagger.v3.oas.annotations.Parameter +import io.swagger.v3.oas.annotations.media.ArraySchema +import io.swagger.v3.oas.annotations.media.Content +import io.swagger.v3.oas.annotations.media.Schema +import io.swagger.v3.oas.annotations.responses.ApiResponse +import io.swagger.v3.oas.annotations.security.SecurityRequirement +import io.swagger.v3.oas.annotations.tags.Tag +import org.mapstruct.factory.Mappers +import org.springframework.http.HttpStatus +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.* + +@RestController +@RequestMapping(value = ["/v1/formations"]) +@Tag(name = "Formations", description = "all requests relating to formation resources") +class FormationController( + private val formationRepository: FormationRepository, + private val userRepository: UserRepository +) { + private val mapper = Mappers.getMapper(FormationMapper::class.java) + + @Operation( + summary = "Access formations of user and filter by name", + responses = [ApiResponse( + responseCode = "200", + description = "List of found formations", + content = [Content( + array = ArraySchema(schema = Schema(implementation = Formation::class)) + )] + ), ApiResponse( + responseCode = "403", + description = "Access forbidden for user", + content = [Content(mediaType = "text/plain")] + )] + ) + @SecurityRequirement(name = "bearer") + @SecurityRequirement(name = "oauth2") + @GetMapping("/{userId}/") + fun getFormations( + @PathVariable @Parameter(description = "The id of the user", + example = "1", + required = true) userId: String, + @RequestParam(name = "name", required = false) @Parameter(description = "Searched name", + example = "ICE", + required = false) name: String? + ): ResponseEntity> { + val userIdConverted = UserId.of(NonEmptyString(userId)) + val userExists = userRepository.existsByUserId(userIdConverted) + if (!userExists) { + return ResponseEntity.status(HttpStatus.FORBIDDEN).build() + } + + val formations = if (!name.isNullOrBlank()) { + formationRepository.findAllByUserIdAndNameContainingIgnoreCase(userIdConverted, name) + } else { + formationRepository.findAllByUserId(userIdConverted) + } + + val formationsDto = formations.map { + val trainSimWorldFormation = if (it.trainSimWorldFormationId != null) { + formationRepository.findByUserIdAndFormationId(userIdConverted, it.trainSimWorldFormationId!!) + } else { + null + } + mapper.mapToDto(it, trainSimWorldFormation) + } + return ResponseEntity.ok(formationsDto) + } + + @Operation( + summary = "Access formation with id belonging to user", + responses = [ApiResponse( + responseCode = "200", + description = "Formation exists and was returned", + content = [Content( + schema = Schema(implementation = Formation::class) + )] + ), ApiResponse( + responseCode = "404", + description = "Formation was not found", + content = [Content(mediaType = "text/plain")] + ), ApiResponse( + responseCode = "403", + description = "Access forbidden for user", + content = [Content(mediaType = "text/plain")] + )] + ) + @SecurityRequirement(name = "bearer") + @SecurityRequirement(name = "oauth2") + @GetMapping("/{userId}/{id}") + fun getFormation( + @PathVariable @Parameter(description = "The id of the user", + example = "1", + required = true) userId: String, + @PathVariable @Parameter(description = "The id of the formation", + example = "1", + required = true) id: String + ): ResponseEntity { + val userIdConverted = UserId.of(NonEmptyString(userId)) + val formationIdConverted = FormationId.of(NonEmptyString(id)) + val userExists = userRepository.existsByUserId(userIdConverted) + if (!userExists) { + return ResponseEntity.status(HttpStatus.FORBIDDEN).build() + } + + val formation = formationRepository.findByUserIdAndFormationId(userIdConverted, formationIdConverted) + ?: return ResponseEntity.notFound().build() + val trainSimWorldFormation = if (formation.trainSimWorldFormationId != null) { + formationRepository.findByUserIdAndFormationId(userIdConverted, formation.trainSimWorldFormationId!!) + } else { + null + } + return ResponseEntity.ok(mapper.mapToDto(formation, trainSimWorldFormation)) + } + + @Operation( + summary = "Store formation with id belonging to user", + responses = [ApiResponse( + responseCode = "200", + description = "Formation was updated", + content = [Content( + schema = Schema(implementation = Formation::class) + )] + ), ApiResponse( + responseCode = "201", + description = "Formation was created", + content = [Content( + schema = Schema(implementation = Formation::class) + )] + ), ApiResponse( + responseCode = "403", + description = "Access forbidden for user", + content = [Content(mediaType = "text/plain")] + )] + ) + @SecurityRequirement(name = "bearer") + @SecurityRequirement(name = "oauth2") + @PutMapping("/{userId}/{id}") + fun putFormation( + @PathVariable @Parameter(description = "The id of the user", + example = "1", + required = true) userId: String, + @PathVariable @Parameter(description = "The id of the formation", + example = "1", + required = true) id: String, + @RequestBody(required = true) @io.swagger.v3.oas.annotations.parameters.RequestBody( + required = true, + content = [ + Content( + schema = Schema(implementation = Formation::class) + ) + ]) body: Formation + ): ResponseEntity { + val userIdConverted = UserId.of(NonEmptyString(userId)) + val formationIdConverted = FormationId.of(NonEmptyString(id)) + val userExists = userRepository.existsByUserId(userIdConverted) + if (!userExists) { + return ResponseEntity.status(HttpStatus.FORBIDDEN).build() + } + + var formation = formationRepository.findByUserIdAndFormationId(userIdConverted, formationIdConverted) + val trainSimWorldFormation = createOrUpdateTrainSimWorldFormation(userIdConverted, body.trainSimWorldFormation) + if (trainSimWorldFormation != null) { + formationRepository.save(trainSimWorldFormation) + } + + var created = false + + if (formation == null) { + created = true + formation = mapper.mapToDB(userIdConverted, body) + } else { + formation.name = NonEmptyString(body.name) + formation.trainSimWorldFormationId = if (body.trainSimWorldFormation != null) + FormationId.of(NonEmptyString(body.trainSimWorldFormation!!.id)) else null + formation.formation = body.formation + formation.length = ZeroOrPositiveInteger(body.length) + } + + formationRepository.save(formation) + + val updatedFormation = mapper.mapToDto(formation, trainSimWorldFormation) + return if (created) { + ResponseEntity.status(HttpStatus.CREATED).body(updatedFormation) + } else { + ResponseEntity.ok(updatedFormation) + } + } + + private fun createOrUpdateTrainSimWorldFormation( + userId: UserId, + formation: Formation? + ): de.twomartens.timetable.model.db.Formation? { + if (formation == null) { + return null + } + + var trainSimWorldFormation = formationRepository.findByUserIdAndFormationId( + userId, FormationId.of(NonEmptyString(formation.id))) + if (trainSimWorldFormation == null) { + trainSimWorldFormation = mapper.mapToDB(userId, formation) + trainSimWorldFormation.trainSimWorldFormationId = null + } else { + trainSimWorldFormation.name = NonEmptyString(formation.name) + trainSimWorldFormation.trainSimWorldFormationId = null + trainSimWorldFormation.formation = formation.formation + trainSimWorldFormation.length = ZeroOrPositiveInteger(formation.length) + } + return trainSimWorldFormation + } + + @Operation( + summary = "Delete formation with id belonging to user", + responses = [ApiResponse( + responseCode = "204", + description = "Formation was deleted", + content = [Content(mediaType = "text/plain")] + ), ApiResponse( + responseCode = "404", + description = "Formation was not found", + content = [Content(mediaType = "text/plain")] + ), ApiResponse( + responseCode = "403", + description = "Access forbidden for user", + content = [Content(mediaType = "text/plain")] + )] + ) + @SecurityRequirement(name = "bearer") + @SecurityRequirement(name = "oauth2") + @DeleteMapping("/{userId}/{id}") + fun deleteFormation( + @PathVariable @Parameter(description = "The id of the user", + example = "1", + required = true) userId: String, + @PathVariable @Parameter(description = "The id of the formation", + example = "1", + required = true) id: String, + ): ResponseEntity { + val userIdConverted = UserId.of(NonEmptyString(userId)) + val formationIdConverted = FormationId.of(NonEmptyString(id)) + val userExists = userRepository.existsByUserId(userIdConverted) + if (!userExists) { + return ResponseEntity.status(HttpStatus.FORBIDDEN).build() + } + + val formation = formationRepository.findByUserIdAndFormationId(userIdConverted, formationIdConverted) + ?: return ResponseEntity.notFound().build() + formationRepository.delete(formation) + + return ResponseEntity.status(HttpStatus.NO_CONTENT).build() + } +} \ No newline at end of file diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/formation/FormationMapper.kt b/module-server/src/main/kotlin/de/twomartens/timetable/formation/FormationMapper.kt new file mode 100644 index 0000000..e245ea3 --- /dev/null +++ b/module-server/src/main/kotlin/de/twomartens/timetable/formation/FormationMapper.kt @@ -0,0 +1,49 @@ +package de.twomartens.timetable.formation + +import de.twomartens.timetable.model.common.FormationId +import de.twomartens.timetable.model.common.UserId +import de.twomartens.timetable.model.dto.Formation +import de.twomartens.timetable.types.NonEmptyString +import de.twomartens.timetable.types.ZeroOrPositiveInteger +import org.mapstruct.* + +@Mapper( + collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED, + nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS, + unmappedTargetPolicy = ReportingPolicy.IGNORE +) +interface FormationMapper { + + fun mapToDto(db: de.twomartens.timetable.model.db.Formation, + trainSimWorldFormation: de.twomartens.timetable.model.db.Formation?): Formation { + + val dtoTrainSimWorldFormation = if (trainSimWorldFormation != null) { + mapToDto(trainSimWorldFormation, null) + } else { + null + } + + return Formation( + db.formationId.id, + db.name.value, + dtoTrainSimWorldFormation, + db.formation, + db.length.value + ) + } + + @Mapping(target = "id", ignore = true) + @Mapping(target = "created", ignore = true) + @Mapping(target = "lastModified", ignore = true) + fun mapToDB(userId: UserId, dto: Formation): de.twomartens.timetable.model.db.Formation { + return de.twomartens.timetable.model.db.Formation( + userId, + FormationId.of(NonEmptyString(dto.id)), + NonEmptyString(dto.name), + if (dto.trainSimWorldFormation != null) + FormationId.of(NonEmptyString(dto.trainSimWorldFormation!!.id)) else null, + dto.formation, + ZeroOrPositiveInteger(dto.length) + ) + } +} \ No newline at end of file diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/formation/FormationRepository.kt b/module-server/src/main/kotlin/de/twomartens/timetable/formation/FormationRepository.kt new file mode 100644 index 0000000..4a97ae5 --- /dev/null +++ b/module-server/src/main/kotlin/de/twomartens/timetable/formation/FormationRepository.kt @@ -0,0 +1,13 @@ +package de.twomartens.timetable.formation + +import de.twomartens.timetable.model.common.FormationId +import de.twomartens.timetable.model.common.UserId +import de.twomartens.timetable.model.db.Formation +import org.bson.types.ObjectId +import org.springframework.data.mongodb.repository.MongoRepository + +interface FormationRepository : MongoRepository { + fun findByUserIdAndFormationId(userId: UserId, formationId: FormationId): Formation? + fun findAllByUserId(userId: UserId): List + fun findAllByUserIdAndNameContainingIgnoreCase(userId: UserId, name: String): List +} \ No newline at end of file diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/model/common/CoachCapacity.kt b/module-server/src/main/kotlin/de/twomartens/timetable/model/common/CoachCapacity.kt deleted file mode 100644 index f104c33..0000000 --- a/module-server/src/main/kotlin/de/twomartens/timetable/model/common/CoachCapacity.kt +++ /dev/null @@ -1,6 +0,0 @@ -package de.twomartens.timetable.model.common - -import de.twomartens.timetable.types.PositiveInteger - -@JvmInline -value class CoachCapacity(val capacity: PositiveInteger) diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/model/common/Depot.kt b/module-server/src/main/kotlin/de/twomartens/timetable/model/common/Depot.kt deleted file mode 100644 index 4a42250..0000000 --- a/module-server/src/main/kotlin/de/twomartens/timetable/model/common/Depot.kt +++ /dev/null @@ -1,11 +0,0 @@ -package de.twomartens.timetable.model.common - -import de.twomartens.timetable.types.NonEmptyString -import kotlin.time.Duration - -data class Depot( - var name: NonEmptyString, - var nearestStationId: StationId, - var tracks: Map, - var formationTimes: Map -) diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/model/common/FormationId.kt b/module-server/src/main/kotlin/de/twomartens/timetable/model/common/FormationId.kt deleted file mode 100644 index 722d073..0000000 --- a/module-server/src/main/kotlin/de/twomartens/timetable/model/common/FormationId.kt +++ /dev/null @@ -1,14 +0,0 @@ -package de.twomartens.timetable.model.common - -import de.twomartens.timetable.types.PositiveInteger - -@JvmInline -value class FormationId private constructor(val id: Int) { - companion object { - val EMPTY = FormationId(-1) - - fun of(id: PositiveInteger): FormationId { - return FormationId(id.value) - } - } -} \ No newline at end of file diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/model/common/Portal.kt b/module-server/src/main/kotlin/de/twomartens/timetable/model/common/Portal.kt deleted file mode 100644 index 5b97269..0000000 --- a/module-server/src/main/kotlin/de/twomartens/timetable/model/common/Portal.kt +++ /dev/null @@ -1,10 +0,0 @@ -package de.twomartens.timetable.model.common - -import de.twomartens.timetable.types.NonEmptyString -import kotlin.time.Duration - -data class Portal( - var name: NonEmptyString, - var nearestStationId: StationId, - var formationTimes: Map -) diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/model/db/Station.kt b/module-server/src/main/kotlin/de/twomartens/timetable/model/db/Station.kt deleted file mode 100644 index 2a4886c..0000000 --- a/module-server/src/main/kotlin/de/twomartens/timetable/model/db/Station.kt +++ /dev/null @@ -1,27 +0,0 @@ -package de.twomartens.timetable.model.db - -import de.twomartens.timetable.model.common.Platform -import de.twomartens.timetable.model.common.StationId -import org.bson.types.ObjectId -import org.springframework.data.annotation.CreatedDate -import org.springframework.data.annotation.Id -import org.springframework.data.annotation.LastModifiedDate -import org.springframework.data.mongodb.core.index.Indexed -import org.springframework.data.mongodb.core.mapping.Document -import java.time.Instant - -@Document -data class Station( - @Indexed(unique = true) var stationId: StationId, - var name: String, - var platforms: List -) { - @Id - var id: ObjectId = ObjectId() - - @CreatedDate - lateinit var created: Instant - - @LastModifiedDate - lateinit var lastModified: Instant -} diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/model/dto/Country.kt b/module-server/src/main/kotlin/de/twomartens/timetable/model/dto/Country.kt deleted file mode 100644 index 5bcb65b..0000000 --- a/module-server/src/main/kotlin/de/twomartens/timetable/model/dto/Country.kt +++ /dev/null @@ -1,9 +0,0 @@ -package de.twomartens.timetable.model.dto - -import de.twomartens.timetable.model.common.CountryCode -import de.twomartens.timetable.types.NonEmptyString - -data class Country( - val countryCode: CountryCode, - val name: NonEmptyString -) diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/model/dto/Formation.kt b/module-server/src/main/kotlin/de/twomartens/timetable/model/dto/Formation.kt deleted file mode 100644 index 982ef6d..0000000 --- a/module-server/src/main/kotlin/de/twomartens/timetable/model/dto/Formation.kt +++ /dev/null @@ -1,11 +0,0 @@ -package de.twomartens.timetable.model.dto - -import de.twomartens.timetable.model.common.FormationId -import de.twomartens.timetable.types.NonEmptyString - -data class Formation( - val id: FormationId, - val name: NonEmptyString, - val trainSimWorldFormationId: FormationId?, - val coaches: List -) diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/model/dto/Rotation.kt b/module-server/src/main/kotlin/de/twomartens/timetable/model/dto/Rotation.kt deleted file mode 100644 index 3aaad45..0000000 --- a/module-server/src/main/kotlin/de/twomartens/timetable/model/dto/Rotation.kt +++ /dev/null @@ -1,14 +0,0 @@ -package de.twomartens.timetable.model.dto - -import de.twomartens.timetable.model.common.FormationId -import de.twomartens.timetable.model.common.RotationId -import java.time.LocalTime - -data class Rotation( - val id: RotationId, - val formationId: FormationId, - val firstServiceStartTime: LocalTime, - val lastServiceEndTime: LocalTime, - val startsInVault: Boolean, - - ) diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/model/dto/ServiceFormationStage.kt b/module-server/src/main/kotlin/de/twomartens/timetable/model/dto/ServiceFormationStage.kt deleted file mode 100644 index d4f1da1..0000000 --- a/module-server/src/main/kotlin/de/twomartens/timetable/model/dto/ServiceFormationStage.kt +++ /dev/null @@ -1,15 +0,0 @@ -package de.twomartens.timetable.model.dto - -import de.twomartens.timetable.model.common.FormationId -import de.twomartens.timetable.model.common.Line -import de.twomartens.timetable.model.common.ServiceId - -data class ServiceFormationStage( - val id: ServiceId, - val line: Line, - val direction: Direction, - val formation: FormationId, - val formationReversed: Boolean, - val startStop: ServiceStop, - val destinationStop: ServiceStop -) diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/model/dto/ServiceRotationStage.kt b/module-server/src/main/kotlin/de/twomartens/timetable/model/dto/ServiceRotationStage.kt deleted file mode 100644 index 3b99f4a..0000000 --- a/module-server/src/main/kotlin/de/twomartens/timetable/model/dto/ServiceRotationStage.kt +++ /dev/null @@ -1,16 +0,0 @@ -package de.twomartens.timetable.model.dto - -import de.twomartens.timetable.model.common.Line -import de.twomartens.timetable.model.common.ServiceId -import de.twomartens.timetable.types.NonEmptyString - -data class ServiceRotationStage( - val id: ServiceId, - val line: Line, - val originStop: NonEmptyString, - val virtualDestinations: List, - val startingFrom: NonEmptyString, - val endingIn: NonEmptyString, - val serviceStartTime: NonEmptyString, - val stops: List -) diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/model/dto/Station.kt b/module-server/src/main/kotlin/de/twomartens/timetable/model/dto/Station.kt deleted file mode 100644 index 10199d4..0000000 --- a/module-server/src/main/kotlin/de/twomartens/timetable/model/dto/Station.kt +++ /dev/null @@ -1,9 +0,0 @@ -package de.twomartens.timetable.model.dto - -import de.twomartens.timetable.model.common.StationId -import de.twomartens.timetable.types.NonEmptyString - -data class Station( - val id: StationId, - val name: NonEmptyString -) diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/model/dto/Timetable.kt b/module-server/src/main/kotlin/de/twomartens/timetable/model/dto/Timetable.kt deleted file mode 100644 index 64576c8..0000000 --- a/module-server/src/main/kotlin/de/twomartens/timetable/model/dto/Timetable.kt +++ /dev/null @@ -1,13 +0,0 @@ -package de.twomartens.timetable.model.dto - -import de.twomartens.timetable.types.NonEmptyString -import java.time.LocalDate - -data class Timetable( - val name: NonEmptyString, - val country: Country, - val routeName: NonEmptyString, - val fetchDate: LocalDate, - val state: TimetableState, - val numberOfServices: String -) diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/model/dto/TswRoute.kt b/module-server/src/main/kotlin/de/twomartens/timetable/model/dto/TswRoute.kt deleted file mode 100644 index 6102420..0000000 --- a/module-server/src/main/kotlin/de/twomartens/timetable/model/dto/TswRoute.kt +++ /dev/null @@ -1,13 +0,0 @@ -package de.twomartens.timetable.model.dto - -import de.twomartens.timetable.model.common.Depot -import de.twomartens.timetable.model.common.Portal -import de.twomartens.timetable.types.NonEmptyString - -data class TswRoute( - val name: NonEmptyString, - val country: Country, - val stations: List, - val portals: List, - val depots: List -) diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/property/ServiceProperties.kt b/module-server/src/main/kotlin/de/twomartens/timetable/property/ServiceProperties.kt index ffbf718..a76c009 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/property/ServiceProperties.kt +++ b/module-server/src/main/kotlin/de/twomartens/timetable/property/ServiceProperties.kt @@ -11,6 +11,5 @@ import org.springframework.cloud.context.config.annotation.RefreshScope @ConfigurationProperties(prefix = "de.twomartens.timetable") @Schema(description = "Properties, to configure this Application") class ServiceProperties { - lateinit var greeting: String lateinit var countryNames: Map> } diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/route/RouteController.kt b/module-server/src/main/kotlin/de/twomartens/timetable/route/RouteController.kt new file mode 100644 index 0000000..34f6de5 --- /dev/null +++ b/module-server/src/main/kotlin/de/twomartens/timetable/route/RouteController.kt @@ -0,0 +1,220 @@ +package de.twomartens.timetable.route + +import de.twomartens.timetable.auth.UserRepository +import de.twomartens.timetable.model.common.RouteId +import de.twomartens.timetable.model.common.UserId +import de.twomartens.timetable.model.dto.TswRoute +import de.twomartens.timetable.types.NonEmptyString +import io.swagger.v3.oas.annotations.Operation +import io.swagger.v3.oas.annotations.Parameter +import io.swagger.v3.oas.annotations.media.ArraySchema +import io.swagger.v3.oas.annotations.media.Content +import io.swagger.v3.oas.annotations.media.Schema +import io.swagger.v3.oas.annotations.responses.ApiResponse +import io.swagger.v3.oas.annotations.security.SecurityRequirement +import io.swagger.v3.oas.annotations.tags.Tag +import org.mapstruct.factory.Mappers +import org.springframework.http.HttpStatus +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.* + +@RestController +@RequestMapping(value = ["/v1/routes"]) +@Tag(name = "Routes", description = "all requests relating to routes") +class RouteController( + private val routeRepository: TswRouteRepository, + private val userRepository: UserRepository +) { + private val mapper = Mappers.getMapper(RouteMapper::class.java) + + @Operation( + summary = "Access routes of user and filter by name", + responses = [ApiResponse( + responseCode = "200", + description = "List of found routes", + content = [Content( + array = ArraySchema(schema = Schema(implementation = TswRoute::class)) + )] + ), ApiResponse( + responseCode = "403", + description = "Access forbidden for user", + content = [Content(mediaType = "text/plain")] + )] + ) + @SecurityRequirement(name = "bearer") + @SecurityRequirement(name = "oauth2") + @GetMapping("/{userId}/") + fun getRoutes( + @PathVariable @Parameter(description = "The id of the user", + example = "1", + required = true) userId: String, + @RequestParam(name = "name", required = false) @Parameter(description = "Searched name", + example = "1", + required = false) name: String? + ): ResponseEntity> { + val userIdConverted = UserId.of(NonEmptyString(userId)) + val userExists = userRepository.existsByUserId(userIdConverted) + if (!userExists) { + return ResponseEntity.status(HttpStatus.FORBIDDEN).build() + } + + val routes = if (!name.isNullOrBlank()) { + routeRepository.findAllByUserIdAndNameContainingIgnoreCase(userIdConverted, name) + } else { + routeRepository.findAllByUserId(userIdConverted) + } + + return ResponseEntity.ok(mapper.mapRoutesToDto(routes)) + } + + @Operation( + summary = "Access route with id belonging to user", + responses = [ApiResponse( + responseCode = "200", + description = "Route exists and was returned", + content = [Content( + schema = Schema(implementation = TswRoute::class) + )] + ), ApiResponse( + responseCode = "404", + description = "Route was not found", + content = [Content(mediaType = "text/plain")] + ), ApiResponse( + responseCode = "403", + description = "Access forbidden for user", + content = [Content(mediaType = "text/plain")] + )] + ) + @SecurityRequirement(name = "bearer") + @SecurityRequirement(name = "oauth2") + @GetMapping("/{userId}/{id}") + fun getRoute( + @PathVariable @Parameter(description = "The id of the user", + example = "1", + required = true) userId: String, + @PathVariable @Parameter(description = "The id of the route", + example = "1", + required = true) id: String + ): ResponseEntity { + val userIdConverted = UserId.of(NonEmptyString(userId)) + val routeIdConverted = RouteId.of(NonEmptyString(id)) + val userExists = userRepository.existsByUserId(userIdConverted) + if (!userExists) { + return ResponseEntity.status(HttpStatus.FORBIDDEN).build() + } + + val route = routeRepository.findByUserIdAndRouteId(userIdConverted, routeIdConverted) + ?: return ResponseEntity.notFound().build() + return ResponseEntity.ok(mapper.mapToDto(route)) + } + + @Operation( + summary = "Store route with id belonging to user", + responses = [ApiResponse( + responseCode = "200", + description = "Route was updated", + content = [Content( + schema = Schema(implementation = TswRoute::class) + )] + ), ApiResponse( + responseCode = "201", + description = "Route was created", + content = [Content( + schema = Schema(implementation = TswRoute::class) + )] + ), ApiResponse( + responseCode = "403", + description = "Access forbidden for user", + content = [Content(mediaType = "text/plain")] + )] + ) + @SecurityRequirement(name = "bearer") + @SecurityRequirement(name = "oauth2") + @PutMapping("/{userId}/{id}") + fun putRoute( + @PathVariable @Parameter(description = "The id of the user", + example = "1", + required = true) userId: String, + @PathVariable @Parameter(description = "The id of the route", + example = "1", + required = true) id: String, + @RequestBody(required = true) @io.swagger.v3.oas.annotations.parameters.RequestBody( + required = true, + content = [ + Content( + schema = Schema(implementation = TswRoute::class) + ) + ]) body: TswRoute + ): ResponseEntity { + val userIdConverted = UserId.of(NonEmptyString(userId)) + val routeIdConverted = RouteId.of(NonEmptyString(id)) + val userExists = userRepository.existsByUserId(userIdConverted) + if (!userExists) { + return ResponseEntity.status(HttpStatus.FORBIDDEN).build() + } + + var route = routeRepository.findByUserIdAndRouteId(userIdConverted, routeIdConverted) + var created = false + + if (route == null) { + created = true + route = mapper.mapToDB(userIdConverted, body) + } else { + route.name = NonEmptyString(body.name) + route.country = body.country + route.stations = body.stations + route.depots = mapper.mapDepotsToDB(body.depots) + route.portals = mapper.mapPortalsToDB(body.portals) + } + + routeRepository.save(route) + + val updatedRoute = mapper.mapToDto(route) + return if (created) { + ResponseEntity.status(HttpStatus.CREATED).body(updatedRoute) + } else { + ResponseEntity.ok(updatedRoute) + } + } + + @Operation( + summary = "Delete route with id belonging to user", + responses = [ApiResponse( + responseCode = "204", + description = "Route was deleted", + content = [Content(mediaType = "text/plain")] + ), ApiResponse( + responseCode = "404", + description = "Route was not found", + content = [Content(mediaType = "text/plain")] + ), ApiResponse( + responseCode = "403", + description = "Access forbidden for user", + content = [Content(mediaType = "text/plain")] + )] + ) + @SecurityRequirement(name = "bearer") + @SecurityRequirement(name = "oauth2") + @DeleteMapping("/{userId}/{id}") + fun deleteRoute( + @PathVariable @Parameter(description = "The id of the user", + example = "1", + required = true) userId: String, + @PathVariable @Parameter(description = "The id of the route", + example = "1", + required = true) id: String, + ): ResponseEntity { + val userIdConverted = UserId.of(NonEmptyString(userId)) + val routeIdConverted = RouteId.of(NonEmptyString(id)) + val userExists = userRepository.existsByUserId(userIdConverted) + if (!userExists) { + return ResponseEntity.status(HttpStatus.FORBIDDEN).build() + } + + val route = routeRepository.findByUserIdAndRouteId(userIdConverted, routeIdConverted) + ?: return ResponseEntity.notFound().build() + routeRepository.delete(route) + + return ResponseEntity.status(HttpStatus.NO_CONTENT).build() + } +} \ No newline at end of file diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/route/RouteMapper.kt b/module-server/src/main/kotlin/de/twomartens/timetable/route/RouteMapper.kt new file mode 100644 index 0000000..1bb3f00 --- /dev/null +++ b/module-server/src/main/kotlin/de/twomartens/timetable/route/RouteMapper.kt @@ -0,0 +1,114 @@ +package de.twomartens.timetable.route + +import de.twomartens.timetable.model.common.DepotId +import de.twomartens.timetable.model.common.PortalId +import de.twomartens.timetable.model.common.RouteId +import de.twomartens.timetable.model.common.UserId +import de.twomartens.timetable.model.dto.Depot +import de.twomartens.timetable.model.dto.Portal +import de.twomartens.timetable.model.dto.TravelDuration +import de.twomartens.timetable.model.dto.TswRoute +import de.twomartens.timetable.types.NonEmptyString +import org.mapstruct.* + +@Mapper( + collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED, + nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS, + unmappedTargetPolicy = ReportingPolicy.IGNORE +) +interface RouteMapper { + @Mapping(target = "id", ignore = true) + @Mapping(target = "created", ignore = true) + @Mapping(target = "lastModified", ignore = true) + fun mapToDB(userId: UserId, dto: TswRoute): de.twomartens.timetable.model.db.TswRoute { + return de.twomartens.timetable.model.db.TswRoute( + userId, + RouteId.of(NonEmptyString(dto.id)), + NonEmptyString(dto.name), + dto.country, + dto.stations, + mapDepotsToDB(dto.depots), + mapPortalsToDB(dto.portals) + ) + } + + fun mapDepotsToDB(depots: List): List { + return depots.map { + de.twomartens.timetable.model.db.Depot( + DepotId.of(NonEmptyString(it.id)), + NonEmptyString(it.name), + it.nearestStation, + it.tracks, + mapTravelDurationsToDB(it.travelDurations) + ) + } + } + + fun mapPortalsToDB(portals: List): List { + return portals.map { + de.twomartens.timetable.model.db.Portal( + PortalId.of(NonEmptyString(it.id)), + NonEmptyString(it.name), + it.nearestStation, + mapTravelDurationsToDB(it.travelDurations) + ) + } + } + + fun mapTravelDurationsToDB(travelDurations: List): List { + return travelDurations.map { + de.twomartens.timetable.model.db.TravelDuration( + it.formation, + it.time + ) + } + } + + fun mapRoutesToDto(routes: List): List + + fun mapToDto(db: de.twomartens.timetable.model.db.TswRoute): TswRoute { + return TswRoute( + db.routeId.id, + db.name.value, + db.country, + db.stations, + db.stations.first(), + db.stations.last(), + db.stations.size, + mapDepotsToDto(db.depots), + mapPortalsToDto(db.portals) + ) + } + + fun mapDepotsToDto(depots: List): List { + return depots.map { depot -> + Depot( + depot.id.id, + depot.name.value, + depot.nearestStation, + depot.tracks, + mapTravelDurationsToDto(depot.travelDurations) + ) + } + } + + fun mapPortalsToDto(portals: List): List { + return portals.map { portal -> + Portal( + portal.id.id, + portal.name.value, + portal.nearestStation, + mapTravelDurationsToDto(portal.travelDurations) + ) + } + } + + fun mapTravelDurationsToDto(travelDurations: List): List { + return travelDurations.map { + TravelDuration( + it.formation, + it.time + ) + } + } +} \ No newline at end of file diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/repository/TswRouteRepository.kt b/module-server/src/main/kotlin/de/twomartens/timetable/route/TswRouteRepository.kt similarity index 55% rename from module-server/src/main/kotlin/de/twomartens/timetable/repository/TswRouteRepository.kt rename to module-server/src/main/kotlin/de/twomartens/timetable/route/TswRouteRepository.kt index 67d5420..77dc744 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/repository/TswRouteRepository.kt +++ b/module-server/src/main/kotlin/de/twomartens/timetable/route/TswRouteRepository.kt @@ -1,5 +1,6 @@ -package de.twomartens.timetable.repository +package de.twomartens.timetable.route +import de.twomartens.timetable.model.common.RouteId import de.twomartens.timetable.model.common.UserId import de.twomartens.timetable.model.db.TswRoute import de.twomartens.timetable.types.NonEmptyString @@ -7,5 +8,8 @@ import org.bson.types.ObjectId import org.springframework.data.mongodb.repository.MongoRepository interface TswRouteRepository : MongoRepository { + fun findByUserIdAndRouteId(userId: UserId, routeId: RouteId): TswRoute? fun findByUserIdAndName(userId: UserId, name: NonEmptyString): TswRoute? + fun findAllByUserId(userId: UserId): List + fun findAllByUserIdAndNameContainingIgnoreCase(userId: UserId, name: String): List } diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/station/StationController.kt b/module-server/src/main/kotlin/de/twomartens/timetable/station/StationController.kt new file mode 100644 index 0000000..6b07186 --- /dev/null +++ b/module-server/src/main/kotlin/de/twomartens/timetable/station/StationController.kt @@ -0,0 +1,97 @@ +package de.twomartens.timetable.station + +import de.twomartens.timetable.bahnApi.service.BahnApiService +import de.twomartens.timetable.bahnApi.service.BahnDatabaseService +import de.twomartens.timetable.model.dto.Station +import de.twomartens.timetable.model.mapper.StationMapper +import de.twomartens.timetable.model.repository.StationRepository +import io.swagger.v3.oas.annotations.Operation +import io.swagger.v3.oas.annotations.Parameter +import io.swagger.v3.oas.annotations.media.ArraySchema +import io.swagger.v3.oas.annotations.media.Content +import io.swagger.v3.oas.annotations.media.Schema +import io.swagger.v3.oas.annotations.responses.ApiResponse +import io.swagger.v3.oas.annotations.security.SecurityRequirement +import io.swagger.v3.oas.annotations.tags.Tag +import org.mapstruct.factory.Mappers +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.* + +@RestController +@RequestMapping(value = ["/v1/stations"]) +@Tag(name = "Stations", description = "all requests relating to stations") +class StationController( + private val stationRepository: StationRepository, + private val bahnApiService: BahnApiService, + private val bahnDatabaseService: BahnDatabaseService +) { + private val mapper = Mappers.getMapper(StationMapper::class.java) + + @Operation( + summary = "Access stations and filter by name", + responses = [ApiResponse( + responseCode = "200", + description = "List of found stations", + content = [Content( + array = ArraySchema(schema = Schema(implementation = Station::class)) + )] + ), ApiResponse( + responseCode = "403", + description = "Access forbidden for user", + content = [Content(mediaType = "text/plain")] + )] + ) + @SecurityRequirement(name = "bearer") + @SecurityRequirement(name = "oauth2") + @GetMapping("/") + fun getStations( + @RequestParam(name = "country", required = false) @Parameter(description = "Searched country", + example = "de", + required = false) countryCode: String?, + @RequestParam(name = "name", required = false) @Parameter(description = "Searched name", + example = "Aachen", + required = false) name: String? + ): ResponseEntity> { + val stations = findStations(countryCode, name) + + return ResponseEntity.ok(mapper.mapStationsToDto(stations)) + } + + private fun findStations( + countryCode: String?, + name: String? + ): List { + val stations = if (!name.isNullOrBlank()) { + if (!countryCode.isNullOrBlank()) { + stationRepository.findAllByCountryCodeAndNameContainingIgnoreCase(countryCode, name) + } else { + stationRepository.findAllByNameContainingIgnoreCase(name) + } + } else { + stationRepository.findAll() + } + return stations + } + + @Operation( + summary = "Update stations", + responses = [ApiResponse( + responseCode = "200", + description = "Stations updated", + content = [Content(mediaType = "text/plain")] + ), ApiResponse( + responseCode = "403", + description = "Access forbidden for user", + content = [Content(mediaType = "text/plain")] + )] + ) + @SecurityRequirement(name = "bearer") + @SecurityRequirement(name = "oauth2") + @PostMapping("/update") + fun updateStations(): ResponseEntity { + val stations = bahnApiService.fetchStations("*") + bahnDatabaseService.storeStations(stations) + + return ResponseEntity.ok().build() + } +} \ No newline at end of file diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/support/model/dto/ErrorMessage.kt b/module-server/src/main/kotlin/de/twomartens/timetable/support/model/dto/ErrorMessage.kt deleted file mode 100644 index 7019caf..0000000 --- a/module-server/src/main/kotlin/de/twomartens/timetable/support/model/dto/ErrorMessage.kt +++ /dev/null @@ -1,3 +0,0 @@ -package de.twomartens.timetable.support.model.dto - -data class ErrorMessage(val message: String) diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/support/monitoring/statusprobe/StatusProbeCriticality.kt b/module-server/src/main/kotlin/de/twomartens/timetable/support/monitoring/statusprobe/StatusProbeCriticality.kt deleted file mode 100644 index 70df86b..0000000 --- a/module-server/src/main/kotlin/de/twomartens/timetable/support/monitoring/statusprobe/StatusProbeCriticality.kt +++ /dev/null @@ -1,7 +0,0 @@ -package de.twomartens.timetable.support.monitoring.statusprobe - -enum class StatusProbeCriticality { - K1, - K2, - K3 -} diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/timetable/TimetableController.kt b/module-server/src/main/kotlin/de/twomartens/timetable/timetable/TimetableController.kt new file mode 100644 index 0000000..b07f99a --- /dev/null +++ b/module-server/src/main/kotlin/de/twomartens/timetable/timetable/TimetableController.kt @@ -0,0 +1,259 @@ +package de.twomartens.timetable.timetable + +import de.twomartens.timetable.auth.UserRepository +import de.twomartens.timetable.bahnApi.service.ScheduledTaskService +import de.twomartens.timetable.model.common.TimetableId +import de.twomartens.timetable.model.common.UserId +import de.twomartens.timetable.model.dto.Timetable +import de.twomartens.timetable.model.dto.TimetableState +import de.twomartens.timetable.route.TswRouteRepository +import de.twomartens.timetable.types.NonEmptyString +import io.swagger.v3.oas.annotations.Operation +import io.swagger.v3.oas.annotations.Parameter +import io.swagger.v3.oas.annotations.media.ArraySchema +import io.swagger.v3.oas.annotations.media.Content +import io.swagger.v3.oas.annotations.media.Schema +import io.swagger.v3.oas.annotations.responses.ApiResponse +import io.swagger.v3.oas.annotations.security.SecurityRequirement +import io.swagger.v3.oas.annotations.tags.Tag +import org.mapstruct.factory.Mappers +import org.springframework.http.HttpStatus +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.* +import org.springframework.web.server.ResponseStatusException +import java.time.Clock +import java.time.LocalDate + +@RestController +@RequestMapping(value = ["/v1/timetables"]) +@Tag(name = "Timetables", description = "all requests relating to timetables") +class TimetableController( + private val clock: Clock, + private val routeRepository: TswRouteRepository, + private val timetableRepository: TimetableRepository, + private val userRepository: UserRepository, + private val scheduledTaskService: ScheduledTaskService +) { + + private val mapper = Mappers.getMapper(TimetableMapper::class.java) + + @Operation( + summary = "Access timetables of user and filter by name", + responses = [ApiResponse( + responseCode = "200", + description = "List of found timetables", + content = [Content( + array = ArraySchema(schema = Schema(implementation = Timetable::class)) + )] + ), ApiResponse( + responseCode = "403", + description = "Access forbidden for user", + content = [Content(mediaType = "text/plain")] + )] + ) + @SecurityRequirement(name = "bearer") + @SecurityRequirement(name = "oauth2") + @GetMapping("/{userId}/") + fun getTimetables( + @PathVariable @Parameter(description = "The id of the user", + example = "1", + required = true) userId: String, + @RequestParam(name = "name", required = false) @Parameter(description = "Searched name", + example = "KAH", + required = false) name: String? + ): ResponseEntity> { + val userIdConverted = UserId.of(NonEmptyString(userId)) + val userExists = userRepository.existsByUserId(userIdConverted) + if (!userExists) { + return ResponseEntity.status(HttpStatus.FORBIDDEN).build() + } + + val timetables = if (!name.isNullOrBlank()) { + timetableRepository.findAllByUserIdAndNameContainingIgnoreCase(userIdConverted, name) + } else { + timetableRepository.findAllByUserId(userIdConverted) + } + + return ResponseEntity.ok(mapper.mapTimetablesToDto(timetables)) + } + + @Operation( + summary = "Access timetable with id belonging to user", + responses = [ApiResponse( + responseCode = "200", + description = "Timetable exists and was returned", + content = [Content( + schema = Schema(implementation = Timetable::class) + )] + ), ApiResponse( + responseCode = "404", + description = "Timetable was not found", + content = [Content(mediaType = "text/plain")] + ), ApiResponse( + responseCode = "403", + description = "Access forbidden for user", + content = [Content(mediaType = "text/plain")] + )] + ) + @SecurityRequirement(name = "bearer") + @SecurityRequirement(name = "oauth2") + @GetMapping("/{userId}/{id}") + fun getTimetable( + @PathVariable @Parameter(description = "The id of the user", + example = "1", + required = true) userId: UserId, + @PathVariable @Parameter(description = "The id of the timetable", + example = "1", + required = true) id: TimetableId + ): ResponseEntity { + val userExists = userRepository.existsByUserId(userId) + if (!userExists) { + return ResponseEntity.status(HttpStatus.FORBIDDEN).build() + } + + val timetable = timetableRepository.findByUserIdAndTimetableId(userId, id) + ?: return ResponseEntity.notFound().build() + return ResponseEntity.ok(mapper.mapToDto(timetable)) + } + + @Operation( + summary = "Store timetable with id belonging to user", + responses = [ApiResponse( + responseCode = "200", + description = "Timetable was updated", + content = [Content( + schema = Schema(implementation = Timetable::class) + )] + ), ApiResponse( + responseCode = "201", + description = "Timetable was created", + content = [Content( + schema = Schema(implementation = Timetable::class) + )] + ), ApiResponse( + responseCode = "403", + description = "Access forbidden for user", + content = [Content(mediaType = "text/plain")] + )] + ) + @SecurityRequirement(name = "bearer") + @SecurityRequirement(name = "oauth2") + @PutMapping("/{userId}/{id}") + fun putTimetable( + @PathVariable @Parameter(description = "The id of the user", + example = "1", + required = true) userId: String, + @PathVariable @Parameter(description = "The id of the timetable", + example = "1", + required = true) id: String, + @RequestBody(required = true) @io.swagger.v3.oas.annotations.parameters.RequestBody( + required = true, + content = [ + Content( + schema = Schema(implementation = Timetable::class) + ) + ]) body: Timetable + ): ResponseEntity { + val userIdConverted = UserId.of(NonEmptyString(userId)) + val timetableIdConverted = TimetableId.of(NonEmptyString(id)) + val userExists = userRepository.existsByUserId(userIdConverted) + if (!userExists) { + return ResponseEntity.status(HttpStatus.FORBIDDEN).build() + } + + var timetable = timetableRepository.findByUserIdAndTimetableId(userIdConverted, timetableIdConverted) + var created = false + + if (timetable == null) { + created = true + timetable = mapper.mapToDB(userIdConverted, body) + timetable.timetableState = TimetableState.PROCESSING + } else { + timetable.name = NonEmptyString(body.name) + } + + timetableRepository.save(timetable) + + val updatedTimetable = mapper.mapToDto(timetable) + return if (created) { + ResponseEntity.status(HttpStatus.CREATED).body(updatedTimetable) + } else { + ResponseEntity.ok(updatedTimetable) + } + } + + @Operation( + summary = "Delete timetable with id belonging to user", + responses = [ApiResponse( + responseCode = "204", + description = "Timetable was deleted", + content = [Content(mediaType = "text/plain")] + ), ApiResponse( + responseCode = "404", + description = "Timetable was not found", + content = [Content(mediaType = "text/plain")] + ), ApiResponse( + responseCode = "403", + description = "Access forbidden for user", + content = [Content(mediaType = "text/plain")] + )] + ) + @SecurityRequirement(name = "bearer") + @SecurityRequirement(name = "oauth2") + @DeleteMapping("/{userId}/{id}") + fun deleteTimetable( + @PathVariable @Parameter(description = "The id of the user", + example = "1", + required = true) userId: String, + @PathVariable @Parameter(description = "The id of the timetable", + example = "1", + required = true) id: String, + ): ResponseEntity { + val userIdConverted = UserId.of(NonEmptyString(userId)) + val timetableIdConverted = TimetableId.of(NonEmptyString(id)) + val userExists = userRepository.existsByUserId(userIdConverted) + if (!userExists) { + return ResponseEntity.status(HttpStatus.FORBIDDEN).build() + } + + val timetable = timetableRepository.findByUserIdAndTimetableId(userIdConverted, timetableIdConverted) + ?: return ResponseEntity.notFound().build() + timetableRepository.delete(timetable) + + return ResponseEntity.status(HttpStatus.NO_CONTENT).build() + } + + @Operation( + summary = "Task the backend with fetching a timetable for specified route and date", + responses = [ApiResponse( + responseCode = "200", + description = "Timetable will be fetched as requested" + ), ApiResponse( + responseCode = "400", + description = "Request does not follow specification" + )] + ) + @SecurityRequirement(name = "bearer") + @SecurityRequirement(name = "oauth2") + @PostMapping("/{userId}/{routeName}/fetchTimetable/{date}") + fun scheduleFetch( + @PathVariable @Parameter(description = "The id of the user", + example = "1", + required = true) userId: UserId, + @PathVariable @Parameter(description = "The name of the TSW route.", + example = "CologneAachen", + required = true) routeName: NonEmptyString, + @PathVariable @Parameter( + description = "The date to fetch timetable for. Must be in the future.", + example = "2023-09-12", + required = true) date: LocalDate + ) { + if (!date.isAfter(LocalDate.now(clock))) { + throw ResponseStatusException(HttpStatus.BAD_REQUEST, "Date must be in the future") + } + val route = routeRepository.findByUserIdAndName(userId, routeName) + ?: throw ResponseStatusException(HttpStatus.BAD_REQUEST, + "Route name must belong to existing route") + scheduledTaskService.triggerTimetableFetch(route, date) + } +} \ No newline at end of file diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/timetable/TimetableMapper.kt b/module-server/src/main/kotlin/de/twomartens/timetable/timetable/TimetableMapper.kt new file mode 100644 index 0000000..1ae7111 --- /dev/null +++ b/module-server/src/main/kotlin/de/twomartens/timetable/timetable/TimetableMapper.kt @@ -0,0 +1,46 @@ +package de.twomartens.timetable.timetable + +import de.twomartens.timetable.model.common.RouteId +import de.twomartens.timetable.model.common.TimetableId +import de.twomartens.timetable.model.common.UserId +import de.twomartens.timetable.model.dto.Timetable +import de.twomartens.timetable.types.NonEmptyString +import de.twomartens.timetable.types.ZeroOrPositiveInteger +import org.mapstruct.* + +@Mapper( + collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED, + nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS, + unmappedTargetPolicy = ReportingPolicy.IGNORE +) +interface TimetableMapper { + @Mapping(target = "id", ignore = true) + @Mapping(target = "created", ignore = true) + @Mapping(target = "lastModified", ignore = true) + fun mapToDB(userId: UserId, dto: Timetable): de.twomartens.timetable.model.db.Timetable { + return de.twomartens.timetable.model.db.Timetable( + userId, + RouteId.of(NonEmptyString(dto.routeId)), + dto.routeName, + TimetableId.of(NonEmptyString(dto.id)), + NonEmptyString(dto.name), + dto.date, + dto.state, + ZeroOrPositiveInteger(dto.numberOfServices) + ) + } + + fun mapTimetablesToDto(routes: List): List + + fun mapToDto(db: de.twomartens.timetable.model.db.Timetable): Timetable { + return Timetable( + db.timetableId.value, + db.name.value, + db.routeId.id, + db.routeName, + db.fetchDate, + db.timetableState, + db.numberOfServices.value + ) + } +} \ No newline at end of file diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/timetable/TimetableRepository.kt b/module-server/src/main/kotlin/de/twomartens/timetable/timetable/TimetableRepository.kt new file mode 100644 index 0000000..085d54c --- /dev/null +++ b/module-server/src/main/kotlin/de/twomartens/timetable/timetable/TimetableRepository.kt @@ -0,0 +1,13 @@ +package de.twomartens.timetable.timetable + +import de.twomartens.timetable.model.common.TimetableId +import de.twomartens.timetable.model.common.UserId +import de.twomartens.timetable.model.db.Timetable +import org.bson.types.ObjectId +import org.springframework.data.mongodb.repository.MongoRepository + +interface TimetableRepository : MongoRepository { + fun findByUserIdAndTimetableId(userId: UserId, timetableId: TimetableId): Timetable? + fun findAllByUserIdAndNameContainingIgnoreCase(userId: UserId, name: String): List + fun findAllByUserId(userId: UserId): List +} \ No newline at end of file diff --git a/module-server/src/main/resources/application.yaml b/module-server/src/main/resources/application.yaml index 3287a6b..37b5aca 100644 --- a/module-server/src/main/resources/application.yaml +++ b/module-server/src/main/resources/application.yaml @@ -2,6 +2,8 @@ server: port: 12100 shutdown: graceful forward-headers-strategy: framework + http2: + enabled: true ### technical configurations ### management: @@ -50,10 +52,10 @@ springdoc: display-request-duration: true operationsSorter: method disable-swagger-default-url: true - path: '/timetable/v1/doc/' + path: '/doc/v1/timetable/' default-produces-media-type: 'application/json' api-docs: - path: '/timetable/v1/api-docs' + path: '/api-docs/v1/timetable' openapi: description: | @@ -82,6 +84,7 @@ de.twomartens.timetable: bahn-api: client-id: ${DB_CLIENT_ID} client-secret: ${DB_CLIENT_SECRET} +de.twomartens.support.health.greeting: "Good morning" time: defaultTimeZone: Europe/Berlin diff --git a/module-server/src/main/resources/log4j2.xml b/module-server/src/main/resources/log4j2.xml index f9b9aa9..f9d062c 100644 --- a/module-server/src/main/resources/log4j2.xml +++ b/module-server/src/main/resources/log4j2.xml @@ -17,5 +17,6 @@ + \ No newline at end of file diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/support/configuration/ClockConfiguration.kt b/module-support/src/main/kotlin/de/twomartens/support/configuration/ClockConfiguration.kt similarity index 74% rename from module-server/src/main/kotlin/de/twomartens/timetable/support/configuration/ClockConfiguration.kt rename to module-support/src/main/kotlin/de/twomartens/support/configuration/ClockConfiguration.kt index 93dc24d..d8eabf7 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/support/configuration/ClockConfiguration.kt +++ b/module-support/src/main/kotlin/de/twomartens/support/configuration/ClockConfiguration.kt @@ -1,6 +1,6 @@ -package de.twomartens.timetable.support.configuration +package de.twomartens.support.configuration -import de.twomartens.timetable.property.TimeProperties +import de.twomartens.support.property.TimeProperties import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import java.time.Clock diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/support/configuration/FilterConfiguration.kt b/module-support/src/main/kotlin/de/twomartens/support/configuration/FilterConfiguration.kt similarity index 85% rename from module-server/src/main/kotlin/de/twomartens/timetable/support/configuration/FilterConfiguration.kt rename to module-support/src/main/kotlin/de/twomartens/support/configuration/FilterConfiguration.kt index abc2b29..617210b 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/support/configuration/FilterConfiguration.kt +++ b/module-support/src/main/kotlin/de/twomartens/support/configuration/FilterConfiguration.kt @@ -1,4 +1,4 @@ -package de.twomartens.timetable.support.configuration +package de.twomartens.support.configuration import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/support/configuration/InterceptorConfiguration.kt b/module-support/src/main/kotlin/de/twomartens/support/configuration/InterceptorConfiguration.kt similarity index 68% rename from module-server/src/main/kotlin/de/twomartens/timetable/support/configuration/InterceptorConfiguration.kt rename to module-support/src/main/kotlin/de/twomartens/support/configuration/InterceptorConfiguration.kt index 0fe9d29..84b7274 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/support/configuration/InterceptorConfiguration.kt +++ b/module-support/src/main/kotlin/de/twomartens/support/configuration/InterceptorConfiguration.kt @@ -1,7 +1,7 @@ -package de.twomartens.timetable.support.configuration +package de.twomartens.support.configuration -import de.twomartens.timetable.support.interceptor.HeaderInterceptorRest -import de.twomartens.timetable.support.interceptor.LoggingInterceptorRest +import de.twomartens.support.interceptor.HeaderInterceptorRest +import de.twomartens.support.interceptor.LoggingInterceptorRest import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import java.time.Clock diff --git a/module-support/src/main/kotlin/de/twomartens/support/configuration/RestClientConfiguration.kt b/module-support/src/main/kotlin/de/twomartens/support/configuration/RestClientConfiguration.kt new file mode 100644 index 0000000..a5e2d43 --- /dev/null +++ b/module-support/src/main/kotlin/de/twomartens/support/configuration/RestClientConfiguration.kt @@ -0,0 +1,24 @@ +package de.twomartens.support.configuration + +import de.twomartens.support.interceptor.HeaderInterceptorRest +import de.twomartens.support.interceptor.LoggingInterceptorRest +import de.twomartens.support.property.RestTemplateTimeoutProperties +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter +import org.springframework.web.client.RestClient + +@Configuration +open class RestClientConfiguration { + @Bean("restClient") + open fun restClient( + headerInterceptorRest: HeaderInterceptorRest, + loggingInterceptor: LoggingInterceptorRest, + restTemplateTimeoutProperties: RestTemplateTimeoutProperties + ): RestClient { + return RestClient.builder() + .messageConverters { it.add(Jaxb2RootElementHttpMessageConverter()) } + .requestInterceptors { listOf(headerInterceptorRest, loggingInterceptor) } + .build() + } +} \ No newline at end of file diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/support/configuration/RestTemplateConfiguration.kt b/module-support/src/main/kotlin/de/twomartens/support/configuration/RestTemplateConfiguration.kt similarity index 84% rename from module-server/src/main/kotlin/de/twomartens/timetable/support/configuration/RestTemplateConfiguration.kt rename to module-support/src/main/kotlin/de/twomartens/support/configuration/RestTemplateConfiguration.kt index 623c0d1..185ca73 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/support/configuration/RestTemplateConfiguration.kt +++ b/module-support/src/main/kotlin/de/twomartens/support/configuration/RestTemplateConfiguration.kt @@ -1,8 +1,8 @@ -package de.twomartens.timetable.support.configuration +package de.twomartens.support.configuration -import de.twomartens.timetable.property.RestTemplateTimeoutProperties -import de.twomartens.timetable.support.interceptor.HeaderInterceptorRest -import de.twomartens.timetable.support.interceptor.LoggingInterceptorRest +import de.twomartens.support.interceptor.HeaderInterceptorRest +import de.twomartens.support.interceptor.LoggingInterceptorRest +import de.twomartens.support.property.RestTemplateTimeoutProperties import org.springframework.boot.web.client.RestTemplateBuilder import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/support/configuration/StatusProbeConfiguration.kt b/module-support/src/main/kotlin/de/twomartens/support/configuration/StatusProbeConfiguration.kt similarity index 61% rename from module-server/src/main/kotlin/de/twomartens/timetable/support/configuration/StatusProbeConfiguration.kt rename to module-support/src/main/kotlin/de/twomartens/support/configuration/StatusProbeConfiguration.kt index 9a63847..3a070ee 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/support/configuration/StatusProbeConfiguration.kt +++ b/module-support/src/main/kotlin/de/twomartens/support/configuration/StatusProbeConfiguration.kt @@ -1,9 +1,9 @@ -package de.twomartens.timetable.support.configuration +package de.twomartens.support.configuration -import de.twomartens.timetable.support.monitoring.statusprobe.CountBasedStatusProbe -import de.twomartens.timetable.support.monitoring.statusprobe.StatusProbe -import de.twomartens.timetable.support.monitoring.statusprobe.StatusProbeCriticality -import de.twomartens.timetable.support.monitoring.statusprobe.StatusProbeLogger +import de.twomartens.support.monitoring.statusprobe.CountBasedStatusProbe +import de.twomartens.support.monitoring.statusprobe.StatusProbe +import de.twomartens.support.monitoring.statusprobe.StatusProbeCriticality +import de.twomartens.support.monitoring.statusprobe.StatusProbeLogger import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import java.time.Clock diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/support/controller/ExceptionController.kt b/module-support/src/main/kotlin/de/twomartens/support/controller/ExceptionController.kt similarity index 87% rename from module-server/src/main/kotlin/de/twomartens/timetable/support/controller/ExceptionController.kt rename to module-support/src/main/kotlin/de/twomartens/support/controller/ExceptionController.kt index ff9f1cc..33cd0ff 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/support/controller/ExceptionController.kt +++ b/module-support/src/main/kotlin/de/twomartens/support/controller/ExceptionController.kt @@ -1,7 +1,7 @@ -package de.twomartens.timetable.support.controller +package de.twomartens.support.controller -import de.twomartens.timetable.support.exception.HttpStatusException -import de.twomartens.timetable.support.model.dto.ErrorMessage +import de.twomartens.support.exception.HttpStatusException +import de.twomartens.support.model.dto.ErrorMessage import mu.KotlinLogging import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/support/controller/v1/HealthCheckController.kt b/module-support/src/main/kotlin/de/twomartens/support/controller/HealthCheckController.kt similarity index 63% rename from module-server/src/main/kotlin/de/twomartens/timetable/support/controller/v1/HealthCheckController.kt rename to module-support/src/main/kotlin/de/twomartens/support/controller/HealthCheckController.kt index bea59aa..5c59a64 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/support/controller/v1/HealthCheckController.kt +++ b/module-support/src/main/kotlin/de/twomartens/support/controller/HealthCheckController.kt @@ -1,14 +1,14 @@ -package de.twomartens.timetable.support.controller.v1 +package de.twomartens.support.controller -import de.twomartens.timetable.property.ServiceProperties +import de.twomartens.support.property.HealthCheckProperties import io.swagger.v3.oas.annotations.Hidden import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RestController @RestController -@RequestMapping(value = ["/timetable/v1"]) -class HealthCheckController(private val properties: ServiceProperties) { +@RequestMapping(value = ["/timetable"]) +class HealthCheckController(private val properties: HealthCheckProperties) { @Hidden diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/support/controller/VersionHtmlController.kt b/module-support/src/main/kotlin/de/twomartens/support/controller/VersionHtmlController.kt similarity index 98% rename from module-server/src/main/kotlin/de/twomartens/timetable/support/controller/VersionHtmlController.kt rename to module-support/src/main/kotlin/de/twomartens/support/controller/VersionHtmlController.kt index cfb482f..1be2b4c 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/support/controller/VersionHtmlController.kt +++ b/module-support/src/main/kotlin/de/twomartens/support/controller/VersionHtmlController.kt @@ -1,4 +1,4 @@ -package de.twomartens.timetable.support.controller +package de.twomartens.support.controller import mu.KotlinLogging import org.springframework.stereotype.Controller diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/support/exception/HttpStatusException.kt b/module-support/src/main/kotlin/de/twomartens/support/exception/HttpStatusException.kt similarity index 59% rename from module-server/src/main/kotlin/de/twomartens/timetable/support/exception/HttpStatusException.kt rename to module-support/src/main/kotlin/de/twomartens/support/exception/HttpStatusException.kt index 2a2ac4c..fd3fa1d 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/support/exception/HttpStatusException.kt +++ b/module-support/src/main/kotlin/de/twomartens/support/exception/HttpStatusException.kt @@ -1,4 +1,4 @@ -package de.twomartens.timetable.support.exception +package de.twomartens.support.exception import org.springframework.http.HttpStatus diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/support/interceptor/BufferingClientHttpResponseWrapper.kt b/module-support/src/main/kotlin/de/twomartens/support/interceptor/BufferingClientHttpResponseWrapper.kt similarity index 79% rename from module-server/src/main/kotlin/de/twomartens/timetable/support/interceptor/BufferingClientHttpResponseWrapper.kt rename to module-support/src/main/kotlin/de/twomartens/support/interceptor/BufferingClientHttpResponseWrapper.kt index 5b81ea8..290c515 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/support/interceptor/BufferingClientHttpResponseWrapper.kt +++ b/module-support/src/main/kotlin/de/twomartens/support/interceptor/BufferingClientHttpResponseWrapper.kt @@ -1,4 +1,4 @@ -package de.twomartens.timetable.support.interceptor +package de.twomartens.support.interceptor import org.springframework.http.HttpHeaders import org.springframework.http.HttpStatusCode @@ -16,12 +16,6 @@ class BufferingClientHttpResponseWrapper(private val response: ClientHttpRespons return response.statusCode } - @Deprecated("Replace calls of getRawStatusCode with getStatusCode", ReplaceWith("getStatusCode()")) - @Throws(IOException::class) - override fun getRawStatusCode(): Int { - return statusCode.value() - } - @Throws(IOException::class) override fun getStatusText(): String { return response.statusText diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/support/interceptor/HeaderInterceptor.kt b/module-support/src/main/kotlin/de/twomartens/support/interceptor/HeaderInterceptor.kt similarity index 98% rename from module-server/src/main/kotlin/de/twomartens/timetable/support/interceptor/HeaderInterceptor.kt rename to module-support/src/main/kotlin/de/twomartens/support/interceptor/HeaderInterceptor.kt index 47c6e48..acb582e 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/support/interceptor/HeaderInterceptor.kt +++ b/module-support/src/main/kotlin/de/twomartens/support/interceptor/HeaderInterceptor.kt @@ -1,4 +1,4 @@ -package de.twomartens.timetable.support.interceptor +package de.twomartens.support.interceptor import org.slf4j.MDC import java.io.Closeable diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/support/interceptor/HeaderInterceptorRest.kt b/module-support/src/main/kotlin/de/twomartens/support/interceptor/HeaderInterceptorRest.kt similarity index 98% rename from module-server/src/main/kotlin/de/twomartens/timetable/support/interceptor/HeaderInterceptorRest.kt rename to module-support/src/main/kotlin/de/twomartens/support/interceptor/HeaderInterceptorRest.kt index f2599c9..b3d2167 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/support/interceptor/HeaderInterceptorRest.kt +++ b/module-support/src/main/kotlin/de/twomartens/support/interceptor/HeaderInterceptorRest.kt @@ -1,4 +1,4 @@ -package de.twomartens.timetable.support.interceptor +package de.twomartens.support.interceptor import jakarta.servlet.http.HttpServletRequest import jakarta.servlet.http.HttpServletResponse diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/support/interceptor/LoggingInterceptorRest.kt b/module-support/src/main/kotlin/de/twomartens/support/interceptor/LoggingInterceptorRest.kt similarity index 99% rename from module-server/src/main/kotlin/de/twomartens/timetable/support/interceptor/LoggingInterceptorRest.kt rename to module-support/src/main/kotlin/de/twomartens/support/interceptor/LoggingInterceptorRest.kt index 8674864..51cf6a3 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/support/interceptor/LoggingInterceptorRest.kt +++ b/module-support/src/main/kotlin/de/twomartens/support/interceptor/LoggingInterceptorRest.kt @@ -1,4 +1,4 @@ -package de.twomartens.timetable.support.interceptor +package de.twomartens.support.interceptor import jakarta.servlet.* import jakarta.servlet.http.HttpServletRequest @@ -15,6 +15,7 @@ import org.springframework.util.StreamUtils import org.springframework.web.util.ContentCachingRequestWrapper import org.springframework.web.util.ContentCachingResponseWrapper import java.io.IOException +import java.net.URI import java.net.URL import java.nio.charset.Charset import java.nio.charset.StandardCharsets @@ -81,7 +82,7 @@ class LoggingInterceptorRest( ) } val query = if (httpRequest.queryString != null) "?${httpRequest.queryString}" else "" - val requestUrl = URL("${httpRequest.requestURL}$query") + val requestUrl = URI.create("${httpRequest.requestURL}$query").toURL() val requestHeaders = extractHeaders( headerNames = httpRequest.headerNames.asIterator(), ignoreList = listOf("authorization")) { diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/support/model/LeadershipStatus.kt b/module-support/src/main/kotlin/de/twomartens/support/model/LeadershipStatus.kt similarity index 71% rename from module-server/src/main/kotlin/de/twomartens/timetable/support/model/LeadershipStatus.kt rename to module-support/src/main/kotlin/de/twomartens/support/model/LeadershipStatus.kt index c85fe3a..9619316 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/support/model/LeadershipStatus.kt +++ b/module-support/src/main/kotlin/de/twomartens/support/model/LeadershipStatus.kt @@ -1,4 +1,4 @@ -package de.twomartens.timetable.support.model +package de.twomartens.support.model import org.springframework.stereotype.Component diff --git a/module-support/src/main/kotlin/de/twomartens/support/model/dto/ErrorMessage.kt b/module-support/src/main/kotlin/de/twomartens/support/model/dto/ErrorMessage.kt new file mode 100644 index 0000000..e9d94c4 --- /dev/null +++ b/module-support/src/main/kotlin/de/twomartens/support/model/dto/ErrorMessage.kt @@ -0,0 +1,3 @@ +package de.twomartens.support.model.dto + +data class ErrorMessage(val message: String) diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/support/monitoring/actuator/AbstractHealthIndicator.kt b/module-support/src/main/kotlin/de/twomartens/support/monitoring/actuator/AbstractHealthIndicator.kt similarity index 97% rename from module-server/src/main/kotlin/de/twomartens/timetable/support/monitoring/actuator/AbstractHealthIndicator.kt rename to module-support/src/main/kotlin/de/twomartens/support/monitoring/actuator/AbstractHealthIndicator.kt index e203d08..2e749bb 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/support/monitoring/actuator/AbstractHealthIndicator.kt +++ b/module-support/src/main/kotlin/de/twomartens/support/monitoring/actuator/AbstractHealthIndicator.kt @@ -1,4 +1,4 @@ -package de.twomartens.timetable.support.monitoring.actuator +package de.twomartens.support.monitoring.actuator import mu.KotlinLogging import org.slf4j.MDC diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/support/monitoring/actuator/AbstractStatusProbeHealthIndicator.kt b/module-support/src/main/kotlin/de/twomartens/support/monitoring/actuator/AbstractStatusProbeHealthIndicator.kt similarity index 88% rename from module-server/src/main/kotlin/de/twomartens/timetable/support/monitoring/actuator/AbstractStatusProbeHealthIndicator.kt rename to module-support/src/main/kotlin/de/twomartens/support/monitoring/actuator/AbstractStatusProbeHealthIndicator.kt index 7198ae4..7c25b51 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/support/monitoring/actuator/AbstractStatusProbeHealthIndicator.kt +++ b/module-support/src/main/kotlin/de/twomartens/support/monitoring/actuator/AbstractStatusProbeHealthIndicator.kt @@ -1,6 +1,6 @@ -package de.twomartens.timetable.support.monitoring.actuator +package de.twomartens.support.monitoring.actuator -import de.twomartens.timetable.support.monitoring.statusprobe.StatusProbe +import de.twomartens.support.monitoring.statusprobe.StatusProbe import org.springframework.boot.actuate.health.Health import org.springframework.boot.actuate.health.HealthIndicator import java.time.Clock diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/support/monitoring/actuator/Preparable.kt b/module-support/src/main/kotlin/de/twomartens/support/monitoring/actuator/Preparable.kt similarity index 58% rename from module-server/src/main/kotlin/de/twomartens/timetable/support/monitoring/actuator/Preparable.kt rename to module-support/src/main/kotlin/de/twomartens/support/monitoring/actuator/Preparable.kt index 3ca227e..07b55f4 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/support/monitoring/actuator/Preparable.kt +++ b/module-support/src/main/kotlin/de/twomartens/support/monitoring/actuator/Preparable.kt @@ -1,4 +1,4 @@ -package de.twomartens.timetable.support.monitoring.actuator +package de.twomartens.support.monitoring.actuator import java.io.Closeable diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/support/monitoring/actuator/RestHealthIndicator.kt b/module-support/src/main/kotlin/de/twomartens/support/monitoring/actuator/RestHealthIndicator.kt similarity index 86% rename from module-server/src/main/kotlin/de/twomartens/timetable/support/monitoring/actuator/RestHealthIndicator.kt rename to module-support/src/main/kotlin/de/twomartens/support/monitoring/actuator/RestHealthIndicator.kt index 980bf53..3c5c59b 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/support/monitoring/actuator/RestHealthIndicator.kt +++ b/module-support/src/main/kotlin/de/twomartens/support/monitoring/actuator/RestHealthIndicator.kt @@ -1,7 +1,7 @@ -package de.twomartens.timetable.support.monitoring.actuator +package de.twomartens.support.monitoring.actuator -import de.twomartens.timetable.property.ServiceProperties -import de.twomartens.timetable.support.interceptor.HeaderInterceptorRest +import de.twomartens.support.interceptor.HeaderInterceptorRest +import de.twomartens.support.property.HealthCheckProperties import org.springframework.boot.actuate.health.Health import org.springframework.boot.actuate.health.HealthIndicator import org.springframework.boot.actuate.health.Status @@ -32,7 +32,7 @@ class RestHealthIndicator( clock: Clock, interceptor: HeaderInterceptorRest, serverProperties: ServerProperties, private val restTemplateRestHealthIndicator: RestTemplate, - private val serviceProperties: ServiceProperties + private val serviceProperties: HealthCheckProperties ) : AbstractHealthIndicator(clock, Preparable { interceptor.markAsHealthCheck() }), HealthIndicator { private val randomizer = SecureRandom() private val urlPrefix: String = (HTTP_PREFIX + HOST + HOST_PORT_SEPARATOR @@ -53,7 +53,7 @@ class RestHealthIndicator( } companion object { - private const val URL_PATH = "/timetable/v1/healthCheck" + private const val URL_PATH = "/timetable/healthCheck" private const val GET_PARAMETER = "message=" } } diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/support/monitoring/statusprobe/CountBasedStatusProbe.kt b/module-support/src/main/kotlin/de/twomartens/support/monitoring/statusprobe/CountBasedStatusProbe.kt similarity index 93% rename from module-server/src/main/kotlin/de/twomartens/timetable/support/monitoring/statusprobe/CountBasedStatusProbe.kt rename to module-support/src/main/kotlin/de/twomartens/support/monitoring/statusprobe/CountBasedStatusProbe.kt index f2c597e..a2286c1 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/support/monitoring/statusprobe/CountBasedStatusProbe.kt +++ b/module-support/src/main/kotlin/de/twomartens/support/monitoring/statusprobe/CountBasedStatusProbe.kt @@ -1,4 +1,4 @@ -package de.twomartens.timetable.support.monitoring.statusprobe +package de.twomartens.support.monitoring.statusprobe import org.springframework.boot.actuate.health.Status import java.time.Clock diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/support/monitoring/statusprobe/PercentageBasedStatusProbe.kt b/module-support/src/main/kotlin/de/twomartens/support/monitoring/statusprobe/PercentageBasedStatusProbe.kt similarity index 96% rename from module-server/src/main/kotlin/de/twomartens/timetable/support/monitoring/statusprobe/PercentageBasedStatusProbe.kt rename to module-support/src/main/kotlin/de/twomartens/support/monitoring/statusprobe/PercentageBasedStatusProbe.kt index 5034b5c..9325754 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/support/monitoring/statusprobe/PercentageBasedStatusProbe.kt +++ b/module-support/src/main/kotlin/de/twomartens/support/monitoring/statusprobe/PercentageBasedStatusProbe.kt @@ -1,4 +1,4 @@ -package de.twomartens.timetable.support.monitoring.statusprobe +package de.twomartens.support.monitoring.statusprobe import org.springframework.boot.actuate.health.Status import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/support/monitoring/statusprobe/ScheduledStatusProbe.kt b/module-support/src/main/kotlin/de/twomartens/support/monitoring/statusprobe/ScheduledStatusProbe.kt similarity index 91% rename from module-server/src/main/kotlin/de/twomartens/timetable/support/monitoring/statusprobe/ScheduledStatusProbe.kt rename to module-support/src/main/kotlin/de/twomartens/support/monitoring/statusprobe/ScheduledStatusProbe.kt index abc6b29..b786d35 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/support/monitoring/statusprobe/ScheduledStatusProbe.kt +++ b/module-support/src/main/kotlin/de/twomartens/support/monitoring/statusprobe/ScheduledStatusProbe.kt @@ -1,4 +1,4 @@ -package de.twomartens.timetable.support.monitoring.statusprobe +package de.twomartens.support.monitoring.statusprobe import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler import org.springframework.scheduling.support.PeriodicTrigger diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/support/monitoring/statusprobe/StatusProbe.kt b/module-support/src/main/kotlin/de/twomartens/support/monitoring/statusprobe/StatusProbe.kt similarity index 96% rename from module-server/src/main/kotlin/de/twomartens/timetable/support/monitoring/statusprobe/StatusProbe.kt rename to module-support/src/main/kotlin/de/twomartens/support/monitoring/statusprobe/StatusProbe.kt index aaba095..99b1ec5 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/support/monitoring/statusprobe/StatusProbe.kt +++ b/module-support/src/main/kotlin/de/twomartens/support/monitoring/statusprobe/StatusProbe.kt @@ -1,4 +1,4 @@ -package de.twomartens.timetable.support.monitoring.statusprobe +package de.twomartens.support.monitoring.statusprobe import lombok.Getter import org.springframework.boot.actuate.health.Status diff --git a/module-support/src/main/kotlin/de/twomartens/support/monitoring/statusprobe/StatusProbeCriticality.kt b/module-support/src/main/kotlin/de/twomartens/support/monitoring/statusprobe/StatusProbeCriticality.kt new file mode 100644 index 0000000..9ae37b7 --- /dev/null +++ b/module-support/src/main/kotlin/de/twomartens/support/monitoring/statusprobe/StatusProbeCriticality.kt @@ -0,0 +1,7 @@ +package de.twomartens.support.monitoring.statusprobe + +enum class StatusProbeCriticality { + K1, + K2, + K3 +} diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/support/monitoring/statusprobe/StatusProbeLogger.kt b/module-support/src/main/kotlin/de/twomartens/support/monitoring/statusprobe/StatusProbeLogger.kt similarity index 98% rename from module-server/src/main/kotlin/de/twomartens/timetable/support/monitoring/statusprobe/StatusProbeLogger.kt rename to module-support/src/main/kotlin/de/twomartens/support/monitoring/statusprobe/StatusProbeLogger.kt index 3de90d6..917aa16 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/support/monitoring/statusprobe/StatusProbeLogger.kt +++ b/module-support/src/main/kotlin/de/twomartens/support/monitoring/statusprobe/StatusProbeLogger.kt @@ -1,4 +1,4 @@ -package de.twomartens.timetable.support.monitoring.statusprobe +package de.twomartens.support.monitoring.statusprobe import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.Logger diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/support/monitoring/statusprobe/TimeBasedStatusProbe.kt b/module-support/src/main/kotlin/de/twomartens/support/monitoring/statusprobe/TimeBasedStatusProbe.kt similarity index 96% rename from module-server/src/main/kotlin/de/twomartens/timetable/support/monitoring/statusprobe/TimeBasedStatusProbe.kt rename to module-support/src/main/kotlin/de/twomartens/support/monitoring/statusprobe/TimeBasedStatusProbe.kt index f7beed4..c562781 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/support/monitoring/statusprobe/TimeBasedStatusProbe.kt +++ b/module-support/src/main/kotlin/de/twomartens/support/monitoring/statusprobe/TimeBasedStatusProbe.kt @@ -1,4 +1,4 @@ -package de.twomartens.timetable.support.monitoring.statusprobe +package de.twomartens.support.monitoring.statusprobe import org.springframework.boot.actuate.health.Status import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler diff --git a/module-support/src/main/kotlin/de/twomartens/support/property/HealthCheckProperties.kt b/module-support/src/main/kotlin/de/twomartens/support/property/HealthCheckProperties.kt new file mode 100644 index 0000000..3d4c6c3 --- /dev/null +++ b/module-support/src/main/kotlin/de/twomartens/support/property/HealthCheckProperties.kt @@ -0,0 +1,10 @@ +package de.twomartens.support.property + +import io.swagger.v3.oas.annotations.media.Schema +import org.springframework.boot.context.properties.ConfigurationProperties + +@ConfigurationProperties(prefix = "de.twomartens.support.health") +@Schema(description = "Properties, to configure health check") +class HealthCheckProperties { + lateinit var greeting: String +} \ No newline at end of file diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/support/property/RestTemplateTimeoutProperties.kt b/module-support/src/main/kotlin/de/twomartens/support/property/RestTemplateTimeoutProperties.kt similarity index 94% rename from module-server/src/main/kotlin/de/twomartens/timetable/support/property/RestTemplateTimeoutProperties.kt rename to module-support/src/main/kotlin/de/twomartens/support/property/RestTemplateTimeoutProperties.kt index e4f1c0f..1af7b6e 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/support/property/RestTemplateTimeoutProperties.kt +++ b/module-support/src/main/kotlin/de/twomartens/support/property/RestTemplateTimeoutProperties.kt @@ -1,4 +1,4 @@ -package de.twomartens.timetable.property +package de.twomartens.support.property import org.springframework.boot.context.properties.ConfigurationProperties import org.springframework.boot.context.properties.bind.ConstructorBinding diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/support/property/StatusProbeProperties.kt b/module-support/src/main/kotlin/de/twomartens/support/property/StatusProbeProperties.kt similarity index 95% rename from module-server/src/main/kotlin/de/twomartens/timetable/support/property/StatusProbeProperties.kt rename to module-support/src/main/kotlin/de/twomartens/support/property/StatusProbeProperties.kt index ee667ff..c12f059 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/support/property/StatusProbeProperties.kt +++ b/module-support/src/main/kotlin/de/twomartens/support/property/StatusProbeProperties.kt @@ -1,4 +1,4 @@ -package de.twomartens.timetable.property +package de.twomartens.support.property import io.swagger.v3.oas.annotations.media.Schema import org.springframework.boot.context.properties.ConfigurationProperties diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/support/property/TimeProperties.kt b/module-support/src/main/kotlin/de/twomartens/support/property/TimeProperties.kt similarity index 92% rename from module-server/src/main/kotlin/de/twomartens/timetable/support/property/TimeProperties.kt rename to module-support/src/main/kotlin/de/twomartens/support/property/TimeProperties.kt index b0fb344..1e4a465 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/support/property/TimeProperties.kt +++ b/module-support/src/main/kotlin/de/twomartens/support/property/TimeProperties.kt @@ -1,4 +1,4 @@ -package de.twomartens.timetable.property +package de.twomartens.support.property import io.swagger.v3.oas.annotations.media.Schema import org.springframework.boot.context.properties.ConfigurationProperties diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/support/security/SpringPolicyEnforcer.kt b/module-support/src/main/kotlin/de/twomartens/support/security/SpringPolicyEnforcer.kt similarity index 98% rename from module-server/src/main/kotlin/de/twomartens/timetable/support/security/SpringPolicyEnforcer.kt rename to module-support/src/main/kotlin/de/twomartens/support/security/SpringPolicyEnforcer.kt index a62cc6d..91fd6bc 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/support/security/SpringPolicyEnforcer.kt +++ b/module-support/src/main/kotlin/de/twomartens/support/security/SpringPolicyEnforcer.kt @@ -1,4 +1,4 @@ -package de.twomartens.timetable.support.security +package de.twomartens.support.security import mu.KotlinLogging import org.keycloak.AuthorizationContext diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/support/security/SpringPolicyEnforcerFilter.kt b/module-support/src/main/kotlin/de/twomartens/support/security/SpringPolicyEnforcerFilter.kt similarity index 98% rename from module-server/src/main/kotlin/de/twomartens/timetable/support/security/SpringPolicyEnforcerFilter.kt rename to module-support/src/main/kotlin/de/twomartens/support/security/SpringPolicyEnforcerFilter.kt index 74b534c..5dc6791 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/support/security/SpringPolicyEnforcerFilter.kt +++ b/module-support/src/main/kotlin/de/twomartens/support/security/SpringPolicyEnforcerFilter.kt @@ -1,4 +1,4 @@ -package de.twomartens.timetable.support.security +package de.twomartens.support.security import jakarta.servlet.* import jakarta.servlet.http.HttpServletRequest diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/support/service/BusService.kt b/module-support/src/main/kotlin/de/twomartens/support/service/BusService.kt similarity index 89% rename from module-server/src/main/kotlin/de/twomartens/timetable/support/service/BusService.kt rename to module-support/src/main/kotlin/de/twomartens/support/service/BusService.kt index 781a640..d08cf56 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/support/service/BusService.kt +++ b/module-support/src/main/kotlin/de/twomartens/support/service/BusService.kt @@ -1,4 +1,4 @@ -package de.twomartens.timetable.support.service +package de.twomartens.support.service import org.springframework.beans.factory.ObjectProvider import org.springframework.cloud.bus.BusBridge diff --git a/module-support/support.gradle.kts b/module-support/support.gradle.kts new file mode 100644 index 0000000..3a26e5c --- /dev/null +++ b/module-support/support.gradle.kts @@ -0,0 +1,15 @@ +plugins { + id("twomartens.spring-boot-cloud-base") + id("twomartens.kotlin") + kotlin("kapt") +} + +dependencies { + implementation(libs.spring.boot.actuator) + implementation(libs.spring.boot.web) + implementation(libs.spring.openapi) + + implementation(libs.bundles.spring.boot.security) + implementation(libs.spring.cloud.leader.election) + implementation(libs.spring.cloud.starter.bus.kafka) +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index dc4096a..9d84666 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,9 +1,12 @@ val projectname: String = providers.gradleProperty("projectname").get() rootProject.name = projectname +include("bahnApi") include("common") -include("server") include("deploy") +include("model") +include("server") +include("support") for (subproject in rootProject.children) { subproject.projectDir = file("module-" + subproject.name)