Skip to content
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

Add test coverage for grpc GlobalInterceptor method annotation #1531

Merged
merged 1 commit into from
Nov 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package io.quarkus.ts.http.advanced;

import static io.grpc.Metadata.ASCII_STRING_MARSHALLER;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.inject.Produces;

import io.grpc.CallOptions;
import io.grpc.Channel;
import io.grpc.ClientCall;
import io.grpc.ClientInterceptor;
import io.grpc.ForwardingClientCall;
import io.grpc.Metadata;
import io.grpc.MethodDescriptor;
import io.quarkus.grpc.GlobalInterceptor;

class GrpcClientInterceptors {
public static Metadata.Key<String> CLIENT_METHOD = Metadata.Key.of("client-method-target", ASCII_STRING_MARSHALLER);
public static Metadata.Key<String> CLIENT_CLASS = Metadata.Key.of("client-class-target", ASCII_STRING_MARSHALLER);

@GlobalInterceptor
@ApplicationScoped
static class ClassTarget extends Base {
}

static class MethodTarget extends Base {
}

static class Producer {
@GlobalInterceptor
@Produces
MethodTarget methodTarget() {
return new MethodTarget();
}
}

abstract static class Base implements ClientInterceptor {
@Override
public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(MethodDescriptor<ReqT, RespT> method, CallOptions options,
Channel next) {
String interceptedTarget = getClass().getName();
return new ForwardingClientCall.SimpleForwardingClientCall<ReqT, RespT>(next.newCall(method, options)) {
@Override
public void start(Listener<RespT> responseListener, Metadata headers) {
if (interceptedTarget.contains("MethodTarget")) {
headers.put(CLIENT_METHOD, interceptedTarget);
} else if (interceptedTarget.contains("ClassTarget")) {
headers.put(CLIENT_CLASS, interceptedTarget);
}
super.start(responseListener, headers);
}
};
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package io.quarkus.ts.http.advanced;

import io.grpc.stub.StreamObserver;
import io.quarkus.example.HelloReply;
import io.quarkus.example.InterceptedMessageGrpc;
import io.quarkus.example.InterceptedRequest;
import io.quarkus.grpc.GrpcService;

@GrpcService
public class GrpcInterceptorsService extends InterceptedMessageGrpc.InterceptedMessageImplBase {

@Override
public void showInterceptedMessage(InterceptedRequest request, StreamObserver<HelloReply> responseObserver) {
String serverMethod = GrpcServerInterceptors.SERVER_METHOD.get();
String serverClass = GrpcServerInterceptors.SERVER_CLASS.get();
HelloReply reply = HelloReply.newBuilder()
.setMessage("Intercepted client side method passed by server method interceptor is: " + serverMethod
+ "\nIntercepted client side class passed by server class interceptor is: " + serverClass)
.build();
responseObserver.onNext(reply);
responseObserver.onCompleted();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,13 @@
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;

import io.quarkus.example.GreeterGrpc;
import io.quarkus.example.HelloReply;
import io.quarkus.example.HelloRequest;
import io.quarkus.example.InterceptedMessageGrpc;
import io.quarkus.example.InterceptedRequest;
import io.quarkus.grpc.GrpcClient;

@Path("/grpc")
Expand All @@ -18,11 +22,22 @@ public class GrpcResource {
@GrpcClient("hello")
GreeterGrpc.GreeterBlockingStub client;

@Inject
@GrpcClient("hello")
InterceptedMessageGrpc.InterceptedMessageBlockingStub interceptorsClient;

@GET
@Path("/{name}")
@Produces(MediaType.TEXT_PLAIN)
public String hello(@PathParam("name") String name) {
return client.sayHello(HelloRequest.newBuilder().setName(name).build()).getMessage();
}

@GET
@Path("global/interceptors")
public Response globalInterceptors() {
HelloReply helloReply = interceptorsClient.showInterceptedMessage(InterceptedRequest.newBuilder().build());
return Response.ok(helloReply.getMessage()).build();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package io.quarkus.ts.http.advanced;

import static io.quarkus.ts.http.advanced.GrpcClientInterceptors.CLIENT_CLASS;
import static io.quarkus.ts.http.advanced.GrpcClientInterceptors.CLIENT_METHOD;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.inject.Produces;

import io.grpc.Context;
import io.grpc.Contexts;
import io.grpc.Metadata;
import io.grpc.ServerCall;
import io.grpc.ServerCallHandler;
import io.grpc.ServerInterceptor;
import io.quarkus.grpc.GlobalInterceptor;

class GrpcServerInterceptors {

public static Context.Key<String> SERVER_METHOD = Context.key("server-method-target");
public static Context.Key<String> SERVER_CLASS = Context.key("server-class-target");

@GlobalInterceptor
@ApplicationScoped
static class ClassTarget extends Base {
}

static class MethodTarget extends Base {
}

static class Producer {
@GlobalInterceptor
@Produces
MethodTarget methodTarget() {
return new MethodTarget();
}
}

abstract static class Base implements ServerInterceptor {
@Override
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> call, Metadata metadata,
ServerCallHandler<ReqT, RespT> next) {
Context ctx = null;
String interceptedTarget = getClass().getName();
// Determine where was grpc call intercepted and put client side intercepted data to Context.
if (interceptedTarget.contains("MethodTarget")) {
ctx = Context.current().withValue(SERVER_METHOD, metadata.get(CLIENT_METHOD));
ctx.attach();
} else if (interceptedTarget.contains("ClassTarget")) {
ctx = Context.current().withValue(SERVER_CLASS, metadata.get(CLIENT_CLASS));
ctx.attach();
} else {
throw new RuntimeException("Unexpected intercepted class or method by GlobalInterceptor.");
}
return Contexts.interceptCall(ctx, call, metadata, next);
}
}
}
10 changes: 9 additions & 1 deletion http/http-advanced/src/main/proto/helloworld.proto
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// Intercepted message service
service InterceptedMessage {
rpc ShowInterceptedMessage (InterceptedRequest) returns (HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
string name = 1;
Expand All @@ -27,4 +32,7 @@ message HelloRequest {
// The response message containing the greetings
message HelloReply {
string message = 1;
}
}

message InterceptedRequest {
}
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,15 @@ public void testGrpc() {
getApp().given().when().get("/api/grpc/trinity").then().statusCode(HttpStatus.SC_OK).body(is("Hello trinity"));
}

@Test
@Tag("QUARKUS-3742")
@DisplayName("GRPC client and server global interceptors test")
public void testGrpcGlobalInterceptors() {
getApp().given().when().get("/api/grpc/global/interceptors").then().statusCode(HttpStatus.SC_OK)
.body(containsString("ClientInterceptors$ClassTarget"),
containsString("ClientInterceptors$MethodTarget"));
}

@Test
@DisplayName("Http/2 Server test")
public void http2Server() throws InterruptedException, URISyntaxException {
Expand Down