From 88bd480e15f57152e1ffedf0703c321fdd88a791 Mon Sep 17 00:00:00 2001 From: Kyle Hansen Date: Wed, 28 Dec 2016 11:43:55 -0700 Subject: [PATCH 1/6] Added support for httpRule definitions in YAML files --- build.gradle | 2 + .../src/test/proto/http_api_config.yaml | 4 + .../com/fullcontact/rpc/jersey/util/Rule.java | 115 ++++++++++++++++++ .../rpc/jersey/util/YamlConfig.java | 14 +++ .../fullcontact/rpc/jersey/CodeGenerator.java | 34 +++++- .../src/main/resources/http_api_config.yml | 9 ++ protos/test.proto | 6 + 7 files changed, 180 insertions(+), 4 deletions(-) create mode 100644 integration-test-proxy/src/test/proto/http_api_config.yaml create mode 100644 jersey-rpc-support/src/main/java/com/fullcontact/rpc/jersey/util/Rule.java create mode 100644 jersey-rpc-support/src/main/java/com/fullcontact/rpc/jersey/util/YamlConfig.java create mode 100644 protoc-gen-jersey/src/main/resources/http_api_config.yml diff --git a/build.gradle b/build.gradle index 66aa227..89ac23d 100644 --- a/build.gradle +++ b/build.gradle @@ -130,6 +130,8 @@ project(":protoc-gen-jersey") { compile "io.grpc:grpc-protobuf:${grpcVersion}" compile "io.grpc:grpc-stub:${grpcVersion}" compile "com.github.spullara.mustache.java:compiler:0.9.4" + compile "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.8.5" + compile "com.fasterxml.jackson.core:jackson-databind:2.8.5" testCompile 'org.glassfish.jersey.core:jersey-common:2.24.1' } diff --git a/integration-test-proxy/src/test/proto/http_api_config.yaml b/integration-test-proxy/src/test/proto/http_api_config.yaml new file mode 100644 index 0000000..3c3e010 --- /dev/null +++ b/integration-test-proxy/src/test/proto/http_api_config.yaml @@ -0,0 +1,4 @@ +http: + rules: + - selector: endpoints.examples.bookstore.BookStore.ListShelves + get: '/v1/shelves' \ No newline at end of file diff --git a/jersey-rpc-support/src/main/java/com/fullcontact/rpc/jersey/util/Rule.java b/jersey-rpc-support/src/main/java/com/fullcontact/rpc/jersey/util/Rule.java new file mode 100644 index 0000000..61ef467 --- /dev/null +++ b/jersey-rpc-support/src/main/java/com/fullcontact/rpc/jersey/util/Rule.java @@ -0,0 +1,115 @@ +package com.fullcontact.rpc.jersey.util; + +import com.google.api.HttpRule; + +import java.util.List; +import java.util.stream.Collectors; + +public class Rule { + private String selector; + private String get; + + public String getSelector() { + return selector; + } + + public void setSelector(String selector) { + this.selector = selector; + } + + public String getGet() { + return get; + } + + public void setGet(String get) { + this.get = get; + } + + public String getPost() { + return post; + } + + public void setPost(String post) { + this.post = post; + } + + public String getPut() { + return put; + } + + public void setPut(String put) { + this.put = put; + } + + public String getDelete() { + return delete; + } + + public void setDelete(String delete) { + this.delete = delete; + } + + public String getBody() { + return body; + } + + public void setBody(String body) { + this.body = body; + } + + public List getAdditionalBindings() { + return additionalBindings; + } + + public void setAdditionalBindings(List additionalBindings) { + this.additionalBindings = additionalBindings; + } + + private String post; + private String put; + private String delete; + private String body; + private List additionalBindings; + + + public HttpRule buildHttpRule(){ + HttpRule.Builder builder = HttpRule.newBuilder(); + if(get != null){ + builder.setGet(get); + } + if(put != null){ + builder.setPut(put); + } + if(delete != null){ + builder.setDelete(delete); + } + if(post != null){ + builder.setPost(post); + } + if(body != null){ + builder.setBody(body); + } + if(additionalBindings != null){ + builder.addAllAdditionalBindings(additionalBindings.stream().map(Rule::buildHttpRule).collect(Collectors.toList())); + } + + return builder.build(); + + } + + @Override + public String toString() { + return "Rule{" + + "selector='" + selector + '\'' + + ", get='" + get + '\'' + + ", post='" + post + '\'' + + ", put='" + put + '\'' + + ", delete='" + delete + '\'' + + ", body='" + body + '\'' + + ", additionalBindings=" + additionalBindings + + '}'; + } + + + +} diff --git a/jersey-rpc-support/src/main/java/com/fullcontact/rpc/jersey/util/YamlConfig.java b/jersey-rpc-support/src/main/java/com/fullcontact/rpc/jersey/util/YamlConfig.java new file mode 100644 index 0000000..a45c2cc --- /dev/null +++ b/jersey-rpc-support/src/main/java/com/fullcontact/rpc/jersey/util/YamlConfig.java @@ -0,0 +1,14 @@ +package com.fullcontact.rpc.jersey.util; + +import java.util.List; +import java.util.Map; + +/** + * Created by kylehansen on 12/28/16. + */ +public class YamlConfig { + public Map> http; + public List getRules(){ + return http.get("rules"); + } +} diff --git a/protoc-gen-jersey/src/main/java/com/fullcontact/rpc/jersey/CodeGenerator.java b/protoc-gen-jersey/src/main/java/com/fullcontact/rpc/jersey/CodeGenerator.java index 19bcb2d..d9d18eb 100644 --- a/protoc-gen-jersey/src/main/java/com/fullcontact/rpc/jersey/CodeGenerator.java +++ b/protoc-gen-jersey/src/main/java/com/fullcontact/rpc/jersey/CodeGenerator.java @@ -1,7 +1,12 @@ package com.fullcontact.rpc.jersey; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import com.fullcontact.rpc.jersey.util.ProtobufDescriptorJavaUtil; +import com.fullcontact.rpc.jersey.util.Rule; +import com.fullcontact.rpc.jersey.util.YamlConfig; import com.github.mustachejava.DefaultMustacheFactory; import com.github.mustachejava.Mustache; import com.github.mustachejava.MustacheFactory; @@ -17,13 +22,13 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Sets; -import com.google.protobuf.DescriptorProtos; -import com.google.protobuf.Descriptors; +import com.google.protobuf.*; import com.google.protobuf.compiler.PluginProtos; import lombok.Builder; import lombok.Value; -import java.io.StringWriter; +import java.io.*; +import java.text.ParseException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -56,6 +61,18 @@ public PluginProtos.CodeGeneratorResponse generate(PluginProtos.CodeGeneratorReq AnnotationsProto.getDescriptor(), HttpRule.getDescriptor().getFile() ); + + YamlConfig yamlConfig = null; + if(this.getClass().getResourceAsStream("/http_api_config.yml") != null) { //TODO: read in the YAML correctly + InputStream yamlStream = this.getClass().getResourceAsStream("/http_api_config.yml"); + ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); + try { + yamlConfig = mapper.readValue(yamlStream, YamlConfig.class); + } catch (IOException e) { + throw new RuntimeException("Failed to parse YAML", e); + } + } + for(DescriptorProtos.FileDescriptorProto fdProto : request.getProtoFileList()) { // Descriptors are provided in dependency-topological order // each time we collect a new FileDescriptor, we add it to a @@ -85,6 +102,16 @@ public PluginProtos.CodeGeneratorResponse generate(PluginProtos.CodeGeneratorReq for(Descriptors.ServiceDescriptor serviceDescriptor : fd.getServices()) { DescriptorProtos.ServiceDescriptorProto serviceDescriptorProto = serviceDescriptor.toProto(); for(DescriptorProtos.MethodDescriptorProto methodProto : serviceDescriptorProto.getMethodList()) { + String fullMethodName = serviceDescriptor.getFullName() +"." + methodProto.getName(); + if(yamlConfig != null){ //Check to see if the rules are defined in the YAML + for(Rule rule :yamlConfig.getRules()){ + if(rule.getSelector().equals(fullMethodName) || rule.getSelector().equals("*")){ //TODO: com.foo.* + //YAML http rules override proto files. - https://cloud.google.com/endpoints/docs/grpc-service-config + DescriptorProtos.MethodOptions yamlOptions = DescriptorProtos.MethodOptions.newBuilder().setExtension(AnnotationsProto.http, rule.buildHttpRule()).build(); + methodProto = DescriptorProtos.MethodDescriptorProto.newBuilder().mergeFrom(methodProto).setOptions(yamlOptions).build(); + } + } + } if(methodProto.getOptions().hasExtension(AnnotationsProto.http)) { // TODO(xorlev): support server streaming if(methodProto.getServerStreaming() || methodProto.getClientStreaming()) @@ -94,7 +121,6 @@ public PluginProtos.CodeGeneratorResponse generate(PluginProtos.CodeGeneratorReq } } } - if(!methodsToGenerate.isEmpty()) generateResource(response, lookup, fdProto, methodsToGenerate, isProxy); } diff --git a/protoc-gen-jersey/src/main/resources/http_api_config.yml b/protoc-gen-jersey/src/main/resources/http_api_config.yml new file mode 100644 index 0000000..0007cb2 --- /dev/null +++ b/protoc-gen-jersey/src/main/resources/http_api_config.yml @@ -0,0 +1,9 @@ +http: + rules: + - selector: TestService.TestMethod4 + get: /yaml_user/{uint3} + additionalBindings: + - post: /yaml_users_nested + body: '*' # Star can't be parsed. + - selector: TestService.TestMethod5 + get: /yaml_users/{s=hello/**}/x/{uint3}/{nt.f1}/*/**/test diff --git a/protos/test.proto b/protos/test.proto index cc76b71..afafc26 100644 --- a/protos/test.proto +++ b/protos/test.proto @@ -21,6 +21,12 @@ service TestService { rpc TestMethod3 (TestRequest) returns (TestResponse) { option (google.api.http).get = "/users/{s=hello/**}/x/{uint3}/{nt.f1}/*/**/test"; } + rpc TestMethod4 (TestRequest) returns (TestResponse) { + //Defined in Yaml + } + rpc TestMethod5 (TestRequest) returns (TestResponse) { + //Defined in Yaml + } } enum TestEnum { FIRST = 0; From 2dc27a6c06ff7a003f117446c1349f75e7fe31ba Mon Sep 17 00:00:00 2001 From: Kyle Hansen Date: Wed, 28 Dec 2016 11:52:06 -0700 Subject: [PATCH 2/6] code cleanup --- .../rpc/jersey/util/YamlConfig.java | 14 ----------- .../rpc/jersey/util/YamlHttpConfig.java | 14 +++++++++++ .../util/{Rule.java => YamlHttpRule.java} | 24 ++++++++++--------- .../fullcontact/rpc/jersey/CodeGenerator.java | 14 ++++------- 4 files changed, 32 insertions(+), 34 deletions(-) delete mode 100644 jersey-rpc-support/src/main/java/com/fullcontact/rpc/jersey/util/YamlConfig.java create mode 100644 jersey-rpc-support/src/main/java/com/fullcontact/rpc/jersey/util/YamlHttpConfig.java rename jersey-rpc-support/src/main/java/com/fullcontact/rpc/jersey/util/{Rule.java => YamlHttpRule.java} (85%) diff --git a/jersey-rpc-support/src/main/java/com/fullcontact/rpc/jersey/util/YamlConfig.java b/jersey-rpc-support/src/main/java/com/fullcontact/rpc/jersey/util/YamlConfig.java deleted file mode 100644 index a45c2cc..0000000 --- a/jersey-rpc-support/src/main/java/com/fullcontact/rpc/jersey/util/YamlConfig.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.fullcontact.rpc.jersey.util; - -import java.util.List; -import java.util.Map; - -/** - * Created by kylehansen on 12/28/16. - */ -public class YamlConfig { - public Map> http; - public List getRules(){ - return http.get("rules"); - } -} diff --git a/jersey-rpc-support/src/main/java/com/fullcontact/rpc/jersey/util/YamlHttpConfig.java b/jersey-rpc-support/src/main/java/com/fullcontact/rpc/jersey/util/YamlHttpConfig.java new file mode 100644 index 0000000..9e2acbf --- /dev/null +++ b/jersey-rpc-support/src/main/java/com/fullcontact/rpc/jersey/util/YamlHttpConfig.java @@ -0,0 +1,14 @@ +package com.fullcontact.rpc.jersey.util; + +import java.util.List; +import java.util.Map; + +/** + * Created by kylehansen @Sypticus on 12/28/16. + */ +public class YamlHttpConfig { + public Map> http; + public List getRules(){ + return http.get("rules"); + } +} diff --git a/jersey-rpc-support/src/main/java/com/fullcontact/rpc/jersey/util/Rule.java b/jersey-rpc-support/src/main/java/com/fullcontact/rpc/jersey/util/YamlHttpRule.java similarity index 85% rename from jersey-rpc-support/src/main/java/com/fullcontact/rpc/jersey/util/Rule.java rename to jersey-rpc-support/src/main/java/com/fullcontact/rpc/jersey/util/YamlHttpRule.java index 61ef467..c4e0303 100644 --- a/jersey-rpc-support/src/main/java/com/fullcontact/rpc/jersey/util/Rule.java +++ b/jersey-rpc-support/src/main/java/com/fullcontact/rpc/jersey/util/YamlHttpRule.java @@ -4,10 +4,18 @@ import java.util.List; import java.util.stream.Collectors; - -public class Rule { +/** + * Created by kylehansen @Sypticus on 12/28/16. + */ +public class YamlHttpRule { private String selector; private String get; + private String post; + private String put; + private String delete; + private String body; + private List additionalBindings; + public String getSelector() { return selector; @@ -57,20 +65,14 @@ public void setBody(String body) { this.body = body; } - public List getAdditionalBindings() { + public List getAdditionalBindings() { return additionalBindings; } - public void setAdditionalBindings(List additionalBindings) { + public void setAdditionalBindings(List additionalBindings) { this.additionalBindings = additionalBindings; } - private String post; - private String put; - private String delete; - private String body; - private List additionalBindings; - public HttpRule buildHttpRule(){ HttpRule.Builder builder = HttpRule.newBuilder(); @@ -90,7 +92,7 @@ public HttpRule buildHttpRule(){ builder.setBody(body); } if(additionalBindings != null){ - builder.addAllAdditionalBindings(additionalBindings.stream().map(Rule::buildHttpRule).collect(Collectors.toList())); + builder.addAllAdditionalBindings(additionalBindings.stream().map(YamlHttpRule::buildHttpRule).collect(Collectors.toList())); } return builder.build(); diff --git a/protoc-gen-jersey/src/main/java/com/fullcontact/rpc/jersey/CodeGenerator.java b/protoc-gen-jersey/src/main/java/com/fullcontact/rpc/jersey/CodeGenerator.java index d9d18eb..2f3b5ba 100644 --- a/protoc-gen-jersey/src/main/java/com/fullcontact/rpc/jersey/CodeGenerator.java +++ b/protoc-gen-jersey/src/main/java/com/fullcontact/rpc/jersey/CodeGenerator.java @@ -1,12 +1,11 @@ package com.fullcontact.rpc.jersey; -import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import com.fullcontact.rpc.jersey.util.ProtobufDescriptorJavaUtil; -import com.fullcontact.rpc.jersey.util.Rule; -import com.fullcontact.rpc.jersey.util.YamlConfig; +import com.fullcontact.rpc.jersey.util.YamlHttpConfig; +import com.fullcontact.rpc.jersey.util.YamlHttpRule; import com.github.mustachejava.DefaultMustacheFactory; import com.github.mustachejava.Mustache; import com.github.mustachejava.MustacheFactory; @@ -28,14 +27,11 @@ import lombok.Value; import java.io.*; -import java.text.ParseException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import java.util.stream.Collectors; /** @@ -62,12 +58,12 @@ public PluginProtos.CodeGeneratorResponse generate(PluginProtos.CodeGeneratorReq HttpRule.getDescriptor().getFile() ); - YamlConfig yamlConfig = null; + YamlHttpConfig yamlConfig = null; if(this.getClass().getResourceAsStream("/http_api_config.yml") != null) { //TODO: read in the YAML correctly InputStream yamlStream = this.getClass().getResourceAsStream("/http_api_config.yml"); ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); try { - yamlConfig = mapper.readValue(yamlStream, YamlConfig.class); + yamlConfig = mapper.readValue(yamlStream, YamlHttpConfig.class); } catch (IOException e) { throw new RuntimeException("Failed to parse YAML", e); } @@ -104,7 +100,7 @@ public PluginProtos.CodeGeneratorResponse generate(PluginProtos.CodeGeneratorReq for(DescriptorProtos.MethodDescriptorProto methodProto : serviceDescriptorProto.getMethodList()) { String fullMethodName = serviceDescriptor.getFullName() +"." + methodProto.getName(); if(yamlConfig != null){ //Check to see if the rules are defined in the YAML - for(Rule rule :yamlConfig.getRules()){ + for(YamlHttpRule rule :yamlConfig.getRules()){ if(rule.getSelector().equals(fullMethodName) || rule.getSelector().equals("*")){ //TODO: com.foo.* //YAML http rules override proto files. - https://cloud.google.com/endpoints/docs/grpc-service-config DescriptorProtos.MethodOptions yamlOptions = DescriptorProtos.MethodOptions.newBuilder().setExtension(AnnotationsProto.http, rule.buildHttpRule()).build(); From c1e346ed2af29ba0533988fcfda90e711bce7803 Mon Sep 17 00:00:00 2001 From: Kyle Hansen Date: Wed, 4 Jan 2017 15:29:12 -0700 Subject: [PATCH 3/6] Yaml path can now be passed in. Added tests around YAML functionality --- build.gradle | 6 +- .../rpc/jersey/EchoTestService.java | 18 +++ .../rpc/jersey/IntegrationBase.java | 82 ++++++++++++ .../src/test/proto}/http_api_config.yml | 11 +- .../src/test/proto/http_api_config.yaml | 4 - .../rpc/jersey/util/YamlHttpConfig.java | 14 --- .../rpc/jersey/util/YamlHttpRule.java | 117 ------------------ .../fullcontact/rpc/jersey/CodeGenerator.java | 27 +--- .../rpc/jersey/yaml/YamlHttpConfig.java | 43 +++++++ .../rpc/jersey/yaml/YamlHttpRule.java | 47 +++++++ protos/test.proto | 5 + 11 files changed, 212 insertions(+), 162 deletions(-) rename {protoc-gen-jersey/src/main/resources => integration-test-base/src/test/proto}/http_api_config.yml (51%) delete mode 100644 integration-test-proxy/src/test/proto/http_api_config.yaml delete mode 100644 jersey-rpc-support/src/main/java/com/fullcontact/rpc/jersey/util/YamlHttpConfig.java delete mode 100644 jersey-rpc-support/src/main/java/com/fullcontact/rpc/jersey/util/YamlHttpRule.java create mode 100644 protoc-gen-jersey/src/main/java/com/fullcontact/rpc/jersey/yaml/YamlHttpConfig.java create mode 100644 protoc-gen-jersey/src/main/java/com/fullcontact/rpc/jersey/yaml/YamlHttpRule.java diff --git a/build.gradle b/build.gradle index 89ac23d..3a8e10d 100644 --- a/build.gradle +++ b/build.gradle @@ -295,7 +295,9 @@ project(":integration-test-serverstub") { generateProtoTasks { all()*.plugins { grpc {} - jersey {} + jersey { + option 'yaml=integration-test-base/src/test/proto/http_api_config.yml' + } } } } @@ -345,7 +347,7 @@ project(":integration-test-proxy") { all()*.plugins { grpc {} jersey { - option 'proxy' + option 'proxy,yaml=/Users/kylehansen/dev/opensource/grpc-jersey/integration-test-base/src/test/proto/http_api_config.yml' } } } diff --git a/integration-test-base/src/test/java/com/fullcontact/rpc/jersey/EchoTestService.java b/integration-test-base/src/test/java/com/fullcontact/rpc/jersey/EchoTestService.java index 6f0ca6e..a773915 100644 --- a/integration-test-base/src/test/java/com/fullcontact/rpc/jersey/EchoTestService.java +++ b/integration-test-base/src/test/java/com/fullcontact/rpc/jersey/EchoTestService.java @@ -29,4 +29,22 @@ public void testMethod3(TestRequest request, StreamObserver respon responseObserver.onNext(TestResponse.newBuilder().setRequest(request).build()); responseObserver.onCompleted(); } + + @Override + public void testMethod4(TestRequest request, StreamObserver responseObserver) { + responseObserver.onNext(TestResponse.newBuilder().setRequest(request).build()); + responseObserver.onCompleted(); + } + + @Override + public void testMethod5(TestRequest request, StreamObserver responseObserver) { + responseObserver.onNext(TestResponse.newBuilder().setRequest(request).build()); + responseObserver.onCompleted(); + } + + @Override + public void testMethod6(TestRequest request, StreamObserver responseObserver) { + responseObserver.onNext(TestResponse.newBuilder().setRequest(request).build()); + responseObserver.onCompleted(); + } } diff --git a/integration-test-base/src/test/java/com/fullcontact/rpc/jersey/IntegrationBase.java b/integration-test-base/src/test/java/com/fullcontact/rpc/jersey/IntegrationBase.java index fb8554e..5cb00c0 100644 --- a/integration-test-base/src/test/java/com/fullcontact/rpc/jersey/IntegrationBase.java +++ b/integration-test-base/src/test/java/com/fullcontact/rpc/jersey/IntegrationBase.java @@ -102,4 +102,86 @@ public void testAdvancedGet() throws Exception { assertThat(response.getRequest().getEnu()).isEqualTo(TestEnum.SECOND); assertThat(response.getRequest().getNt().getF1()).isEqualTo("abcd"); } + + @Test + public void testAdvancedGetFromYaml() throws Exception { + // /yaml_users/{s=hello/**}/x/{uint3}/{nt.f1}/*/**/test + String responseJson = resources().getJerseyTest() + .target("/yaml_users/hello/string1/test/x/1234/abcd/foo/bar/baz/test") + .queryParam("d", 1234.5678) + .queryParam("enu", "SECOND") + .queryParam("uint3", "5678") // ensure path param has precedence + .queryParam("x", "y") + .request() + .buildGet() + .invoke(String.class); + + TestResponse.Builder responseFromJson = TestResponse.newBuilder(); + JsonFormat.parser().merge(responseJson, responseFromJson); + TestResponse response = responseFromJson.build(); + + assertThat(response.getRequest().getS()).isEqualTo("hello/string1/test"); + assertThat(response.getRequest().getUint3()).isEqualTo(1234); + assertThat(response.getRequest().getD()).isEqualTo(1234.5678); + assertThat(response.getRequest().getEnu()).isEqualTo(TestEnum.SECOND); + assertThat(response.getRequest().getNt().getF1()).isEqualTo("abcd"); + } + + @Test + public void testBasicGetFromYaml() throws Exception { + // /yaml_users/{s}/{uint3}/{nt.f1} + String responseJson = resources().getJerseyTest() + .target("/yaml_users/string1/1234/abcd") + .request() + .buildGet() + .invoke(String.class); + + TestResponse.Builder responseFromJson = TestResponse.newBuilder(); + JsonFormat.parser().merge(responseJson, responseFromJson); + TestResponse response = responseFromJson.build(); + + assertThat(response.getRequest().getS()).isEqualTo("string1"); + assertThat(response.getRequest().getUint3()).isEqualTo(1234); + assertThat(response.getRequest().getNt().getF1()).isEqualTo("abcd"); + assertThat(false); + } + + @Test + public void testBasicPostYaml() throws Exception { + TestRequest request = TestRequest.newBuilder() + .setBoolean(true) + .setS("Hello") + .setNt(NestedType.newBuilder().setF1("World")) + .build(); + String responseJson = resources().getJerseyTest() + .target("/yaml_users/") + .request() + .buildPost(Entity.entity(JsonFormat.printer().print(request), "application/json; charset=utf-8")) + .invoke(String.class); + + TestResponse.Builder responseFromJson = TestResponse.newBuilder(); + JsonFormat.parser().merge(responseJson, responseFromJson); + TestResponse response = responseFromJson.build(); + + assertThat(response.getRequest()).isEqualTo(request); + } + + @Test + public void testPost__nestedBindingYaml() throws Exception { + NestedType request = NestedType.newBuilder().setF1("World").build(); + String responseJson = resources().getJerseyTest() + .target("/yaml_users_nested/") + .request() + .buildPost(Entity.entity(JsonFormat.printer().print(request), + "application/json; charset=utf-8")) + .invoke(String.class); + + TestResponse.Builder responseFromJson = TestResponse.newBuilder(); + JsonFormat.parser().merge(responseJson, responseFromJson); + TestResponse response = responseFromJson.build(); + + assertThat(response.getRequest().getNt()).isEqualTo(request); + } + + } diff --git a/protoc-gen-jersey/src/main/resources/http_api_config.yml b/integration-test-base/src/test/proto/http_api_config.yml similarity index 51% rename from protoc-gen-jersey/src/main/resources/http_api_config.yml rename to integration-test-base/src/test/proto/http_api_config.yml index 0007cb2..d6d9151 100644 --- a/protoc-gen-jersey/src/main/resources/http_api_config.yml +++ b/integration-test-base/src/test/proto/http_api_config.yml @@ -1,9 +1,12 @@ http: rules: - selector: TestService.TestMethod4 - get: /yaml_user/{uint3} - additionalBindings: - - post: /yaml_users_nested - body: '*' # Star can't be parsed. + get: /yaml_users/{s}/{uint3}/{nt.f1} - selector: TestService.TestMethod5 get: /yaml_users/{s=hello/**}/x/{uint3}/{nt.f1}/*/**/test + - selector: TestService.TestMethod6 + post: /yaml_users/ + body: "*" + additionalBindings: + - post: /yaml_users_nested + body: "nt" \ No newline at end of file diff --git a/integration-test-proxy/src/test/proto/http_api_config.yaml b/integration-test-proxy/src/test/proto/http_api_config.yaml deleted file mode 100644 index 3c3e010..0000000 --- a/integration-test-proxy/src/test/proto/http_api_config.yaml +++ /dev/null @@ -1,4 +0,0 @@ -http: - rules: - - selector: endpoints.examples.bookstore.BookStore.ListShelves - get: '/v1/shelves' \ No newline at end of file diff --git a/jersey-rpc-support/src/main/java/com/fullcontact/rpc/jersey/util/YamlHttpConfig.java b/jersey-rpc-support/src/main/java/com/fullcontact/rpc/jersey/util/YamlHttpConfig.java deleted file mode 100644 index 9e2acbf..0000000 --- a/jersey-rpc-support/src/main/java/com/fullcontact/rpc/jersey/util/YamlHttpConfig.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.fullcontact.rpc.jersey.util; - -import java.util.List; -import java.util.Map; - -/** - * Created by kylehansen @Sypticus on 12/28/16. - */ -public class YamlHttpConfig { - public Map> http; - public List getRules(){ - return http.get("rules"); - } -} diff --git a/jersey-rpc-support/src/main/java/com/fullcontact/rpc/jersey/util/YamlHttpRule.java b/jersey-rpc-support/src/main/java/com/fullcontact/rpc/jersey/util/YamlHttpRule.java deleted file mode 100644 index c4e0303..0000000 --- a/jersey-rpc-support/src/main/java/com/fullcontact/rpc/jersey/util/YamlHttpRule.java +++ /dev/null @@ -1,117 +0,0 @@ -package com.fullcontact.rpc.jersey.util; - -import com.google.api.HttpRule; - -import java.util.List; -import java.util.stream.Collectors; -/** - * Created by kylehansen @Sypticus on 12/28/16. - */ -public class YamlHttpRule { - private String selector; - private String get; - private String post; - private String put; - private String delete; - private String body; - private List additionalBindings; - - - public String getSelector() { - return selector; - } - - public void setSelector(String selector) { - this.selector = selector; - } - - public String getGet() { - return get; - } - - public void setGet(String get) { - this.get = get; - } - - public String getPost() { - return post; - } - - public void setPost(String post) { - this.post = post; - } - - public String getPut() { - return put; - } - - public void setPut(String put) { - this.put = put; - } - - public String getDelete() { - return delete; - } - - public void setDelete(String delete) { - this.delete = delete; - } - - public String getBody() { - return body; - } - - public void setBody(String body) { - this.body = body; - } - - public List getAdditionalBindings() { - return additionalBindings; - } - - public void setAdditionalBindings(List additionalBindings) { - this.additionalBindings = additionalBindings; - } - - - public HttpRule buildHttpRule(){ - HttpRule.Builder builder = HttpRule.newBuilder(); - if(get != null){ - builder.setGet(get); - } - if(put != null){ - builder.setPut(put); - } - if(delete != null){ - builder.setDelete(delete); - } - if(post != null){ - builder.setPost(post); - } - if(body != null){ - builder.setBody(body); - } - if(additionalBindings != null){ - builder.addAllAdditionalBindings(additionalBindings.stream().map(YamlHttpRule::buildHttpRule).collect(Collectors.toList())); - } - - return builder.build(); - - } - - @Override - public String toString() { - return "Rule{" + - "selector='" + selector + '\'' + - ", get='" + get + '\'' + - ", post='" + post + '\'' + - ", put='" + put + '\'' + - ", delete='" + delete + '\'' + - ", body='" + body + '\'' + - ", additionalBindings=" + additionalBindings + - '}'; - } - - - -} diff --git a/protoc-gen-jersey/src/main/java/com/fullcontact/rpc/jersey/CodeGenerator.java b/protoc-gen-jersey/src/main/java/com/fullcontact/rpc/jersey/CodeGenerator.java index 2f3b5ba..8920e51 100644 --- a/protoc-gen-jersey/src/main/java/com/fullcontact/rpc/jersey/CodeGenerator.java +++ b/protoc-gen-jersey/src/main/java/com/fullcontact/rpc/jersey/CodeGenerator.java @@ -1,11 +1,9 @@ package com.fullcontact.rpc.jersey; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import com.fullcontact.rpc.jersey.util.ProtobufDescriptorJavaUtil; -import com.fullcontact.rpc.jersey.util.YamlHttpConfig; -import com.fullcontact.rpc.jersey.util.YamlHttpRule; +import com.fullcontact.rpc.jersey.yaml.YamlHttpConfig; +import com.fullcontact.rpc.jersey.yaml.YamlHttpRule; import com.github.mustachejava.DefaultMustacheFactory; import com.github.mustachejava.Mustache; import com.github.mustachejava.MustacheFactory; @@ -27,11 +25,7 @@ import lombok.Value; import java.io.*; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; /** @@ -58,16 +52,7 @@ public PluginProtos.CodeGeneratorResponse generate(PluginProtos.CodeGeneratorReq HttpRule.getDescriptor().getFile() ); - YamlHttpConfig yamlConfig = null; - if(this.getClass().getResourceAsStream("/http_api_config.yml") != null) { //TODO: read in the YAML correctly - InputStream yamlStream = this.getClass().getResourceAsStream("/http_api_config.yml"); - ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); - try { - yamlConfig = mapper.readValue(yamlStream, YamlHttpConfig.class); - } catch (IOException e) { - throw new RuntimeException("Failed to parse YAML", e); - } - } + Optional yamlConfig = YamlHttpConfig.getFromOptions(options); for(DescriptorProtos.FileDescriptorProto fdProto : request.getProtoFileList()) { // Descriptors are provided in dependency-topological order @@ -99,8 +84,8 @@ public PluginProtos.CodeGeneratorResponse generate(PluginProtos.CodeGeneratorReq DescriptorProtos.ServiceDescriptorProto serviceDescriptorProto = serviceDescriptor.toProto(); for(DescriptorProtos.MethodDescriptorProto methodProto : serviceDescriptorProto.getMethodList()) { String fullMethodName = serviceDescriptor.getFullName() +"." + methodProto.getName(); - if(yamlConfig != null){ //Check to see if the rules are defined in the YAML - for(YamlHttpRule rule :yamlConfig.getRules()){ + if(yamlConfig.isPresent()){ //Check to see if the rules are defined in the YAML + for(YamlHttpRule rule :yamlConfig.get().getRules()){ if(rule.getSelector().equals(fullMethodName) || rule.getSelector().equals("*")){ //TODO: com.foo.* //YAML http rules override proto files. - https://cloud.google.com/endpoints/docs/grpc-service-config DescriptorProtos.MethodOptions yamlOptions = DescriptorProtos.MethodOptions.newBuilder().setExtension(AnnotationsProto.http, rule.buildHttpRule()).build(); diff --git a/protoc-gen-jersey/src/main/java/com/fullcontact/rpc/jersey/yaml/YamlHttpConfig.java b/protoc-gen-jersey/src/main/java/com/fullcontact/rpc/jersey/yaml/YamlHttpConfig.java new file mode 100644 index 0000000..c761092 --- /dev/null +++ b/protoc-gen-jersey/src/main/java/com/fullcontact/rpc/jersey/yaml/YamlHttpConfig.java @@ -0,0 +1,43 @@ +package com.fullcontact.rpc.jersey.yaml; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +/** + * Created by kylehansen @Sypticus on 12/28/16. + */ +public class YamlHttpConfig { + public Map> http; + public List getRules(){ + return http.get("rules"); + } + + public static Optional getFromOptions(Set options){ + Optional yamlOption = options.stream().filter(option -> option.startsWith("yaml=")).findFirst(); + if(yamlOption.isPresent()) { + String yamlPath = yamlOption.get().split("=")[1]; + try { + File yamlFile = new File(yamlPath); + if(!yamlFile.exists()){ + throw new RuntimeException("YAMLs file does not exist: "+ yamlFile.getAbsolutePath()); + } + InputStream yamlStream = new FileInputStream(yamlFile); + + ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); + return Optional.of(mapper.readValue(yamlStream, YamlHttpConfig.class)); + } catch (IOException e) { + throw new RuntimeException("Failed to parse YAML", e); + } + } + return Optional.empty(); + } +} diff --git a/protoc-gen-jersey/src/main/java/com/fullcontact/rpc/jersey/yaml/YamlHttpRule.java b/protoc-gen-jersey/src/main/java/com/fullcontact/rpc/jersey/yaml/YamlHttpRule.java new file mode 100644 index 0000000..982560b --- /dev/null +++ b/protoc-gen-jersey/src/main/java/com/fullcontact/rpc/jersey/yaml/YamlHttpRule.java @@ -0,0 +1,47 @@ +package com.fullcontact.rpc.jersey.yaml; + +import com.google.api.HttpRule; +import lombok.Data; + +import java.util.List; +import java.util.stream.Collectors; +/** + * Created by kylehansen @Sypticus on 12/28/16. + */ +@Data +public class YamlHttpRule { + private String selector; + private String get; + private String post; + private String put; + private String delete; + private String body; + private List additionalBindings; + + public HttpRule buildHttpRule(){ + HttpRule.Builder builder = HttpRule.newBuilder(); + if(get != null){ + builder.setGet(get); + } + if(put != null){ + builder.setPut(put); + } + if(delete != null){ + builder.setDelete(delete); + } + if(post != null){ + builder.setPost(post); + } + if(body != null){ + builder.setBody(body); + } + if(additionalBindings != null){ + builder.addAllAdditionalBindings(additionalBindings.stream().map(YamlHttpRule::buildHttpRule).collect(Collectors.toList())); + } + + return builder.build(); + + } + + +} diff --git a/protos/test.proto b/protos/test.proto index afafc26..6296e1d 100644 --- a/protos/test.proto +++ b/protos/test.proto @@ -27,6 +27,11 @@ service TestService { rpc TestMethod5 (TestRequest) returns (TestResponse) { //Defined in Yaml } + + rpc TestMethod6 (TestRequest) returns (TestResponse) { + //Defined in Yaml + } + } enum TestEnum { FIRST = 0; From 9dfb2e46045279408ecdc16b7bff586aae2ef3fa Mon Sep 17 00:00:00 2001 From: Kyle Hansen Date: Wed, 4 Jan 2017 15:34:38 -0700 Subject: [PATCH 4/6] removed hardcoded path --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 3a8e10d..9d450b3 100644 --- a/build.gradle +++ b/build.gradle @@ -347,7 +347,7 @@ project(":integration-test-proxy") { all()*.plugins { grpc {} jersey { - option 'proxy,yaml=/Users/kylehansen/dev/opensource/grpc-jersey/integration-test-base/src/test/proto/http_api_config.yml' + option 'proxy,yaml=integration-test-base/src/test/proto/http_api_config.yml' } } } From d86a10c504e290277b58ade7765a487b671d9f45 Mon Sep 17 00:00:00 2001 From: Kyle Hansen Date: Wed, 4 Jan 2017 15:54:47 -0700 Subject: [PATCH 5/6] Cleaned up code and styling --- .../fullcontact/rpc/jersey/CodeGenerator.java | 16 +++++++----- .../rpc/jersey/yaml/YamlHttpConfig.java | 10 ++++++-- .../rpc/jersey/yaml/YamlHttpRule.java | 25 +++++++++++-------- protos/test.proto | 5 ++-- 4 files changed, 34 insertions(+), 22 deletions(-) diff --git a/protoc-gen-jersey/src/main/java/com/fullcontact/rpc/jersey/CodeGenerator.java b/protoc-gen-jersey/src/main/java/com/fullcontact/rpc/jersey/CodeGenerator.java index 8920e51..27b475b 100644 --- a/protoc-gen-jersey/src/main/java/com/fullcontact/rpc/jersey/CodeGenerator.java +++ b/protoc-gen-jersey/src/main/java/com/fullcontact/rpc/jersey/CodeGenerator.java @@ -84,12 +84,16 @@ public PluginProtos.CodeGeneratorResponse generate(PluginProtos.CodeGeneratorReq DescriptorProtos.ServiceDescriptorProto serviceDescriptorProto = serviceDescriptor.toProto(); for(DescriptorProtos.MethodDescriptorProto methodProto : serviceDescriptorProto.getMethodList()) { String fullMethodName = serviceDescriptor.getFullName() +"." + methodProto.getName(); - if(yamlConfig.isPresent()){ //Check to see if the rules are defined in the YAML - for(YamlHttpRule rule :yamlConfig.get().getRules()){ - if(rule.getSelector().equals(fullMethodName) || rule.getSelector().equals("*")){ //TODO: com.foo.* - //YAML http rules override proto files. - https://cloud.google.com/endpoints/docs/grpc-service-config - DescriptorProtos.MethodOptions yamlOptions = DescriptorProtos.MethodOptions.newBuilder().setExtension(AnnotationsProto.http, rule.buildHttpRule()).build(); - methodProto = DescriptorProtos.MethodDescriptorProto.newBuilder().mergeFrom(methodProto).setOptions(yamlOptions).build(); + if(yamlConfig.isPresent()) { //Check to see if the rules are defined in the YAML + for(YamlHttpRule rule : yamlConfig.get().getRules()) { + if(rule.getSelector().equals(fullMethodName) || rule.getSelector().equals("*")) { //TODO: com.foo.* + DescriptorProtos.MethodOptions yamlOptions = DescriptorProtos.MethodOptions.newBuilder() + .setExtension(AnnotationsProto.http, rule.buildHttpRule()) + .build(); + methodProto = DescriptorProtos.MethodDescriptorProto.newBuilder() + .mergeFrom(methodProto) + .setOptions(yamlOptions) + .build(); } } } diff --git a/protoc-gen-jersey/src/main/java/com/fullcontact/rpc/jersey/yaml/YamlHttpConfig.java b/protoc-gen-jersey/src/main/java/com/fullcontact/rpc/jersey/yaml/YamlHttpConfig.java index c761092..a432bee 100644 --- a/protoc-gen-jersey/src/main/java/com/fullcontact/rpc/jersey/yaml/YamlHttpConfig.java +++ b/protoc-gen-jersey/src/main/java/com/fullcontact/rpc/jersey/yaml/YamlHttpConfig.java @@ -14,6 +14,12 @@ /** * Created by kylehansen @Sypticus on 12/28/16. + * + * Allows HTTPRules to be generated from a .yml file instead of the .proto files. + * Any rule defined in a .yml file will override rules in the .proto. + * https://cloud.google.com/endpoints/docs/grpc-service-config + * + * Path to the .yml file should be passed in as part of the optional parameter */ public class YamlHttpConfig { public Map> http; @@ -21,13 +27,13 @@ public List getRules(){ return http.get("rules"); } - public static Optional getFromOptions(Set options){ + public static Optional getFromOptions(Set options) { Optional yamlOption = options.stream().filter(option -> option.startsWith("yaml=")).findFirst(); if(yamlOption.isPresent()) { String yamlPath = yamlOption.get().split("=")[1]; try { File yamlFile = new File(yamlPath); - if(!yamlFile.exists()){ + if(!yamlFile.exists()) { throw new RuntimeException("YAMLs file does not exist: "+ yamlFile.getAbsolutePath()); } InputStream yamlStream = new FileInputStream(yamlFile); diff --git a/protoc-gen-jersey/src/main/java/com/fullcontact/rpc/jersey/yaml/YamlHttpRule.java b/protoc-gen-jersey/src/main/java/com/fullcontact/rpc/jersey/yaml/YamlHttpRule.java index 982560b..5294ee2 100644 --- a/protoc-gen-jersey/src/main/java/com/fullcontact/rpc/jersey/yaml/YamlHttpRule.java +++ b/protoc-gen-jersey/src/main/java/com/fullcontact/rpc/jersey/yaml/YamlHttpRule.java @@ -1,24 +1,27 @@ package com.fullcontact.rpc.jersey.yaml; import com.google.api.HttpRule; -import lombok.Data; +import lombok.Value; import java.util.List; import java.util.stream.Collectors; /** * Created by kylehansen @Sypticus on 12/28/16. + * + * HTTPRules defined in the .yml will be parsed into a YamlHTTPRule, from which a com.google.api.HttpRule can be generated. */ -@Data + +@Value public class YamlHttpRule { - private String selector; - private String get; - private String post; - private String put; - private String delete; - private String body; - private List additionalBindings; - - public HttpRule buildHttpRule(){ + String selector; + String get; + String post; + String put; + String delete; + String body; + List additionalBindings; + + public HttpRule buildHttpRule() { HttpRule.Builder builder = HttpRule.newBuilder(); if(get != null){ builder.setGet(get); diff --git a/protos/test.proto b/protos/test.proto index 6296e1d..6ef018b 100644 --- a/protos/test.proto +++ b/protos/test.proto @@ -22,14 +22,13 @@ service TestService { option (google.api.http).get = "/users/{s=hello/**}/x/{uint3}/{nt.f1}/*/**/test"; } rpc TestMethod4 (TestRequest) returns (TestResponse) { - //Defined in Yaml + //Defined in Yaml } rpc TestMethod5 (TestRequest) returns (TestResponse) { //Defined in Yaml } - rpc TestMethod6 (TestRequest) returns (TestResponse) { - //Defined in Yaml + //Defined in Yaml } } From fc27e2cc2a6e88c3d9df23d70152a112674af87f Mon Sep 17 00:00:00 2001 From: Kyle Hansen Date: Wed, 4 Jan 2017 16:15:04 -0700 Subject: [PATCH 6/6] Cleaned up comments --- .../java/com/fullcontact/rpc/jersey/yaml/YamlHttpConfig.java | 3 ++- .../java/com/fullcontact/rpc/jersey/yaml/YamlHttpRule.java | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/protoc-gen-jersey/src/main/java/com/fullcontact/rpc/jersey/yaml/YamlHttpConfig.java b/protoc-gen-jersey/src/main/java/com/fullcontact/rpc/jersey/yaml/YamlHttpConfig.java index a432bee..16b8aee 100644 --- a/protoc-gen-jersey/src/main/java/com/fullcontact/rpc/jersey/yaml/YamlHttpConfig.java +++ b/protoc-gen-jersey/src/main/java/com/fullcontact/rpc/jersey/yaml/YamlHttpConfig.java @@ -13,13 +13,14 @@ import java.util.Set; /** - * Created by kylehansen @Sypticus on 12/28/16. * * Allows HTTPRules to be generated from a .yml file instead of the .proto files. * Any rule defined in a .yml file will override rules in the .proto. * https://cloud.google.com/endpoints/docs/grpc-service-config * * Path to the .yml file should be passed in as part of the optional parameter + * + * @author Kyle Hansen (sypticus) */ public class YamlHttpConfig { public Map> http; diff --git a/protoc-gen-jersey/src/main/java/com/fullcontact/rpc/jersey/yaml/YamlHttpRule.java b/protoc-gen-jersey/src/main/java/com/fullcontact/rpc/jersey/yaml/YamlHttpRule.java index 5294ee2..60318cc 100644 --- a/protoc-gen-jersey/src/main/java/com/fullcontact/rpc/jersey/yaml/YamlHttpRule.java +++ b/protoc-gen-jersey/src/main/java/com/fullcontact/rpc/jersey/yaml/YamlHttpRule.java @@ -6,9 +6,10 @@ import java.util.List; import java.util.stream.Collectors; /** - * Created by kylehansen @Sypticus on 12/28/16. * * HTTPRules defined in the .yml will be parsed into a YamlHTTPRule, from which a com.google.api.HttpRule can be generated. + * + * @author Kyle Hansen (sypticus) */ @Value