diff --git a/.github/workflows/cd.yaml b/.github/workflows/cd.yaml new file mode 100644 index 0000000..f4f851b --- /dev/null +++ b/.github/workflows/cd.yaml @@ -0,0 +1,32 @@ +name: "Library publishing" + +on: + push: + branches: + - main + paths: + - client-common/** + - marudor-client/** + - regenbogen-ice-client/** + - träwelling-client/** + +env: + ORG_GRADLE_PROJECT_signingKey: ${{ secrets.GPG_KEY }} + ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.GPG_KEY_PASSWORD }} + JFROG_KEY: ${{ secrets.JFROG_KEY }} + JFROG_USER: ${{ secrets.JFROG_USER }} + +jobs: + publish: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 18 + uses: actions/setup-java@v1 + with: + distribution: 'temurin' + java-version: 18 + - name: Publish with Gradle + uses: gradle/gradle-build-action@v2 + with: + arguments: publishAllPublicationsToMavenRepository diff --git a/.gitignore b/.gitignore index c4f2ab3..f915ef7 100644 --- a/.gitignore +++ b/.gitignore @@ -79,8 +79,6 @@ fabric.properties # Ignore Gradle GUI config gradle-app.setting -# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) -!gradle-wrapper.jar # Cache of project .gradletasknamecache @@ -115,3 +113,5 @@ hs_err_pid* **/.test-env +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar diff --git a/buildSrc/src/main/kotlin/hafalsch-publishing.gradle.kts b/buildSrc/src/main/kotlin/hafalsch-publishing.gradle.kts index b5032f9..f33e4ef 100644 --- a/buildSrc/src/main/kotlin/hafalsch-publishing.gradle.kts +++ b/buildSrc/src/main/kotlin/hafalsch-publishing.gradle.kts @@ -67,7 +67,7 @@ signing { ) publishing.publications.withType { - //sign(this) + sign(this) } } diff --git a/client-common/.ci b/client-common/.ci new file mode 100644 index 0000000..0cfbf08 --- /dev/null +++ b/client-common/.ci @@ -0,0 +1 @@ +2 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..41d9927 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 diff --git a/regenbogen-ice-client/src/commonMain/kotlin/dev/schlaubi/hafalsch/rainbow_ice/RainbowICE.kt b/regenbogen-ice-client/src/commonMain/kotlin/dev/schlaubi/hafalsch/rainbow_ice/RainbowICE.kt index 7da1d27..0a4313a 100644 --- a/regenbogen-ice-client/src/commonMain/kotlin/dev/schlaubi/hafalsch/rainbow_ice/RainbowICE.kt +++ b/regenbogen-ice-client/src/commonMain/kotlin/dev/schlaubi/hafalsch/rainbow_ice/RainbowICE.kt @@ -3,11 +3,18 @@ package dev.schlaubi.hafalsch.rainbow_ice import dev.schlaubi.hafalsch.client.ClientCompanion import dev.schlaubi.hafalsch.client.ClientResources import dev.schlaubi.hafalsch.client.util.safeBody +import dev.schlaubi.hafalsch.rainbow_ice.entity.Station import dev.schlaubi.hafalsch.rainbow_ice.entity.TrainVehicle import io.ktor.client.call.* import io.ktor.client.plugins.resources.* +import io.ktor.client.statement.* import dev.schlaubi.hafalsch.rainbow_ice.routes.RainbowICE as RainbowICERoute +/** + * Mapper of the [regenbogen-ice.de](regenbogen-ice.de) API. + * + * You might need to import [dev.schlaubi.hafalsch.client.invoke] to use [RainbowICEBuilder] + */ public class RainbowICE(private val resources: ClientResources) { /** @@ -36,9 +43,28 @@ public class RainbowICE(private val resources: ClientResources) { includeRoutes: Boolean? = null, includeMarudorLink: Boolean? = null ): TrainVehicle? = - resources.client.get(RainbowICERoute.TrainVehicle(query, tripLimit, includeRoutes, includeMarudorLink)) + resources.client.get(RainbowICERoute.TrainVehicle.Specific(query, tripLimit, includeRoutes, includeMarudorLink)) .safeBody() + /** + * Provides station autocomplete for [query]. + * + * **This only includes long distance travel stations** + */ + public suspend fun stationSearch(query: String): List = + resources.client.get(RainbowICERoute.StationSearch(query)).body() + + /** + * Returns an RSS feed for the `Regenbogen ICE`. + */ + public suspend fun rss(): HttpResponse = resources.client.get(RainbowICERoute.Rss()) + + /** + * Returns an RSS feed for the `Regenbogen ICE` at [station]. + */ + public suspend fun rss(station: String): HttpResponse = + resources.client.get(RainbowICERoute.Rss.ForStation(station)) + public companion object : ClientCompanion { override fun newBuilder(): RainbowICEBuilder = RainbowICEBuilder() } diff --git a/regenbogen-ice-client/src/commonMain/kotlin/dev/schlaubi/hafalsch/rainbow_ice/annotations/ExperimentalRainbowICEApi.kt b/regenbogen-ice-client/src/commonMain/kotlin/dev/schlaubi/hafalsch/rainbow_ice/annotations/ExperimentalRainbowICEApi.kt new file mode 100644 index 0000000..1a7cc24 --- /dev/null +++ b/regenbogen-ice-client/src/commonMain/kotlin/dev/schlaubi/hafalsch/rainbow_ice/annotations/ExperimentalRainbowICEApi.kt @@ -0,0 +1,9 @@ +package dev.schlaubi.hafalsch.rainbow_ice.annotations + +/** + * Annotation marking experimental rainbow ice apis. + */ +@Target(AnnotationTarget.CLASS, AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION) +@MustBeDocumented +@RequiresOptIn +public annotation class ExperimentalRainbowICEApi diff --git a/regenbogen-ice-client/src/commonMain/kotlin/dev/schlaubi/hafalsch/rainbow_ice/entity/Station.kt b/regenbogen-ice-client/src/commonMain/kotlin/dev/schlaubi/hafalsch/rainbow_ice/entity/Station.kt new file mode 100644 index 0000000..a43be3e --- /dev/null +++ b/regenbogen-ice-client/src/commonMain/kotlin/dev/schlaubi/hafalsch/rainbow_ice/entity/Station.kt @@ -0,0 +1,12 @@ +package dev.schlaubi.hafalsch.rainbow_ice.entity + +import kotlinx.serialization.Serializable + +/** + * Representation of a train station. + * + * @property evaNumber the eva (or IBNR) of the station + * @property name the name of the station + */ +@Serializable +public data class Station(val evaNumber: Int, val name: String) diff --git a/regenbogen-ice-client/src/commonMain/kotlin/dev/schlaubi/hafalsch/rainbow_ice/entity/TrainVehicle.kt b/regenbogen-ice-client/src/commonMain/kotlin/dev/schlaubi/hafalsch/rainbow_ice/entity/TrainVehicle.kt index 60e48d4..238f599 100644 --- a/regenbogen-ice-client/src/commonMain/kotlin/dev/schlaubi/hafalsch/rainbow_ice/entity/TrainVehicle.kt +++ b/regenbogen-ice-client/src/commonMain/kotlin/dev/schlaubi/hafalsch/rainbow_ice/entity/TrainVehicle.kt @@ -4,14 +4,36 @@ import kotlinx.datetime.Instant import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable +/** + * Representation of a train vehicle. + * + * @property number the tzn of the train + * @property name the name of the train + * @property trainType the type of the train + * @property buildingSeries the [building series](https://en.wikipedia.org/wiki/DB_locomotive_classification) of the train + * @property trips a list of [Trips][Trip] if requested + */ @Serializable public data class TrainVehicle( val number: Int, - val name: String, + val name: String?, @SerialName("train_type") val trainType: String, + @SerialName("building_series") + val buildingSeries: Int, val trips: List? = null ) { + /** + * Representation of a trip. + * + * @property groupIndex the index of this train (if connected to another train) + * @property trainType type of the train + * @property trainNumber train number of this trip + * @property originStation name of the origin station + * @property destinationStation name of the destination station + * @property marudor link to [marudor.de]()marudor.de) if requested + * @property stops list of [Stops][Stop] the train stops at + */ @Serializable public data class Trip( @SerialName("group_index") @@ -33,6 +55,16 @@ public data class TrainVehicle( val marudor: String? = null, val stops: List ) { + /** + * Representation of a stop. + * + * @property cancelled integer for whatever reason (probably 0=false,1=true) + * @property station the station name + * @property scheduledDeparture an [Instant] in which the train is scheduled to depart + * @property departure an [Instant] in which the train departed + * @property scheduledArrival an [Instant] in which the train is scheduled to arrive + * @property arrival an [Instant] in which the train arrived + */ @Serializable public data class Stop( val cancelled: Int, diff --git a/regenbogen-ice-client/src/commonMain/kotlin/dev/schlaubi/hafalsch/rainbow_ice/routes/RainbowICE.kt b/regenbogen-ice-client/src/commonMain/kotlin/dev/schlaubi/hafalsch/rainbow_ice/routes/RainbowICE.kt index 334944b..8b512df 100644 --- a/regenbogen-ice-client/src/commonMain/kotlin/dev/schlaubi/hafalsch/rainbow_ice/routes/RainbowICE.kt +++ b/regenbogen-ice-client/src/commonMain/kotlin/dev/schlaubi/hafalsch/rainbow_ice/routes/RainbowICE.kt @@ -1,23 +1,84 @@ package dev.schlaubi.hafalsch.rainbow_ice.routes +import dev.schlaubi.hafalsch.rainbow_ice.annotations.ExperimentalRainbowICEApi +import dev.schlaubi.hafalsch.rainbow_ice.entity.TrainVehicle as TrainVehicleEntity import io.ktor.resources.* import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable +/** + * Mapping of [Regenbogen ICE API](https://github.com/regenbogen-ice/api). + */ @Resource("") @Serializable public class RainbowICE { + + /** + * Returns an RSS feed for the `Regenbogen ICE`. + */ + @Serializable + @Resource("rss") + public data class Rss(val rainbowICE: RainbowICE = RainbowICE()) { + + /** + * Returns an RSS feed for the `Regenbogen ICE` at [station]. + */ + @Serializable + @Resource("{station}") + public data class ForStation(val station: String, val rss: Rss = Rss()) + } + + /** + * Provides station autocomplete for [query]. + * + * **This only includes long distance travel stations** + */ + @Serializable + @Resource("stationSearch/{query}") + public data class StationSearch(val query: String, val rainbowICE: RainbowICE = RainbowICE()) + + /** + * Provides train autocomplete for [query] (TZn and name). + */ @Serializable @Resource("autocomplete/{query}") public data class Autocomplete(val query: String, val rainbowICE: RainbowICE = RainbowICE()) + /** + * Meta-class for `/train_vehicle` route. + */ @Serializable @Resource("train_vehicle") - public data class TrainVehicle( - @SerialName("q") val query: String, - @SerialName("trip_limit") val tripLimit: Int? = null, - @SerialName("include_routes") val includeRoutes: Boolean? = null, - @SerialName("include_marudor_link") val includeMarudorLink: Boolean? = null, - val rainbowICE: RainbowICE = RainbowICE() - ) + public data class TrainVehicle(val rainbowICE: RainbowICE = RainbowICE()) { + /** + * Fetches [train information][TrainVehicleEntity] for [query]. + * + * @property tripLimit How many [trips][TrainVehicleEntity.trips] to fetch + * @property includeRoutes whether to include [TrainVehicleEntity.trips] + * @property includeMarudorLink whether to include [TrainVehicleEntity.Trip.marudor] + */ + @Serializable + @Resource("") + public data class Specific( + @SerialName("q") val query: String, + @SerialName("trip_limit") val tripLimit: Int? = null, + @SerialName("include_routes") val includeRoutes: Boolean? = null, + @SerialName("include_marudor_link") val includeMarudorLink: Boolean? = null, + val trainVehicle: TrainVehicle = TrainVehicle() + ) + + + /** + * Probably retrieves all trains. + * + * **This api exists in the code, however does not exist on the production instance** + * + * https://github.com/regenbogen-ice/api/blob/canary/src/webserver/paths/train_vehicle.ts#L124-L132 exists + * however https://regenbogen-ice.de/api/train_vehicle/all returns 404 + */ + @ExperimentalRainbowICEApi + @Serializable + @Resource("all") + public data class All(val trainVehicle: TrainVehicle = TrainVehicle()) + } }