Skip to content

Commit

Permalink
Fix the NPE when there is message field type of google.protobuf.Empty
Browse files Browse the repository at this point in the history
  • Loading branch information
daneshk committed Oct 4, 2024
1 parent 4d85ffc commit 0b36229
Show file tree
Hide file tree
Showing 8 changed files with 291 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ public Field build() {

private Builder(DescriptorProtos.FieldDescriptorProto fieldDescriptor, String fieldType) {
this.fieldDescriptor = fieldDescriptor;
this.type = fieldType;
this.type = fieldType != null ? fieldType : "empty:Empty";
}
}

Expand Down
1 change: 1 addition & 0 deletions protoc-cli/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,5 @@
requires info.picocli;
requires org.slf4j;
requires org.apache.commons.lang3;
requires protobuf.java;
}
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,11 @@ public void testUnaryHelloWorldTimestamp() {
"helloWorldTimestamp_pb.bal", "helloworld_service.bal",
"helloworld_client.bal", "tool_test_unary_10");
}

@Test
public void testUnaryWithEmptyFieldMessage() {
assertGeneratedSources("unary", "emptyFieldMessage.proto",
"emptyFieldMessage_pb.bal", "testservice_service.bal",
"testservice_client.bal", "tool_test_unary_11");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import ballerina/grpc;
import ballerina/protobuf;
import ballerina/protobuf.types.empty;

public const string EMPTYFIELDMESSAGE_DESC = "0A17656D7074794669656C644D6573736167652E70726F746F1204746573741A1B676F6F676C652F70726F746F6275662F656D7074792E70726F746F22410A0B436F6D706C65785479706512100A03666F6F18012001280D5203666F6F12200A0362617218022001280E320E2E746573742E54657374456E756D520362617222790A134F7074696F6E616C436F6D706C65785479706512360A0D636F6D706C65785F76616C756518012001280B32112E746573742E436F6D706C657854797065520C636F6D706C657856616C7565122A0A046E6F6E6518022001280B32162E676F6F676C652E70726F746F6275662E456D70747952046E6F6E6522500A0E4F7074696F6E616C537472696E6712120A046E616D6518012001280952046E616D65122A0A046E6F6E6518022001280B32162E676F6F676C652E70726F746F6275662E456D70747952046E6F6E65223D0A0E526571756573744D657373616765122B0A0372657118012003280B32192E746573742E4F7074696F6E616C436F6D706C6578547970655203726571223B0A0F526573706F6E73654D65737361676512280A047265737018012003280B32142E746573742E4F7074696F6E616C537472696E675204726573702A280A0854657374456E756D120D0A09454E5452595F4F4E451000120D0A09454E5452595F54574F100132450A0B546573745365727669636512360A075465737452504312142E746573742E526571756573744D6573736167651A152E746573742E526573706F6E73654D657373616765620670726F746F33";

public isolated client class TestServiceClient {
*grpc:AbstractClientEndpoint;

private final grpc:Client grpcClient;

public isolated function init(string url, *grpc:ClientConfiguration config) returns grpc:Error? {
self.grpcClient = check new (url, config);
check self.grpcClient.initStub(self, EMPTYFIELDMESSAGE_DESC);
}

isolated remote function TestRPC(RequestMessage|ContextRequestMessage req) returns ResponseMessage|grpc:Error {
map<string|string[]> headers = {};
RequestMessage message;
if req is ContextRequestMessage {
message = req.content;
headers = req.headers;
} else {
message = req;
}
var payload = check self.grpcClient->executeSimpleRPC("test.TestService/TestRPC", message, headers);
[anydata, map<string|string[]>] [result, _] = payload;
return <ResponseMessage>result;
}

isolated remote function TestRPCContext(RequestMessage|ContextRequestMessage req) returns ContextResponseMessage|grpc:Error {
map<string|string[]> headers = {};
RequestMessage message;
if req is ContextRequestMessage {
message = req.content;
headers = req.headers;
} else {
message = req;
}
var payload = check self.grpcClient->executeSimpleRPC("test.TestService/TestRPC", message, headers);
[anydata, map<string|string[]>] [result, respHeaders] = payload;
return {content: <ResponseMessage>result, headers: respHeaders};
}
}

public isolated client class TestServiceResponseMessageCaller {
private final grpc:Caller caller;

public isolated function init(grpc:Caller caller) {
self.caller = caller;
}

public isolated function getId() returns int {
return self.caller.getId();
}

isolated remote function sendResponseMessage(ResponseMessage response) returns grpc:Error? {
return self.caller->send(response);
}

isolated remote function sendContextResponseMessage(ContextResponseMessage response) returns grpc:Error? {
return self.caller->send(response);
}

isolated remote function sendError(grpc:Error response) returns grpc:Error? {
return self.caller->sendError(response);
}

isolated remote function complete() returns grpc:Error? {
return self.caller->complete();
}

public isolated function isCancelled() returns boolean {
return self.caller.isCancelled();
}
}

public type ContextRequestMessage record {|
RequestMessage content;
map<string|string[]> headers;
|};

public type ContextResponseMessage record {|
ResponseMessage content;
map<string|string[]> headers;
|};

@protobuf:Descriptor {value: EMPTYFIELDMESSAGE_DESC}
public type RequestMessage record {|
OptionalComplexType[] req = [];
|};

@protobuf:Descriptor {value: EMPTYFIELDMESSAGE_DESC}
public type OptionalComplexType record {|
ComplexType complex_value = {};
empty:Empty none = {};
|};

@protobuf:Descriptor {value: EMPTYFIELDMESSAGE_DESC}
public type ResponseMessage record {|
OptionalString[] resp = [];
|};

@protobuf:Descriptor {value: EMPTYFIELDMESSAGE_DESC}
public type OptionalString record {|
string name = "";
empty:Empty none = {};
|};

@protobuf:Descriptor {value: EMPTYFIELDMESSAGE_DESC}
public type ComplexType record {|
int:Unsigned32 foo = 0;
TestEnum bar = ENTRY_ONE;
|};

public enum TestEnum {
ENTRY_ONE, ENTRY_TWO
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import ballerina/grpc;
import ballerina/protobuf;
import ballerina/protobuf.types.empty;

public const string EMPTYFIELDMESSAGE_DESC = "0A17656D7074794669656C644D6573736167652E70726F746F1204746573741A1B676F6F676C652F70726F746F6275662F656D7074792E70726F746F22410A0B436F6D706C65785479706512100A03666F6F18012001280D5203666F6F12200A0362617218022001280E320E2E746573742E54657374456E756D520362617222790A134F7074696F6E616C436F6D706C65785479706512360A0D636F6D706C65785F76616C756518012001280B32112E746573742E436F6D706C657854797065520C636F6D706C657856616C7565122A0A046E6F6E6518022001280B32162E676F6F676C652E70726F746F6275662E456D70747952046E6F6E6522500A0E4F7074696F6E616C537472696E6712120A046E616D6518012001280952046E616D65122A0A046E6F6E6518022001280B32162E676F6F676C652E70726F746F6275662E456D70747952046E6F6E65223D0A0E526571756573744D657373616765122B0A0372657118012003280B32192E746573742E4F7074696F6E616C436F6D706C6578547970655203726571223B0A0F526573706F6E73654D65737361676512280A047265737018012003280B32142E746573742E4F7074696F6E616C537472696E675204726573702A280A0854657374456E756D120D0A09454E5452595F4F4E451000120D0A09454E5452595F54574F100132450A0B546573745365727669636512360A075465737452504312142E746573742E526571756573744D6573736167651A152E746573742E526573706F6E73654D657373616765620670726F746F33";

public isolated client class TestServiceClient {
*grpc:AbstractClientEndpoint;

private final grpc:Client grpcClient;

public isolated function init(string url, *grpc:ClientConfiguration config) returns grpc:Error? {
self.grpcClient = check new (url, config);
check self.grpcClient.initStub(self, EMPTYFIELDMESSAGE_DESC);
}

isolated remote function TestRPC(RequestMessage|ContextRequestMessage req) returns ResponseMessage|grpc:Error {
map<string|string[]> headers = {};
RequestMessage message;
if req is ContextRequestMessage {
message = req.content;
headers = req.headers;
} else {
message = req;
}
var payload = check self.grpcClient->executeSimpleRPC("test.TestService/TestRPC", message, headers);
[anydata, map<string|string[]>] [result, _] = payload;
return <ResponseMessage>result;
}

isolated remote function TestRPCContext(RequestMessage|ContextRequestMessage req) returns ContextResponseMessage|grpc:Error {
map<string|string[]> headers = {};
RequestMessage message;
if req is ContextRequestMessage {
message = req.content;
headers = req.headers;
} else {
message = req;
}
var payload = check self.grpcClient->executeSimpleRPC("test.TestService/TestRPC", message, headers);
[anydata, map<string|string[]>] [result, respHeaders] = payload;
return {content: <ResponseMessage>result, headers: respHeaders};
}
}

public type ContextRequestMessage record {|
RequestMessage content;
map<string|string[]> headers;
|};

public type ContextResponseMessage record {|
ResponseMessage content;
map<string|string[]> headers;
|};

@protobuf:Descriptor {value: EMPTYFIELDMESSAGE_DESC}
public type RequestMessage record {|
OptionalComplexType[] req = [];
|};

@protobuf:Descriptor {value: EMPTYFIELDMESSAGE_DESC}
public type OptionalComplexType record {|
ComplexType complex_value = {};
empty:Empty none = {};
|};

@protobuf:Descriptor {value: EMPTYFIELDMESSAGE_DESC}
public type ResponseMessage record {|
OptionalString[] resp = [];
|};

@protobuf:Descriptor {value: EMPTYFIELDMESSAGE_DESC}
public type OptionalString record {|
string name = "";
empty:Empty none = {};
|};

@protobuf:Descriptor {value: EMPTYFIELDMESSAGE_DESC}
public type ComplexType record {|
int:Unsigned32 foo = 0;
TestEnum bar = ENTRY_ONE;
|};

public enum TestEnum {
ENTRY_ONE, ENTRY_TWO
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import ballerina/io;

TestServiceClient ep = check new ("http://localhost:9090");

public function main() returns error? {
RequestMessage testRPCRequest = {req: [{complex_value: {foo: 1, bar: "ENTRY_ONE"}, none: {}}]};
ResponseMessage testRPCResponse = check ep->TestRPC(testRPCRequest);
io:println(testRPCResponse);
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import ballerina/grpc;

listener grpc:Listener ep = new (9090);

@grpc:Descriptor {value: EMPTYFIELDMESSAGE_DESC}
service "TestService" on ep {

remote function TestRPC(RequestMessage value) returns ResponseMessage|error {
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org).
*
* WSO2 LLC. licenses this file to you 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.
*/

syntax = "proto3";

package test;

import "google/protobuf/empty.proto";

service TestService {
rpc TestRPC(RequestMessage) returns (ResponseMessage);
}

enum TestEnum {
ENTRY_ONE = 0;
ENTRY_TWO = 1;
}

message ComplexType {
uint32 foo = 1;
TestEnum bar = 2;
}

message OptionalComplexType {
ComplexType complex_value = 1;
google.protobuf.Empty none = 2;
}

message OptionalString {
string name = 1;
google.protobuf.Empty none = 2;
}

message RequestMessage {
repeated OptionalComplexType req = 1;
}

message ResponseMessage {
repeated OptionalString resp = 1;
}

0 comments on commit 0b36229

Please sign in to comment.