-
Notifications
You must be signed in to change notification settings - Fork 28
Add kxrpc grpc plugin #224
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
class SampleServiceImpl : SampleService { | ||
override suspend fun greeting(name: ClientGreeting): ServerGreeting { | ||
return ServerGreeting { content = "Hello, ${name.name}!" } | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
Add gRPC services to your Ktor application! | ||
|
||
# Usage | ||
|
||
## DISCLAIMER | ||
**This is a dev preview of the kotlinx-rpc gRPC plugin for Ktor. It is not yet ready for production use. | ||
The artifacts provided are dev artifacts, and the API may and will change in the future.** | ||
|
||
**Please use it to play with the plugin. We will appreciate any feedback on the | ||
[Github](https://github.com/Kotlin/kotlinx-rpc/issues) | ||
or in [Slack](https://kotlinlang.slack.com/archives/C072YJ3Q91V)** | ||
|
||
## gRPC Server | ||
You can add gRPC services to your Ktor application by using the `grpc` function provided by the plugin. | ||
Here's an example: | ||
|
||
Declare a service in a `.proto` file. | ||
```protobuf | ||
syntax = "proto3"; | ||
|
||
message ClientGreeting { | ||
string name = 1; | ||
} | ||
|
||
message ServerGreeting { | ||
string content = 2; | ||
} | ||
|
||
service SampleService { | ||
rpc greeting(ClientGreeting) returns (ServerGreeting); | ||
} | ||
``` | ||
|
||
The `SampleService` interface will generated for you alongside with other types and helper declarations. | ||
|
||
Define an implementation. Then register it on the server and all done: | ||
|
||
```kotlin | ||
class SampleServiceImpl : SampleService { | ||
override suspend fun greeting(name: ClientGreeting): ServerGreeting { | ||
return ServerGreeting { content = "Hello, ${name.name}!" } | ||
} | ||
} | ||
|
||
fun Application.module() { | ||
grpc { | ||
registerService<SampleService> { SampleServiceImpl() } | ||
} | ||
} | ||
``` | ||
|
||
## gRPC Client | ||
You can use any gRPC client on the other end, but also you can use ours! | ||
|
||
To do that - use the `GrpcClient` class: | ||
```Kotlin | ||
val client = GrpcClient("localhost", 8081) { | ||
usePlaintext() | ||
} | ||
|
||
val response = client.withService<SampleService>().greeting( | ||
ClientGreeting { | ||
name = "Alex" | ||
} | ||
) | ||
|
||
assertEquals("Hello, Alex!", response.content, "Wrong response message") | ||
|
||
client.close() | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import io.ktor.server.application.Application | ||
import kotlinx.rpc.grpc.ktor.server.grpc | ||
import kotlinx.rpc.registerService | ||
|
||
fun Application.install() { | ||
grpc { | ||
registerService<SampleService> { SampleServiceImpl() } | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
name: gRPC | ||
description: Add gRPC services to your Ktor server with kotlinx.rpc | ||
vcsLink: https://github.com/Kotlin/kotlinx-rpc | ||
license: Apache 2.0 | ||
category: Frameworks | ||
maven: | ||
disabled: true | ||
# TODO KRPC-131 waiting for fixes to enable amper support | ||
amper: | ||
disabled: true | ||
gradle: | ||
pluginRepositories: | ||
- url: https://maven.pkg.jetbrains.space/public/p/krpc/grpc | ||
repositories: | ||
- url: https://maven.pkg.jetbrains.space/public/p/krpc/grpc | ||
plugins: | ||
- id: org.jetbrains.kotlinx.rpc.plugin | ||
version: $kotlinx-rpc-grpc | ||
installation: | ||
inside_app: inside_app.kt | ||
test_function: test.kt | ||
gradle_build: |- | ||
rpc { | ||
grpc() | ||
} | ||
sources: | ||
- SampleServiceImpl.kt | ||
|
||
# I need src/main/proto structure | ||
# | ||
#mainSourceSet: | ||
# - proto/service.proto | ||
|
||
# Or Maybe? | ||
#src: | ||
# - main: | ||
# - proto: | ||
Comment on lines
+26
to
+37
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm updating it so you can just write:
And it'll move the proto file into its own source folder. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @bjhham is this solution much easier than to make custom source mapping? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, custom source mappings would need a bit of extra work to ensure they play nice with the gradle vs. amper source folder layout logic. The new project templating engine I'm working on will have support for all that though. |
||
# - proto/service.proto |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
syntax = "proto3"; | ||
|
||
package io.ktor; | ||
Mr3zee marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
message ClientGreeting { | ||
string name = 1; | ||
} | ||
|
||
message ServerGreeting { | ||
string content = 2; | ||
} | ||
|
||
service SampleService { | ||
rpc greeting(ClientGreeting) returns (ServerGreeting); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
// DO NOT DELETE, not included in generation, but is used for code highlighting in registry | ||
|
||
class ClientGreeting { | ||
var name: String = "" | ||
|
||
companion object { | ||
operator fun invoke(body: ClientGreeting.() -> Unit): ClientGreeting = ClientGreeting().apply(body) | ||
} | ||
} | ||
|
||
class ServerGreeting { | ||
var content: String = "" | ||
|
||
companion object { | ||
operator fun invoke(body: ServerGreeting.() -> Unit): ServerGreeting = ServerGreeting().apply(body) | ||
} | ||
} | ||
|
||
interface SampleService { | ||
suspend fun greeting(name: ClientGreeting): ServerGreeting | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import io.ktor.server.testing.testApplication | ||
import kotlinx.rpc.grpc.GrpcClient | ||
import kotlinx.rpc.grpc.ktor.server.grpc | ||
import kotlinx.rpc.registerService | ||
import kotlinx.rpc.withService | ||
import kotlin.test.Test | ||
import kotlin.test.assertEquals | ||
import kotlin.time.Duration.Companion.minutes | ||
|
||
class ApplicationRpcTest { | ||
@Test | ||
fun testRpc() = testApplication { | ||
application { | ||
grpc(8081) { | ||
registerService<SampleService> { SampleServiceImpl() } | ||
} | ||
} | ||
|
||
startApplication() | ||
|
||
val client = GrpcClient("localhost", 8081) { | ||
usePlaintext() | ||
} | ||
|
||
val response = client.withService<SampleService>().greeting( | ||
ClientGreeting { | ||
name = "Alex" | ||
} | ||
) | ||
|
||
assertEquals("Hello, Alex!", response.content, "Wrong response message") | ||
|
||
client.shutdown() | ||
client.awaitTermination(1.minutes) | ||
} | ||
Mr3zee marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
"[3.0,)": | ||
- org.jetbrains.kotlinx:kotlinx-rpc-grpc-ktor-server:$kotlinx-rpc-grpc | ||
- io.grpc:grpc-netty:1.73.0 | ||
- io.grpc:grpc-kotlin-stub:1.4.1 |
Uh oh!
There was an error while loading. Please reload this page.