Skip to content

daviddenton/protokruft

Repository files navigation

coverage kotlin build status bintray version

Protokruft is a simple Gradle plugin for generating a Kotlin DSL from generated Java Protobuf sources. It removes the worst of the Java boilerplate that is generated, leaving you with a nice clean syntax in your Kotlin. The target classes are:

  • messages which extend GeneratedMessageV3.
  • RPC Server interfaces which extend BindableService or XXXImplBase
  • RPC Client interfaces which extend BlockingStub

By default, the generated classes are put next to the Java files, into the build/generated/source/proto/main/java folder.

What does it do?

Given this proto:

syntax = "proto3";

option java_package = "org.protokruft.example1";
package AnExample1;

service CarService {
    rpc GetEngine (Car) returns (Engine) {}
}

message Car {
    string model = 1;
    Engine engine = 2;
}

message Engine {
    int32 cc = 1;
    int32 bhp = 2;
}

The generated Java code from the Google protoc would be used like this:

    val person = Person.newBuilder()
            .setName("Hello Kitty")
            .setAddress(
                    Address.newBuilder()
                            .setNumber(123)
                            .setStreet("Hello Kitty Street")
                            .setPostcode("N304SD")
                            .build())
            .build()

Sprinkle on some Protokruft, and you can use it like this to hide the newBuilder().build() boilerplate:

    val person = newPerson {
        name = "Hello Kitty"
        address = newAddress {
            number = 123
            street = "Hello Kitty Street"
            postcode = "N304SD"
        }
    }

... or like this to scope the Builder as it:

    val personScoped = newPerson.also {
        it.name = "Hello Kitty"
        it.address = newAddress.also {
            it.number = 123
            it.street = "Hello Kitty Street"
            it.postcode = "N304SD"
        }
    }

For services, Protokruft generates this interface which wraps the final class generated by GRPC, allowing you to mock the service interfaces:

interface CarService {
    fun getEngine(car: Example1.Car): Example1.Engine

    object Grpc {
        object Client {
            operator fun invoke(channel: Channel): CarService {
                val stub = CarServiceGrpc.newBlockingStub(channel)
                return object : CarService {
                    override fun getEngine(car: Example1.Car): Example1.Engine = stub.getEngine(car)
                }
            }
        }

        object Server {
            operator fun invoke(delegate: PersonService): BindableService = object : PersonServiceImplBase() {
                override fun getAddress(person: Example2.Person, responseObserver: StreamObserver<Example2.Address>) {
                    responseObserver.onNext(delegate.getAddress(person))
                    responseObserver.onCompleted()
                }
            }
        }
    }
}

val newBlockingStub: CarService = CarService.Grpc.Client(channel)
val grpcService: BindableService = CarService.Grpc.Server(myCarServiceImplementation)

Optionally, you can also add a markerInterface option to aid IDE navigability.

Use it:

Merge the following sections into your Gradle file. This assumes that you've already got Protobuf classes being generated by the protobuf-gradle-plugin:

    buildscript {
        dependencies {
            classpath 'org.protokruft:protokruft:2.X.X'
        }
    }

    repositories {
        jcenter()
    }

    apply plugin: 'protokruft'

    protokruft {
        packageNames = ["com.mygreatpackage"] // "*" by default
        messageClassFile = "customMessage" // "messageDsl" by default
        messageDslPrefix = "customPrefix" // "new" by default
        serviceClassFile = "customService" // "serviceDsl" by default
        serviceDslSuffix = "CustomSuffix" // "" by default
        markerInterface = "com.my.great.Interface" // null by default
    }

    // allows you to just run generateProtoDsl
    generateProtobufDsl.dependsOn('generateProto')    

Then just run: ./gradlew generateProtobufDsl

NB: To generate classes correctly, your project should be using GRPC v1.18.0 or higher of the GRPC libraries.

About

Generate a DSL to hide all the hideous GRPC boilerplate

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •