Adven27 / grpc-wiremock

gRPC Mock Server
MIT License
93 stars 40 forks source link

Response<->Protobuf version matching based on protobuf package name /descriptor? #30

Closed butchmarshall closed 2 years ago

butchmarshall commented 2 years ago

Is your feature request related to a problem? Please describe.

We're versioning our protobuf files based on the package name, e.g.

package userquery.protobuf.grpc; package userquery.protobuf.grpc.v1_0_0; package userquery.protobuf.grpc.v1_1_0;

The server seems to register them properly:

grpc_wiremock | Registered services: grpc_wiremock | grpc.reflection.v1alpha.ServerReflection grpc_wiremock | userquery.protobuf.grpc.v1_0_1.UserQuery grpc_wiremock | userquery.protobuf.grpc.v1_0_0.UserQuery grpc_wiremock | userquery.protobuf.grpc.UserQuery

But so far I have not yet found a way to serve JSON responses based on matching the protobufs package.

Is it possible to match the appropriate packages response with a corresponding JSON template?

Describe the solution you'd like

A way to match the JSON response with the package descriptor

Describe alternatives you've considered

body matching, url patch matching, header matching

butchmarshall commented 2 years ago

Figured it out - modify HeaderPropagationInterceptor to forward the full method name descriptor, then do a match on the header value.

package io.adven.grpc.wiremock;

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 java.util.Map;

import static io.grpc.Metadata.ASCII_STRING_MARSHALLER;
import static io.grpc.Metadata.Key.of;
import static java.util.function.Function.identity;
import static java.util.stream.Collectors.toMap;

public class HeaderPropagationInterceptor implements ServerInterceptor {
    public static final Context.Key<Map<String, String>> HEADERS = Context.key("GRPC_WIREMOCK_HEADERS");

    @Override
    public <Req, Resp> ServerCall.Listener<Req> interceptCall(ServerCall<Req, Resp> call, final Metadata headers, ServerCallHandler<Req, Resp> next) {
        Map<String, String> header_map = asMap(headers);
        header_map.put("x-grpc-full-method-name",call.getMethodDescriptor().getFullMethodName());

        return Contexts.interceptCall(
            Context.current().withValue(HEADERS, header_map),
            call,
            headers,
            next
        );
    }

    private Map<String, String> asMap(Metadata headers) {
        return headers.keys().stream().collect(toMap(identity(), k -> headers.get(of(k, ASCII_STRING_MARSHALLER))));
    }
}
Adven27 commented 2 years ago

Propagating package info by header looks like a useful and backward-compatible solution. I think that I can pull these changes to the main branch. Thanks.