Skip to content

Commit

Permalink
Adds new conformance/client module
Browse files Browse the repository at this point in the history
- This contains the new conformance client, for the new conformance tests
- The base module has all of the logic and uses the Java Lite runtime (since
  it is lowest common denominator)
- There are google-java and google-javalite modules which have the main
  functions for running the clients. The google-java module swaps out the
  javalite runtime for the java runtime. The client module (with shared logic)
  can convert from javalite generated messages to java generated messages by
  serializing and deserializing
- The adapt package in the client module provides a glimpse of what I think
  better stream APIs would look like. I'd like to update the actual stream
  interfaces to more closely match this in the future. For now, the code just
  adapts exising interfaces to these.
- To test the different unary invocation styles, there is an enum for each
  style and a method in UnaryClient that is an adapter: a single interface for
  the actual conformance client to use (that looks like the Callback style), but
  under the hood it will call the suspend, callback, or blocking methods based
  on the desired invocation style.
- The actual client logic is TODO, in various handle* methods of the Client
  class. This felt like a big enough chunk already, so I figured it would be
  good to get this reviewed before plowing through the rest.
  • Loading branch information
jhump committed Dec 20, 2023
1 parent 4f9af3d commit 9bd518f
Show file tree
Hide file tree
Showing 30 changed files with 1,408 additions and 0 deletions.
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ generate: $(PROTOC) buildplugin generateconformance generateexamples ## Generate
.PHONY: generateconformance
generateconformance: $(PROTOC) buildplugin ## Generate protofiles for conformance tests.
buf generate --template conformance/buf.gen.yaml -o conformance conformance/proto
buf generate --template conformance/client/buf.gen.yaml -o conformance/client buf.build/connectrpc/conformance:v1.0.0-rc1
buf generate --template conformance/client/buf.gen.lite.yaml -o conformance/client buf.build/connectrpc/conformance:v1.0.0-rc1

.PHONY: generateexamples
generateexamples: $(PROTOC) buildplugin ## Generate proto files for example apps.
Expand Down
20 changes: 20 additions & 0 deletions conformance/client/buf.gen.lite.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
version: v1
managed:
enabled: true
java_package_prefix: com.connectrpc.lite
plugins:
- plugin: connect-kotlin
out: build/generated/sources/bufgen
path: ./protoc-gen-connect-kotlin/build/install/protoc-gen-connect-kotlin/bin/protoc-gen-connect-kotlin
opt:
- generateCallbackMethods=true
- generateCoroutineMethods=true
- generateBlockingUnaryMethods=true
- plugin: java
out: build/generated/sources/bufgen
protoc_path: .tmp/bin/protoc
opt: lite
- plugin: kotlin
out: build/generated/sources/bufgen
protoc_path: .tmp/bin/protoc
opt: lite
17 changes: 17 additions & 0 deletions conformance/client/buf.gen.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
version: v1
managed:
enabled: true
plugins:
- plugin: connect-kotlin
out: google-java/build/generated/sources/bufgen
path: ./protoc-gen-connect-kotlin/build/install/protoc-gen-connect-kotlin/bin/protoc-gen-connect-kotlin
opt:
- generateCallbackMethods=true
- generateCoroutineMethods=true
- generateBlockingUnaryMethods=true
- plugin: java
out: google-java/build/generated/sources/bufgen
protoc_path: .tmp/bin/protoc
- plugin: kotlin
out: google-java/build/generated/sources/bufgen
protoc_path: .tmp/bin/protoc
30 changes: 30 additions & 0 deletions conformance/client/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
plugins {
kotlin("jvm")
}

// This base project contains generated code for the lite runtime
// and depends on the Google Protobuf Java Lite runtime.
// The main client logic is implemented in terms of generated
// code for that lite runtime.
//
// The non-lite runtime excludes the Google Protobuf Java Lite
// runtime and instead uses the full Java runtime. It then can
// adapt from the lite-runtime-generated code by serializing to
// bytes and then de-serializing into non-lite-generated types.

sourceSets {
main {
java {
srcDir("build/generated/sources/bufgen")
}
}
}

dependencies {
implementation(project(":okhttp"))
implementation(libs.kotlin.coroutines.core)
implementation(libs.protobuf.kotlin)
implementation(libs.protobuf.javalite)
implementation(libs.okio.core)
implementation(libs.okhttp.tls)
}
35 changes: 35 additions & 0 deletions conformance/client/google-java/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
plugins {
kotlin("jvm")
}

