Skip to content

Commit

Permalink
First implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
btlines committed Jul 7, 2017
1 parent 18f7d0a commit 17bcfb9
Show file tree
Hide file tree
Showing 10 changed files with 589 additions and 2 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
*.class
*.log
target/
*/target/
8 changes: 8 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
dist: trusty
language: scala
jdk: openjdk8

script:
- sbt ++2.10.6 generator/compile
- sbt ++2.12.2 runtime/compile
- test $TRAVIS_PULL_REQUEST = false && sbt updateImpactSubmit || true
118 changes: 116 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,116 @@
# grpcmonix
Use Monix's Observable with GRPC services
[![Build status](https://api.travis-ci.org/btlines/grpcmonix.svg?branch=master)](https://travis-ci.org/btlines/grpcmonix)
[![Dependencies](https://app.updateimpact.com/badge/852442212779298816/grpcmonix.svg?config=compile)](https://app.updateimpact.com/latest/852442212779298816/grpcmonix)
[![License](https://img.shields.io/:license-MIT-blue.svg)](https://opensource.org/licenses/MIT)
[![GRPCMonixGenerator](https://api.bintray.com/packages/beyondthelines/maven/grpcmonixgenerator/images/download.svg) ](https://bintray.com/beyondthelines/maven/grpcmonixgenerator/_latestVersion)
[![GRPCMonixRuntime](https://api.bintray.com/packages/beyondthelines/maven/grpcmonixruntime/images/download.svg) ](https://bintray.com/beyondthelines/maven/grpcmonixruntime/_latestVersion)

# GRPC Monix

Use Monix's Tasks and Observables to implement your GRPC services instead of Java's StreamObservers.

- Unary calls return a Task[T] for the response returned by the server
- Server streaming calls return an Observable[T] for the elements returned by the server
- Client streaming calls take an Observable[T] for the elements emitted by the client and return a Task[U] for the server response
- Bidi streaming calls take an Observable[T] for the elements emitted by the client and return an Observable[U] for the elements returned by the server

## Installation

You need to enable [`sbt-protoc`](https://github.com/thesamet/sbt-protoc) plugin to generate source code for the proto definitions.
You can do it by adding a `protoc.sbt` file into your `project` folder with the following lines:

```scala
addSbtPlugin("com.thesamet" % "sbt-protoc" % "0.99.9")

resolvers += Resolver.bintrayRepo("beyondthelines", "maven")

libraryDependencies ++= Seq(
"com.trueaccord.scalapb" %% "compilerplugin" % "0.6.0-pre5",
"beyondthelines" %% "grpcmonixgenerator" % "0.0.0"
)
```

Here we add a dependency to the GRPCMonix protobuf generator.

Then we need to trigger the generation from the `build.sbt`:

```scala
PB.targets in Compile := Seq(
// compile your proto files into scala source files
scalapb.gen() -> (sourceManaged in Compile).value,
// generate the GRPCMonix source code
grpcmonix.generators.GrpcMonixGenerator -> (sourceManaged in Compile).value
)

resolvers += Resolver.bintrayRepo("beyondthelines", "maven")

libraryDependencies += "beyondthelines" %% "grpcgatewayruntime" % "0.0.0"
```

### Usage

You're now ready to implement your GRPC service using Monix's Tasks and Observable.

To implement your service's business logic you simply extend the GRPC monix generated trait.

E.g. for the RouteGuide service:

```scala
class RouteGuideMonixService(features: Seq[Feature]) extends RouteGuideGrpcMonix.RouteGuide {
// Unary call
override def getFeature(request: Point): Task[Feature] = ???
// Server streaming
override def listFeatures(request: Rectangle): Observable[Feature] = ???
// Client streaming
override def recordRoute(points: Observable[Point]): Task[RouteSummary] = ???
// Bidi streaming
override def routeChat(notes: Observable[RouteNote]): Observable[RouteNote] = ???
}
```

The server creation is similar except you need to provide a Monix's `Scheduler` instead of an `ExecutionContext` when binding the service

```scala
val server = ServerBuilder
.forPort(8980)
.addService(
RouteGuideGrpcMonix.bindService(
new RouteGuideMonixService(features), // the service implemented above
monix.execution.Scheduler.global
)
)
.build()
```

Tasks and Observables are also available on the client side:

```scala
val channel = ManagedChannelBuilder
.forAddress("localhost", 8980)
.usePlainText(true)
.build()

val stub = RouteGuideGrpcMonix.stub(channel) // only an async stub is provided

// Unary call
val feature: Task[Feature] = stub.getFeature(408031728, -748645385)
// Server streaming
val request = Rectangle(
lo = Some(Point(408031728, -748645385)),
hi = Some(Point(413700272, -742135189))
)
val features: Observable[Feature] = stub.listFeatures(request)
// Client streaming
val route: Observable[Feature] = Observable
.fromIterable(features.map(_.getLocation))
.delayOnNext(100.millis)
val summary: Task[RouteSummary] = stub.recordRoute(route)
// Bidi streaming
val notes: Observable[RouteNote] = Observable(
RouteNote(message = "First message", location = Some(Point(0, 0))),
RouteNote(message = "Second message", location = Some(Point(0, 1))),
RouteNote(message = "Third message", location = Some(Point(1, 0))),
RouteNote(message = "Fourth message", location = Some(Point(1, 1)))
).delayOnNext(1.second)
val allNotes = stub.routeChat(notes)
```

29 changes: 29 additions & 0 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
lazy val commonSettings = Seq(
organization := "beyondthelines",
version := "0.0.0",
licenses := ("MIT", url("http://opensource.org/licenses/MIT")) :: Nil,
bintrayOrganization := Some("beyondthelines"),
bintrayPackageLabels := Seq("scala", "protobuf", "grpc", "monix")
)

lazy val runtime = (project in file("runtime"))
.settings(
commonSettings,
scalaVersion := "2.12.2",
crossScalaVersions := Seq("2.12.2", "2.11.11"),
name := "GrpcMonixRuntime",
libraryDependencies ++= Seq(
"com.trueaccord.scalapb" %% "scalapb-runtime-grpc" % "0.6.0-pre5",
"io.monix" %% "monix" % "2.3.0"
)
)

lazy val generator = (project in file("generator"))
.settings(
commonSettings,
scalaVersion := "2.10.6",
name := "GrpcMonixGenerator",
libraryDependencies ++= Seq(
"com.trueaccord.scalapb" %% "compilerplugin" % "0.6.0-pre5"
)
)
Loading

0 comments on commit 17bcfb9

Please sign in to comment.