Skip to content

Commit

Permalink
feat: serialization and versioning support (#9)
Browse files Browse the repository at this point in the history
* feat: `serialization` & `versioning` support is added
* docs: documentation improvements (for the new artifacts, root one)
  • Loading branch information
y9vad9 authored Oct 4, 2023
1 parent 8a5c724 commit 0816e21
Show file tree
Hide file tree
Showing 49 changed files with 1,572 additions and 343 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/publish-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
- name: Set up Java
uses: actions/setup-java@v3
with:
java-version: '17'
java-version: '11'
distribution: 'corretto'

- name: Cache Gradle dependencies
Expand All @@ -35,4 +35,4 @@ jobs:
SSH_HOST: ${{ secrets.SSH_HOST }}
SSH_PASSWORD: ${{ secrets.SSH_PASSWORD }}
SSH_USER: ${{ secrets.SSH_USER }}
run: ./gradlew publish -Pversion=dev-${{ env.LIB_VERSION }}
run: ./gradlew publish
175 changes: 112 additions & 63 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,59 +1,63 @@
![GitHub release (with filter)](https://img.shields.io/github/v/release/y9vad9/rsocket-kotlin-router)
![GitHub](https://img.shields.io/github/license/y9vad9/rsocket-kotlin-router)
# rsocket-kotlin-router
`rsocket-kotlin-router` is a customizable library designed to streamline and simplify routing
![GitHub release](https://img.shields.io/github/v/release/y9vad9/rsocket-kotlin-router) ![GitHub](https://img.shields.io/github/license/y9vad9/rsocket-kotlin-router)
# RSocket Router

`rsocket-kotlin-router` is a customisable library designed to streamline and simplify routing
for RSocket Kotlin server applications. This library offers a typesafe DSL for handling various
routes, serving as a declarative simplified alternative to manual routing that would
otherwise, result in long-winded ternary logic or exhaustive when statements.
otherwise result in long-winded ternary logic or exhaustive when statements.

Library provides the following features:
- [Routing Builder](#Interceptors)
- [Interceptors](#Interceptors)
- [Request Versioning](router-versioning)
- [Request Serialization](router-serialization)

## Features
### Router
It's the basic thing in the `rsocket-kotlin-router` that's responsible for managing routes, their settings, etc. You
can define it in the following way:
## How to use
First of all, you need to implement basic artifacts with routing support. For now, `rsocket-kotlin-router`
is available only at my self-hosted maven:
```kotlin
val ServerRouter = router {
router {
routeSeparator = '.'
routeProvider { metadata ->
metadata?.read(RoutingMetadata)?.tags?.first()
?: throw RSocketError.Invalid("No routing metadata was provided")
}

routing { // this: RoutingBuilder
// ...
}
}
repositories {
maven("https://maven.y9vad9.com")
}
```
To install it later, you can use `Router.installOn(RSocketRequestHandlerBuilder)` function:
```kotlin
fun ServerRequestHandler(router: Router): RSocket = RSocketRequestHandlerBuilder {
router.installOn(this)

dependencies {
implementation("com.y9vad9.rsocket.router:router-core:$version")
}
```
Or you can call `router` function directly in the `RSocketRequestHandlerBuilder` context – it will automatically
install router on the given context.
> For now, it's available for JVM only, but as there is no JVM platform API used,
> new targets will be available [upon your request](https://github.com/y9vad9/rsocket-kotlin-router/issues/new).
### Routing Builder
You can define routes using bundled DSL-Builder functions:
Example of defining RSocket router:
```kotlin
fun RoutingBuilder.usersRoute(): Unit = route("users") {
// extension function that wraps RSocket `requestResponse` into `route` with given path.
requestResponse("get") { payload -> TODO() }

// ... other
val serverRouter = router {
routeSeparator = '.'
routeProvider { metadata: ByteReadPacket? ->
metadata?.read(RoutingMetadata)?.tags?.first()
?: throw RSocketError.Invalid("No routing metadata was provided")
}

routing { // this: RoutingBuilder
route("authorization") {
requestResponse("register") { payload: Payload ->
// just 4 example
println(payload.data.readText())
Payload.Empty
}
}
}
}
```
> **Note** <br>
> The library does not include the functionality to add routing to a `metadataPush` type of request. I am not sure
> how it should be exactly implemented (API), so your ideas are welcome. For now, I consider it a per-project responsibility.
### Interceptors
> **Warning** <br>
> Interceptors are experimental feature: API can be changed in the future.
#### Preprocessors
Preprocessors are utilities that run before the routing feature applies. For cases, when you need to transform input into something or propagate
values using coroutines – you can extend [`Preprocessor.Modifier`](https://github.com/y9vad9/rsocket-kotlin-router/blob/8bace098e0a47e3cf514eec0dfb702f7e4e13591/router-core/src/commonMain/kotlin/com.y9vad9.rsocket.router/interceptors/Interceptor.kt#L35) or [`Preprocessor.CoroutineContext`](https://github.com/y9vad9/rsocket-kotlin-router/blob/8bace098e0a47e3cf514eec0dfb702f7e4e13591/router-core/src/commonMain/kotlin/com.y9vad9.rsocket.router/interceptors/Interceptor.kt#L27). Here's an example:

See also what else is supported:

<details id="Interceptors">
<summary>Interceptors</summary>
<i>Interceptors are experimental feature: API can be changed in the future.</i>

<b id="Preprocessors">Preprocessors</b>

Preprocessors are utilities that run before routing feature applies. For cases, when you need to transform input into something or propagate
values using coroutines – you can extend [`Preprocessor.Modifier`](https://github.com/y9vad9/rsocket-kotlin-router/blob/2a794e9a8c5d2ac53cb87ea58cfbe4a2ecfa217d/router-core/src/commonMain/kotlin/com.y9vad9.rsocket.router/interceptors/Interceptor.kt#L39) or [`Preprocessor.CoroutineContext`](https://github.com/y9vad9/rsocket-kotlin-router/blob/master/router-core/src/commonMain/kotlin/com.y9vad9.rsocket.router/interceptors/Interceptor.kt#L31). Here's an example:
```kotlin
class MyCoroutineContextElement(val value: String): CoroutineContext.Element {...}

Expand All @@ -65,7 +69,8 @@ class MyCoroutineContextPreprocessor : Preprocessor.CoroutineContext {
}
```

#### Route Interceptors
<b id="RouteInterceptors">Route Interceptors</b>

In addition to the `Preprocessors`, `rsocket-kotlin-router` also provides API to intercept specific routes:
```kotlin
@OptIn(ExperimentalInterceptorsApi::class)
Expand All @@ -75,9 +80,61 @@ class MyRouteInterceptor : RouteInterceptor.Modifier {
}
}
```
It has the same abilities as Preprocessors. You can take a look at it [here](https://github.com/y9vad9/rsocket-kotlin-router/blob/8bace098e0a47e3cf514eec0dfb702f7e4e13591/router-core/src/commonMain/kotlin/com.y9vad9.rsocket.router/interceptors/Interceptor.kt#L45).
### Testability

<b>Installation</b>
```kotlin
val serverRouter = router {
preprocessors {
forCoroutineContext(MyCoroutineContextPreprocessor())
}

sharedInterceptors {
forModification(MyRouteInterceptor())
}
}
```
</details>

<details>
<summary>Versioning support</summary>

To use request versioning in your project, use the following artifact:

```kotlin
dependencies {
// ...
implementation("com.y9vad9.rsocket.router:router-versioning-core:$version")
}
```
For details, please refer to the [versioning guide](router-versioning/README.md).
</details>

<details>
<summary>Serialization support</summary>

To make type-safe requests with serialization/deserialization mechanisms, implement the following:

```kotlin
dependencies {
implementation("com.y9vad9.rsocket.router:router-serialization-core:$version")
// for JSON support
implementation("com.y9vad9.rsocket.router:router-serialization-json:$version")
}
```
For details, please refer to the [serialization guide](router-serialization/README.md).
</details>

<details>
<summary>Testing</summary>

`rsocket-kotlin-router` provides ability to test your routes with `router-test` artifact:

```kotlin
dependencies {
implementation("com.y9vad9.rsocket.router:router-test:$version")
}
```

```kotlin
@Test
fun testRoutes() {
Expand All @@ -94,20 +151,12 @@ fun testRoutes() {
}
}
```
You can refer to [full example](router-test/src/jvmTest/kotlin/com/y9vad9/rsocket/router/test/RouterTest.kt).

## Implementation
To implement this library, you should define the following:
```kotlin
repositories {
maven("https://maven.y9vad9.com")
}
You can refer to the [example](router-core/test/src/jvmTest/kotlin/com/y9vad9/rsocket/router/test/RouterTest.kt) for more details.
</details>

dependencies {
implementation("com.y9vad9.rsocket.router:router-core:$version")
// for testing
implementation("com.y9vad9.rsocket.router:router-test:$version")
}
```
> For now, it's available for JVM only, but as there is no JVM platform API used,
> new targets will be available [upon your request](https://github.com/y9vad9/rsocket-kotlin-router/issues/new).
## Bugs and Feedback
For bugs, questions and discussions please use the [GitHub Issues](https://github.com/y9vad9/rsocket-kotlin-router/issues).

## License
This library is licensed under [MIT License](LICENSE). Feel free to use, modify, and distribute it for any purpose.
18 changes: 18 additions & 0 deletions build-conventions/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
plugins {
`kotlin-dsl`
}

repositories {
mavenCentral()
google()
gradlePluginPortal()
}

kotlin {
jvmToolchain(11)
}

dependencies {
api(libs.kotlin.plugin)
api(libs.vanniktech.maven.publish)
}
14 changes: 14 additions & 0 deletions build-conventions/settings.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
dependencyResolutionManagement {
repositories {
mavenCentral()
google()
}

versionCatalogs {
create("libs") {
from(files("../gradle/libs.versions.toml"))
}
}
}

rootProject.name = "conventions"
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import org.jetbrains.kotlin.gradle.dsl.*

plugins {
kotlin("multiplatform")
id("com.vanniktech.maven.publish")
}

kotlin {
jvm()
jvmToolchain(11)

explicitApi = ExplicitApiMode.Strict
}

mavenPublishing {
pom {
url.set("https://github.com/y9vad9/rsocket-kotlin-router")
inceptionYear.set("2023")

licenses {
license {
name.set("The MIT License")
url.set("https://opensource.org/licenses/MIT")
distribution.set("https://opensource.org/licenses/MIT")
}
}

developers {
developer {
id.set("y9vad9")
name.set("Vadym Yaroshchuk")
url.set("https://github.com/y9vad9/")
}
}

scm {
url.set("https://github.com/y9vad9/rsocket-kotlin-router")
connection.set("scm:git:git://github.com/y9vad9/rsocket-kotlin-router.git")
developerConnection.set("scm:git:ssh://[email protected]/y9vad9/rsocket-kotlin-router.git")
}

issueManagement {
system.set("GitHub Issues")
url.set("https://github.com/y9vad9/rsocket-kotlin-router/issues")
}
}
}

publishing {
repositories {
maven {
name = "y9vad9Maven"

url = uri(
"sftp://${System.getenv("SSH_HOST")}:22/${System.getenv("SSH_DEPLOY_PATH")}"
)

credentials {
username = System.getenv("SSH_USER")
password = System.getenv("SSH_PASSWORD")
}
}
}
}
23 changes: 0 additions & 23 deletions build-logic/publish-library-plugin/build.gradle.kts

This file was deleted.

1 change: 0 additions & 1 deletion build-logic/publish-library-plugin/settings.gradle.kts

This file was deleted.

Loading

0 comments on commit 0816e21

Please sign in to comment.