tasks {
compileKotlin {
kotlinOptions {
// Generated Kotlin code for protobufs uses OptIn annotation
freeCompilerArgs += "-opt-in=kotlin.RequiresOptIn"
}
}
}

// This project contains an alternate copy of the generated
// types, generated for the non-lite runtime.
sourceSets {
main {
java {
srcDir("build/generated/sources/bufgen")
}
}
}

dependencies {
implementation(project(":conformance:client")) {
exclude(group = "com.google.protobuf", module = "protobuf-javalite")
}
implementation(project(":extensions:google-java"))
implementation(project(":okhttp"))
implementation(libs.kotlin.coroutines.core)
implementation(libs.protobuf.kotlin)
implementation(libs.protobuf.java)
implementation(libs.okio.core)
implementation(libs.okhttp.tls)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright 2022-2023 The Connect Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.connectrpc.conformance.client.java

import com.connectrpc.Headers
import com.connectrpc.conformance.client.adapt.BidiStreamClient
import com.connectrpc.conformance.v1.BidiStreamRequest
import com.connectrpc.conformance.v1.BidiStreamResponse
import com.connectrpc.conformance.v1.ConformanceServiceClient

class JavaBidiStreamClient(
private val client: ConformanceServiceClient,
) : BidiStreamClient<BidiStreamRequest, BidiStreamResponse>(
BidiStreamRequest.getDefaultInstance(),
BidiStreamResponse.getDefaultInstance(),
) {
override suspend fun execute(headers: Headers): BidiStream<BidiStreamRequest, BidiStreamResponse> {
return BidiStream.new(client.bidiStream(headers))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright 2022-2023 The Connect Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.connectrpc.conformance.client.java

import com.connectrpc.Headers
import com.connectrpc.conformance.client.adapt.ClientStreamClient
import com.connectrpc.conformance.v1.ClientStreamRequest
import com.connectrpc.conformance.v1.ClientStreamResponse
import com.connectrpc.conformance.v1.ConformanceServiceClient

class JavaClientStreamClient(
private val client: ConformanceServiceClient,
) : ClientStreamClient<ClientStreamRequest, ClientStreamResponse>(
ClientStreamRequest.getDefaultInstance(),
ClientStreamResponse.getDefaultInstance(),
) {
override suspend fun execute(headers: Headers): ClientStream<ClientStreamRequest, ClientStreamResponse> {
return ClientStream.new(client.clientStream(headers))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Copyright 2022-2023 The Connect Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.connectrpc.conformance.client.java

import com.connectrpc.SerializationStrategy
import com.connectrpc.conformance.client.adapt.BidiStreamClient
import com.connectrpc.conformance.client.adapt.ClientStreamClient
import com.connectrpc.conformance.client.adapt.Invoker
import com.connectrpc.conformance.client.adapt.ServerStreamClient
import com.connectrpc.conformance.client.adapt.UnaryClient
import com.connectrpc.conformance.v1.ConformanceServiceClient
import com.connectrpc.extensions.GoogleJavaJSONStrategy
import com.connectrpc.extensions.GoogleJavaProtobufStrategy
import com.connectrpc.impl.ProtocolClient
import com.connectrpc.lite.connectrpc.conformance.v1.Codec

class JavaInvoker(
protocolClient: ProtocolClient,
) : Invoker {
private val client = ConformanceServiceClient(protocolClient)
override fun unaryClient(): UnaryClient<*, *> {
return JavaUnaryClient(client)
}

override fun unimplementedClient(): UnaryClient<*, *> {
return JavaUnimplementedClient(client)
}

override fun clientStreamClient(): ClientStreamClient<*, *> {
return JavaClientStreamClient(client)
}

override fun serverStreamClient(): ServerStreamClient<*, *> {
return JavaServerStreamClient(client)
}

override fun bidiStreamClient(): BidiStreamClient<*, *> {
return JavaBidiStreamClient(client)
}

companion object {
fun serializationStrategy(codec: Codec): SerializationStrategy {
return when (codec) {
Codec.CODEC_PROTO -> GoogleJavaProtobufStrategy()
Codec.CODEC_JSON -> GoogleJavaJSONStrategy()
else -> throw RuntimeException("unsupported codec $codec")
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright 2022-2023 The Connect Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.connectrpc.conformance.client.java

import com.connectrpc.Headers
import com.connectrpc.conformance.client.adapt.ResponseStream
import com.connectrpc.conformance.client.adapt.ServerStreamClient
import com.connectrpc.conformance.v1.ConformanceServiceClient
import com.connectrpc.conformance.v1.ServerStreamRequest
import com.connectrpc.conformance.v1.ServerStreamResponse

class JavaServerStreamClient(
private val client: ConformanceServiceClient,
) : ServerStreamClient<ServerStreamRequest, ServerStreamResponse>(
ServerStreamRequest.getDefaultInstance(),
ServerStreamResponse.getDefaultInstance(),
) {
override suspend fun execute(req: ServerStreamRequest, headers: Headers): ResponseStream<ServerStreamResponse> {
val stream = client.serverStream(headers)
stream.sendAndClose(req)
return ResponseStream.new(stream)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright 2022-2023 The Connect Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.connectrpc.conformance.client.java

import com.connectrpc.Headers
import com.connectrpc.ResponseMessage
import com.connectrpc.UnaryBlockingCall
import com.connectrpc.conformance.client.adapt.UnaryClient
import com.connectrpc.conformance.v1.ConformanceServiceClient
import com.connectrpc.conformance.v1.UnaryRequest
import com.connectrpc.conformance.v1.UnaryResponse
import com.connectrpc.http.Cancelable

class JavaUnaryClient(
private val client: ConformanceServiceClient,
) : UnaryClient<UnaryRequest, UnaryResponse>(
UnaryRequest.getDefaultInstance(),
UnaryResponse.getDefaultInstance(),
) {
override suspend fun execute(req: UnaryRequest, headers: Headers): ResponseMessage<UnaryResponse> {
return client.unary(req, headers)
}

override fun execute(
req: UnaryRequest,
headers: Headers,
onFinish: (ResponseMessage<UnaryResponse>) -> Unit,
): Cancelable {
return client.unary(req, headers, onFinish)
}

override fun blocking(req: UnaryRequest, headers: Headers): UnaryBlockingCall<UnaryResponse> {
return client.unaryBlocking(req, headers)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright 2022-2023 The Connect Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.connectrpc.conformance.client.java

import com.connectrpc.Headers
import com.connectrpc.ResponseMessage
import com.connectrpc.UnaryBlockingCall
import com.connectrpc.conformance.client.adapt.UnaryClient
import com.connectrpc.conformance.v1.ConformanceServiceClient
import com.connectrpc.conformance.v1.UnimplementedRequest
import com.connectrpc.conformance.v1.UnimplementedResponse
import com.connectrpc.http.Cancelable

class JavaUnimplementedClient(
private val client: ConformanceServiceClient,
) : UnaryClient<UnimplementedRequest, UnimplementedResponse>(
UnimplementedRequest.getDefaultInstance(),
UnimplementedResponse.getDefaultInstance(),
) {
override suspend fun execute(req: UnimplementedRequest, headers: Headers): ResponseMessage<UnimplementedResponse> {
return client.unimplemented(req, headers)
}

override fun execute(
req: UnimplementedRequest,
headers: Headers,
onFinish: (ResponseMessage<UnimplementedResponse>) -> Unit,
): Cancelable {
return client.unimplemented(req, headers, onFinish)
}

override fun blocking(req: UnimplementedRequest, headers: Headers): UnaryBlockingCall<UnimplementedResponse> {
return client.unimplementedBlocking(req, headers)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.connectrpc.conformance.client.java

import com.connectrpc.conformance.client.Client
import com.connectrpc.conformance.client.ConformanceClientLoop

fun main(args: Array<String>) {
try {
val invokeStyle = ConformanceClientLoop.parseArgs(args)
val client = Client(
invokerFactory = ::JavaInvoker,
serializationFactory = JavaInvoker::serializationStrategy,
invokeStyle = invokeStyle,
)
ConformanceClientLoop.run(System.`in`, System.out, client)
} catch (e: Exception) {
e.printStackTrace(System.err)
Runtime.getRuntime().exit(1)
}
}
Loading

0 comments on commit 9bd518f

Please sign in to comment.