From 44cecbb2f73d1aaf65fd67d2e413c891210f54f1 Mon Sep 17 00:00:00 2001 From: Feso Date: Wed, 3 Oct 2018 00:50:57 +0100 Subject: grpc_cli json support --- test/cpp/util/config_grpc_cli.h | 7 + test/cpp/util/grpc_tool.cc | 103 ++++++++-- test/cpp/util/grpc_tool_test.cc | 379 ++++++++++++++++++++++++++++++++++++- test/cpp/util/proto_file_parser.cc | 81 +++++++- test/cpp/util/proto_file_parser.h | 20 +- 5 files changed, 561 insertions(+), 29 deletions(-) (limited to 'test/cpp/util') diff --git a/test/cpp/util/config_grpc_cli.h b/test/cpp/util/config_grpc_cli.h index 358884196d..1df7b36e2e 100644 --- a/test/cpp/util/config_grpc_cli.h +++ b/test/cpp/util/config_grpc_cli.h @@ -40,6 +40,11 @@ #define GRPC_CUSTOM_TEXTFORMAT ::google::protobuf::TextFormat #endif +#ifndef GRPC_CUSTOM_JSONUTIL +#include +#define GRPC_CUSTOM_JSONUTIL ::google::protobuf::util +#endif + #ifndef GRPC_CUSTOM_DISKSOURCETREE #include #define GRPC_CUSTOM_DISKSOURCETREE ::google::protobuf::compiler::DiskSourceTree @@ -58,6 +63,8 @@ typedef GRPC_CUSTOM_MERGEDDESCRIPTORDATABASE MergedDescriptorDatabase; typedef GRPC_CUSTOM_TEXTFORMAT TextFormat; +namespace json = GRPC_CUSTOM_JSONUTIL; + namespace compiler { typedef GRPC_CUSTOM_DISKSOURCETREE DiskSourceTree; typedef GRPC_CUSTOM_IMPORTER Importer; diff --git a/test/cpp/util/grpc_tool.cc b/test/cpp/util/grpc_tool.cc index ccc60cca27..b4eeee1a9b 100644 --- a/test/cpp/util/grpc_tool.cc +++ b/test/cpp/util/grpc_tool.cc @@ -57,6 +57,8 @@ DEFINE_string(proto_path, ".", "Path to look for the proto file."); DEFINE_string(protofiles, "", "Name of the proto file."); DEFINE_bool(binary_input, false, "Input in binary format"); DEFINE_bool(binary_output, false, "Output in binary format"); +DEFINE_bool(json_input, false, "Input in json format"); +DEFINE_bool(json_output, false, "Output in json format"); DEFINE_string(infile, "", "Input file (default is stdin)"); DEFINE_bool(batch, false, "Input contains multiple requests. Please do not use this to send " @@ -88,6 +90,8 @@ class GrpcTool { GrpcToolOutputCallback callback); bool ToText(int argc, const char** argv, const CliCredentials& cred, GrpcToolOutputCallback callback); + bool ToJson(int argc, const char** argv, const CliCredentials& cred, + GrpcToolOutputCallback callback); bool ToBinary(int argc, const char** argv, const CliCredentials& cred, GrpcToolOutputCallback callback); @@ -233,6 +237,7 @@ const Command ops[] = { {"parse", BindWith5Args(&GrpcTool::ParseMessage), 2, 3}, {"totext", BindWith5Args(&GrpcTool::ToText), 2, 3}, {"tobinary", BindWith5Args(&GrpcTool::ToBinary), 2, 3}, + {"tojson", BindWith5Args(&GrpcTool::ToJson), 2, 3}, }; void Usage(const grpc::string& msg) { @@ -244,6 +249,7 @@ void Usage(const grpc::string& msg) { " grpc_cli type ... ; Print type\n" " grpc_cli parse ... ; Parse message\n" " grpc_cli totext ... ; Convert binary message to text\n" + " grpc_cli tojson ... ; Convert binary message to json\n" " grpc_cli tobinary ... ; Convert text message to binary\n" " grpc_cli help ... ; Print this message, or per-command usage\n" "\n", @@ -465,7 +471,9 @@ bool GrpcTool::CallMethod(int argc, const char** argv, " --infile ; Input filename (defaults to stdin)\n" " --outfile ; Output filename (defaults to stdout)\n" " --binary_input ; Input in binary format\n" - " --binary_output ; Output in binary format\n" + + " --binary_output ; Output in binary format\n" + " --json_input ; Input in json format\n" + " --json_output ; Output in json format\n" + cred.GetCredentialUsage()); std::stringstream output_ss; @@ -547,8 +555,12 @@ bool GrpcTool::CallMethod(int argc, const char** argv, request_text.clear(); } else { gpr_mu_lock(&parser_mu); - serialized_request_proto = parser->GetSerializedProtoFromMethod( - method_name, request_text, true /* is_request */); + serialized_request_proto = + FLAGS_json_input ? + parser->GetSerializedProtoFromMethodJsonFormat( + method_name, request_text, true /* is_request */) : + parser->GetSerializedProtoFromMethodTextFormat( + method_name, request_text, true /* is_request */); request_text.clear(); if (parser->HasError()) { if (print_mode) { @@ -631,8 +643,12 @@ bool GrpcTool::CallMethod(int argc, const char** argv, serialized_request_proto = request_text; request_text.clear(); } else { - serialized_request_proto = parser->GetSerializedProtoFromMethod( - method_name, request_text, true /* is_request */); + serialized_request_proto = + FLAGS_json_input ? + parser->GetSerializedProtoFromMethodJsonFormat( + method_name, request_text, true /* is_request */) : + parser->GetSerializedProtoFromMethodTextFormat( + method_name, request_text, true /* is_request */); request_text.clear(); if (parser->HasError()) { if (print_mode) { @@ -668,9 +684,15 @@ bool GrpcTool::CallMethod(int argc, const char** argv, break; } } else { - grpc::string response_text = parser->GetTextFormatFromMethod( - method_name, serialized_response_proto, - false /* is_request */); + grpc::string response_text = + FLAGS_json_output ? + parser->GetJsonFormatFromMethod(method_name, + serialized_response_proto, + false /* is_request */) : + parser->GetTextFormatFromMethod(method_name, + serialized_response_proto, + false /* is_request */); + if (parser->HasError() && print_mode) { fprintf(stderr, "Failed to parse response.\n"); } else { @@ -726,8 +748,12 @@ bool GrpcTool::CallMethod(int argc, const char** argv, if (FLAGS_binary_input) { serialized_request_proto = request_text; } else { - serialized_request_proto = parser->GetSerializedProtoFromMethod( - method_name, request_text, true /* is_request */); + serialized_request_proto = + FLAGS_json_input ? + parser->GetSerializedProtoFromMethodJsonFormat( + method_name, request_text, true /* is_request */) : + parser->GetSerializedProtoFromMethodTextFormat( + method_name, request_text, true /* is_request */); if (parser->HasError()) { fprintf(stderr, "Failed to parse request.\n"); return false; @@ -750,14 +776,20 @@ bool GrpcTool::CallMethod(int argc, const char** argv, &serialized_response_proto, receive_initial_metadata ? &server_initial_metadata : nullptr); receive_initial_metadata = false) { - if (!FLAGS_binary_output) { + if (FLAGS_json_output) { + serialized_response_proto = parser->GetJsonFormatFromMethod( + method_name, serialized_response_proto, false /* is_request */); + } else if (!FLAGS_binary_output) { serialized_response_proto = parser->GetTextFormatFromMethod( method_name, serialized_response_proto, false /* is_request */); - if (parser->HasError()) { - fprintf(stderr, "Failed to parse response.\n"); - return false; - } } + if (FLAGS_json_output || !FLAGS_binary_output) { + if (parser->HasError()) { + fprintf(stderr, "Failed to parse response.\n"); + return false; + } + } + if (receive_initial_metadata) { PrintMetadata(server_initial_metadata, "Received initial metadata from server:"); @@ -797,7 +829,9 @@ bool GrpcTool::ParseMessage(int argc, const char** argv, " --infile ; Input filename (defaults to stdin)\n" " --outfile ; Output filename (defaults to stdout)\n" " --binary_input ; Input in binary format\n" - " --binary_output ; Output in binary format\n" + + " --binary_output ; Output in binary format\n" + " --json_input ; Input in json format\n" + " --json_output ; Output in json format\n" + cred.GetCredentialUsage()); std::stringstream output_ss; @@ -845,7 +879,11 @@ bool GrpcTool::ParseMessage(int argc, const char** argv, serialized_request_proto = message_text; } else { serialized_request_proto = - parser->GetSerializedProtoFromMessageType(type_name, message_text); + FLAGS_json_input ? + parser->GetSerializedProtoFromMessageTypeJsonFormat(type_name, + message_text) : + parser->GetSerializedProtoFromMessageTypeTextFormat(type_name, + message_text); if (parser->HasError()) { fprintf(stderr, "Failed to serialize the message.\n"); return false; @@ -855,12 +893,20 @@ bool GrpcTool::ParseMessage(int argc, const char** argv, if (FLAGS_binary_output) { output_ss << serialized_request_proto; } else { - grpc::string output_text = parser->GetTextFormatFromMessageType( - type_name, serialized_request_proto); + grpc::string output_text; + if (FLAGS_json_output) { + output_text = parser->GetJsonFormatFromMessageType( + type_name, serialized_request_proto); + } else { + output_text = parser->GetTextFormatFromMessageType( + type_name, serialized_request_proto); + } + if (parser->HasError()) { fprintf(stderr, "Failed to deserialize the message.\n"); return false; } + output_ss << output_text << std::endl; } @@ -885,6 +931,25 @@ bool GrpcTool::ToText(int argc, const char** argv, const CliCredentials& cred, return ParseMessage(argc, argv, cred, callback); } +bool GrpcTool::ToJson(int argc, const char** argv, const CliCredentials& cred, + GrpcToolOutputCallback callback) { + CommandUsage( + "Convert binary message to json\n" + " grpc_cli tojson \n" + " ; Comma separated list of proto files\n" + " ; Protocol buffer type name\n" + " --proto_path ; The search path of proto files\n" + " --infile ; Input filename (defaults to stdin)\n" + " --outfile ; Output filename (defaults to stdout)\n"); + + FLAGS_protofiles = argv[0]; + FLAGS_remotedb = false; + FLAGS_binary_input = true; + FLAGS_binary_output = false; + FLAGS_json_output = true; + return ParseMessage(argc, argv, cred, callback); +} + bool GrpcTool::ToBinary(int argc, const char** argv, const CliCredentials& cred, GrpcToolOutputCallback callback) { CommandUsage( diff --git a/test/cpp/util/grpc_tool_test.cc b/test/cpp/util/grpc_tool_test.cc index 3aae090e81..4409b5b7ed 100644 --- a/test/cpp/util/grpc_tool_test.cc +++ b/test/cpp/util/grpc_tool_test.cc @@ -74,13 +74,22 @@ using grpc::testing::EchoResponse; " rpc Echo(grpc.testing.EchoRequest) returns (grpc.testing.EchoResponse) " \ "{}\n" -#define ECHO_RESPONSE_MESSAGE \ +#define ECHO_RESPONSE_MESSAGE_TEXT_FORMAT \ "message: \"echo\"\n" \ "param {\n" \ " host: \"localhost\"\n" \ " peer: \"peer\"\n" \ "}\n\n" +#define ECHO_RESPONSE_MESSAGE_JSON_FORMAT \ + "{\n" \ + " \"message\": \"echo\",\n" \ + " \"param\": {\n" \ + " \"host\": \"localhost\",\n" \ + " \"peer\": \"peer\"\n" \ + " }\n" \ + "}\n\n" + DECLARE_string(channel_creds_type); DECLARE_string(ssl_target); @@ -89,6 +98,8 @@ namespace testing { DECLARE_bool(binary_input); DECLARE_bool(binary_output); +DECLARE_bool(json_input); +DECLARE_bool(json_output); DECLARE_bool(l); DECLARE_bool(batch); DECLARE_string(metadata); @@ -426,6 +437,61 @@ TEST_F(GrpcToolTest, CallCommand) { // Expected output: "message: \"Hello\"" EXPECT_TRUE(nullptr != strstr(output_stream.str().c_str(), "message: \"Hello\"")); + + // with json_output + output_stream.str(grpc::string()); + output_stream.clear(); + + FLAGS_json_output = true; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + FLAGS_json_output = false; + + // Expected output: + // { + // "message": "Hello" + // } + EXPECT_TRUE(nullptr != + strstr(output_stream.str().c_str(), "{\n \"message\": \"Hello\"\n}")); + + ShutdownServer(); +} + +TEST_F(GrpcToolTest, CallCommandJsonInput) { + // Test input "grpc_cli call localhost: Echo "{ \"message\": \"Hello\"}" + std::stringstream output_stream; + + const grpc::string server_address = SetUpServer(); + const char* argv[] = {"grpc_cli", "call", server_address.c_str(), "Echo", + "{ \"message\": \"Hello\"}"}; + + FLAGS_json_input = true; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + // Expected output: "message: \"Hello\"" + EXPECT_TRUE(nullptr != + strstr(output_stream.str().c_str(), "message: \"Hello\"")); + + // with json_output + output_stream.str(grpc::string()); + output_stream.clear(); + + FLAGS_json_output = true; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + FLAGS_json_output = false; + FLAGS_json_input = false; + + // Expected output: + // { + // "message": "Hello" + // } + EXPECT_TRUE(nullptr != + strstr(output_stream.str().c_str(), "{\n \"message\": \"Hello\"\n}")); + ShutdownServer(); } @@ -453,6 +519,101 @@ TEST_F(GrpcToolTest, CallCommandBatch) { EXPECT_TRUE(nullptr != strstr(output_stream.str().c_str(), "message: \"Hello0\"\nmessage: " "\"Hello1\"\nmessage: \"Hello2\"\n")); + // with json_output + output_stream.str(grpc::string()); + output_stream.clear(); + ss.clear(); + ss.seekg(0); + std::cin.rdbuf(ss.rdbuf()); + + FLAGS_batch = true; + FLAGS_json_output= true; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + FLAGS_json_output= false; + FLAGS_batch = false; + + // Expected output: + // { + // "message": "Hello0" + // } + // { + // "message": "Hello1" + // } + // { + // "message": "Hello2" + // } + // Expected output: "message: "Hello0"\nmessage: "Hello1"\nmessage: + // "Hello2"\n" + EXPECT_TRUE(nullptr != strstr(output_stream.str().c_str(), + "{\n \"message\": \"Hello0\"\n}\n" + "{\n \"message\": \"Hello1\"\n}\n" + "{\n \"message\": \"Hello2\"\n}\n")); + + std::cin.rdbuf(orig); + ShutdownServer(); +} + +TEST_F(GrpcToolTest, CallCommandBatchJsonInput) { + // Test input "grpc_cli call Echo" + std::stringstream output_stream; + + const grpc::string server_address = SetUpServer(); + const char* argv[] = {"grpc_cli", "call", server_address.c_str(), "Echo", + "{\"message\": \"Hello0\"}"}; + + // Mock std::cin input "message: 'Hello1'\n\n message: 'Hello2'\n\n" + std::streambuf* orig = std::cin.rdbuf(); + std::istringstream ss("{\"message\": \"Hello1\"}\n\n{\"message\": \"Hello2\" }\n\n"); + std::cin.rdbuf(ss.rdbuf()); + + FLAGS_json_input = true; + FLAGS_batch = true; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + FLAGS_batch = false; + + // Expected output: "message: "Hello0"\nmessage: "Hello1"\nmessage: + // "Hello2"\n" + EXPECT_TRUE(nullptr != strstr(output_stream.str().c_str(), + "message: \"Hello0\"\nmessage: " + "\"Hello1\"\nmessage: \"Hello2\"\n")); + // with json_output + output_stream.str(grpc::string()); + output_stream.clear(); + ss.clear(); + ss.seekg(0); + std::cin.rdbuf(ss.rdbuf()); + + FLAGS_batch = true; + FLAGS_json_output= true; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + FLAGS_json_output= false; + FLAGS_batch = false; + FLAGS_json_input = false; + + // Expected output: + // { + // "message": "Hello0" + // } + // { + // "message": "Hello1" + // } + // { + // "message": "Hello2" + // } + // Expected output: "message: "Hello0"\nmessage: "Hello1"\nmessage: + // "Hello2"\n" + EXPECT_TRUE(nullptr != strstr(output_stream.str().c_str(), + "{\n \"message\": \"Hello0\"\n}\n" + "{\n \"message\": \"Hello1\"\n}\n" + "{\n \"message\": \"Hello2\"\n}\n")); + + std::cin.rdbuf(orig); ShutdownServer(); } @@ -479,6 +640,94 @@ TEST_F(GrpcToolTest, CallCommandBatchWithBadRequest) { // Expected output: "message: "Hello0"\nmessage: "Hello2"\n" EXPECT_TRUE(nullptr != strstr(output_stream.str().c_str(), "message: \"Hello0\"\nmessage: \"Hello2\"\n")); + + // with json_output + output_stream.str(grpc::string()); + output_stream.clear(); + ss.clear(); + ss.seekg(0); + std::cin.rdbuf(ss.rdbuf()); + + FLAGS_batch = true; + FLAGS_json_output= true; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + FLAGS_json_output= false; + FLAGS_batch = false; + + // Expected output: + // { + // "message": "Hello0" + // } + // { + // "message": "Hello2" + // } + // Expected output: "message: "Hello0"\nmessage: "Hello1"\nmessage: + // "Hello2"\n" + EXPECT_TRUE(nullptr != strstr(output_stream.str().c_str(), + "{\n \"message\": \"Hello0\"\n}\n" + "{\n \"message\": \"Hello2\"\n}\n")); + + std::cin.rdbuf(orig); + ShutdownServer(); +} + +TEST_F(GrpcToolTest, CallCommandBatchJsonInputWithBadRequest) { + // Test input "grpc_cli call Echo" + std::stringstream output_stream; + + const grpc::string server_address = SetUpServer(); + const char* argv[] = {"grpc_cli", "call", server_address.c_str(), "Echo", + "{ \"message\": \"Hello0\"}"}; + + // Mock std::cin input "message: 1\n\n message: 'Hello2'\n\n" + std::streambuf* orig = std::cin.rdbuf(); + std::istringstream ss("{ \"message\": 1 }\n\n { \"message\": \"Hello2\" }\n\n"); + std::cin.rdbuf(ss.rdbuf()); + + FLAGS_batch = true; + FLAGS_json_input = true; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + FLAGS_json_input = false; + FLAGS_batch = false; + + // Expected output: "message: "Hello0"\nmessage: "Hello2"\n" + EXPECT_TRUE(nullptr != strstr(output_stream.str().c_str(), + "message: \"Hello0\"\nmessage: \"Hello2\"\n")); + + // with json_output + output_stream.str(grpc::string()); + output_stream.clear(); + ss.clear(); + ss.seekg(0); + std::cin.rdbuf(ss.rdbuf()); + + FLAGS_batch = true; + FLAGS_json_input = true; + FLAGS_json_output= true; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + FLAGS_json_output= false; + FLAGS_json_input = false; + FLAGS_batch = false; + + // Expected output: + // { + // "message": "Hello0" + // } + // { + // "message": "Hello2" + // } + // Expected output: "message: "Hello0"\nmessage: "Hello1"\nmessage: + // "Hello2"\n" + EXPECT_TRUE(nullptr != strstr(output_stream.str().c_str(), + "{\n \"message\": \"Hello0\"\n}\n" + "{\n \"message\": \"Hello2\"\n}\n")); + std::cin.rdbuf(orig); ShutdownServer(); } @@ -508,6 +757,33 @@ TEST_F(GrpcToolTest, CallCommandRequestStream) { ShutdownServer(); } +TEST_F(GrpcToolTest, CallCommandRequestStreamJsonInput) { + // Test input: grpc_cli call localhost: RequestStream "{ \"message\": + // \"Hello0\"}" + std::stringstream output_stream; + + const grpc::string server_address = SetUpServer(); + const char* argv[] = {"grpc_cli", "call", server_address.c_str(), + "RequestStream", "{ \"message\": \"Hello0\" }"}; + + // Mock std::cin input "message: 'Hello1'\n\n message: 'Hello2'\n\n" + std::streambuf* orig = std::cin.rdbuf(); + std::istringstream ss("{ \"message\": \"Hello1\" }\n\n{ \"message\": \"Hello2\" }\n\n"); + std::cin.rdbuf(ss.rdbuf()); + + FLAGS_json_input = true; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + FLAGS_json_input = false; + + // Expected output: "message: \"Hello0Hello1Hello2\"" + EXPECT_TRUE(nullptr != strstr(output_stream.str().c_str(), + "message: \"Hello0Hello1Hello2\"")); + std::cin.rdbuf(orig); + ShutdownServer(); +} + TEST_F(GrpcToolTest, CallCommandRequestStreamWithBadRequest) { // Test input: grpc_cli call localhost: RequestStream "message: // 'Hello0'" @@ -533,6 +809,33 @@ TEST_F(GrpcToolTest, CallCommandRequestStreamWithBadRequest) { ShutdownServer(); } +TEST_F(GrpcToolTest, CallCommandRequestStreamWithBadRequestJsonInput) { + // Test input: grpc_cli call localhost: RequestStream "message: + // 'Hello0'" + std::stringstream output_stream; + + const grpc::string server_address = SetUpServer(); + const char* argv[] = {"grpc_cli", "call", server_address.c_str(), + "RequestStream", "{ \"message\": \"Hello0\" }"}; + + // Mock std::cin input "bad_field: 'Hello1'\n\n message: 'Hello2'\n\n" + std::streambuf* orig = std::cin.rdbuf(); + std::istringstream ss("{ \"bad_field\": \"Hello1\" }\n\n{ \"message\": \"Hello2\" }\n\n"); + std::cin.rdbuf(ss.rdbuf()); + + FLAGS_json_input = true; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + FLAGS_json_input = false; + + // Expected output: "message: \"Hello0Hello2\"" + EXPECT_TRUE(nullptr != + strstr(output_stream.str().c_str(), "message: \"Hello0Hello2\"")); + std::cin.rdbuf(orig); + ShutdownServer(); +} + TEST_F(GrpcToolTest, CallCommandResponseStream) { // Test input: grpc_cli call localhost: ResponseStream "message: // 'Hello'" @@ -554,6 +857,24 @@ TEST_F(GrpcToolTest, CallCommandResponseStream) { expected_response_text.c_str())); } + // with json_output + output_stream.str(grpc::string()); + output_stream.clear(); + + FLAGS_json_output= true; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + FLAGS_json_output = false; + + // Expected output: "{\n \"message\": \"Hello{n}\"\n}\n" + for (int i = 0; i < kServerDefaultResponseStreamsToSend; i++) { + grpc::string expected_response_text = + "{\n \"message\": \"Hello" + grpc::to_string(i) + "\"\n}\n"; + EXPECT_TRUE(nullptr != strstr(output_stream.str().c_str(), + expected_response_text.c_str())); + } + ShutdownServer(); } @@ -617,15 +938,28 @@ TEST_F(GrpcToolTest, ParseCommand) { const grpc::string server_address = SetUpServer(); const char* argv[] = {"grpc_cli", "parse", server_address.c_str(), - "grpc.testing.EchoResponse", ECHO_RESPONSE_MESSAGE}; + "grpc.testing.EchoResponse", ECHO_RESPONSE_MESSAGE_TEXT_FORMAT}; FLAGS_binary_input = false; FLAGS_binary_output = false; EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), std::bind(PrintStream, &output_stream, std::placeholders::_1))); - // Expected output: ECHO_RESPONSE_MESSAGE - EXPECT_TRUE(0 == strcmp(output_stream.str().c_str(), ECHO_RESPONSE_MESSAGE)); + // Expected output: ECHO_RESPONSE_MESSAGE_TEXT_FORMAT + EXPECT_TRUE(0 == strcmp(output_stream.str().c_str(), ECHO_RESPONSE_MESSAGE_TEXT_FORMAT)); + + // with json_output + output_stream.str(grpc::string()); + output_stream.clear(); + + FLAGS_json_output = true; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + FLAGS_json_output = false; + + // Expected output: ECHO_RESPONSE_MESSAGE_JSON_FORMAT + EXPECT_TRUE(0 == strcmp(output_stream.str().c_str(), ECHO_RESPONSE_MESSAGE_JSON_FORMAT)); // Parse text message to binary message and then parse it back to text message output_stream.str(grpc::string()); @@ -645,13 +979,48 @@ TEST_F(GrpcToolTest, ParseCommand) { std::placeholders::_1))); // Expected output: ECHO_RESPONSE_MESSAGE - EXPECT_TRUE(0 == strcmp(output_stream.str().c_str(), ECHO_RESPONSE_MESSAGE)); + EXPECT_TRUE(0 == strcmp(output_stream.str().c_str(), ECHO_RESPONSE_MESSAGE_TEXT_FORMAT)); FLAGS_binary_input = false; FLAGS_binary_output = false; ShutdownServer(); } +TEST_F(GrpcToolTest, ParseCommandJsonFormat) { + // Test input "grpc_cli parse localhost: grpc.testing.EchoResponse + // ECHO_RESPONSE_MESSAGE_JSON_FORMAT" + std::stringstream output_stream; + std::stringstream binary_output_stream; + + const grpc::string server_address = SetUpServer(); + const char* argv[] = {"grpc_cli", "parse", server_address.c_str(), + "grpc.testing.EchoResponse", ECHO_RESPONSE_MESSAGE_JSON_FORMAT}; + + FLAGS_json_input = true; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + + // Expected output: ECHO_RESPONSE_MESSAGE_TEXT_FORMAT + EXPECT_TRUE(0 == strcmp(output_stream.str().c_str(), ECHO_RESPONSE_MESSAGE_TEXT_FORMAT)); + + // with json_output + output_stream.str(grpc::string()); + output_stream.clear(); + + FLAGS_json_output = true; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + FLAGS_json_output = false; + FLAGS_json_input = false; + + // Expected output: ECHO_RESPONSE_MESSAGE_JSON_FORMAT + EXPECT_TRUE(0 == strcmp(output_stream.str().c_str(), ECHO_RESPONSE_MESSAGE_JSON_FORMAT)); + + ShutdownServer(); +} + TEST_F(GrpcToolTest, TooFewArguments) { // Test input "grpc_cli call Echo" std::stringstream output_stream; diff --git a/test/cpp/util/proto_file_parser.cc b/test/cpp/util/proto_file_parser.cc index a530ed1ffc..997808cbc3 100644 --- a/test/cpp/util/proto_file_parser.cc +++ b/test/cpp/util/proto_file_parser.cc @@ -216,7 +216,7 @@ bool ProtoFileParser::IsStreaming(const grpc::string& method, bool is_request) { : method_desc->server_streaming(); } -grpc::string ProtoFileParser::GetSerializedProtoFromMethod( +grpc::string ProtoFileParser::GetSerializedProtoFromMethodTextFormat( const grpc::string& method, const grpc::string& text_format_proto, bool is_request) { has_error_ = false; @@ -224,10 +224,22 @@ grpc::string ProtoFileParser::GetSerializedProtoFromMethod( if (has_error_) { return ""; } - return GetSerializedProtoFromMessageType(message_type_name, + return GetSerializedProtoFromMessageTypeTextFormat(message_type_name, text_format_proto); } +grpc::string ProtoFileParser::GetSerializedProtoFromMethodJsonFormat( + const grpc::string& method, const grpc::string& json_format_proto, + bool is_request) { + has_error_ = false; + grpc::string message_type_name = GetMessageTypeFromMethod(method, is_request); + if (has_error_) { + return ""; + } + return GetSerializedProtoFromMessageTypeJsonFormat(message_type_name, + json_format_proto); +} + grpc::string ProtoFileParser::GetTextFormatFromMethod( const grpc::string& method, const grpc::string& serialized_proto, bool is_request) { @@ -239,7 +251,18 @@ grpc::string ProtoFileParser::GetTextFormatFromMethod( return GetTextFormatFromMessageType(message_type_name, serialized_proto); } -grpc::string ProtoFileParser::GetSerializedProtoFromMessageType( +grpc::string ProtoFileParser::GetJsonFormatFromMethod( + const grpc::string& method, const grpc::string& serialized_proto, + bool is_request) { + has_error_ = false; + grpc::string message_type_name = GetMessageTypeFromMethod(method, is_request); + if (has_error_) { + return ""; + } + return GetJsonFormatFromMessageType(message_type_name, serialized_proto); +} + +grpc::string ProtoFileParser::GetSerializedProtoFromMessageTypeTextFormat( const grpc::string& message_type_name, const grpc::string& text_format_proto) { has_error_ = false; @@ -265,6 +288,32 @@ grpc::string ProtoFileParser::GetSerializedProtoFromMessageType( return serialized; } +grpc::string ProtoFileParser::GetSerializedProtoFromMessageTypeJsonFormat( + const grpc::string& message_type_name, + const grpc::string& json_format_proto) { + has_error_ = false; + grpc::string serialized; + const protobuf::Descriptor* desc = + desc_pool_->FindMessageTypeByName(message_type_name); + if (!desc) { + LogError("Message type not found"); + return ""; + } + std::unique_ptr msg( + dynamic_factory_->GetPrototype(desc)->New()); + + if (!grpc::protobuf::json::JsonStringToMessage(json_format_proto, msg.get()).ok()) { + LogError("Failed to parse json format to proto."); + return ""; + } + bool ok = msg->SerializeToString(&serialized); + if (!ok) { + LogError("Failed to serialize proto."); + return ""; + } + return serialized; +} + grpc::string ProtoFileParser::GetTextFormatFromMessageType( const grpc::string& message_type_name, const grpc::string& serialized_proto) { @@ -289,6 +338,32 @@ grpc::string ProtoFileParser::GetTextFormatFromMessageType( return text_format; } +grpc::string ProtoFileParser::GetJsonFormatFromMessageType( + const grpc::string& message_type_name, + const grpc::string& serialized_proto) { + has_error_ = false; + const protobuf::Descriptor* desc = + desc_pool_->FindMessageTypeByName(message_type_name); + if (!desc) { + LogError("Message type not found"); + return ""; + } + std::unique_ptr msg( + dynamic_factory_->GetPrototype(desc)->New()); + if (!msg->ParseFromString(serialized_proto)) { + LogError("Failed to deserialize proto."); + return ""; + } + grpc::string json_format; + grpc::protobuf::json::JsonPrintOptions jsonPrintOptions; + jsonPrintOptions.add_whitespace = true; + if (!grpc::protobuf::json::MessageToJsonString(*msg.get(), &json_format, jsonPrintOptions).ok()) { + LogError("Failed to print proto message to json format"); + return ""; + } + return json_format; +} + void ProtoFileParser::LogError(const grpc::string& error_msg) { if (!error_msg.empty()) { std::cerr << error_msg << std::endl; diff --git a/test/cpp/util/proto_file_parser.h b/test/cpp/util/proto_file_parser.h index eb1d793c2b..d2b2bd753e 100644 --- a/test/cpp/util/proto_file_parser.h +++ b/test/cpp/util/proto_file_parser.h @@ -53,22 +53,38 @@ class ProtoFileParser { // used as the argument of Stub::Call() grpc::string GetFormattedMethodName(const grpc::string& method); - grpc::string GetSerializedProtoFromMethod( + grpc::string GetSerializedProtoFromMethodTextFormat( const grpc::string& method, const grpc::string& text_format_proto, bool is_request); + grpc::string GetSerializedProtoFromMethodJsonFormat( + const grpc::string& method, const grpc::string& json_format_proto, + bool is_request); + grpc::string GetTextFormatFromMethod(const grpc::string& method, const grpc::string& serialized_proto, bool is_request); - grpc::string GetSerializedProtoFromMessageType( + grpc::string GetJsonFormatFromMethod(const grpc::string& method, + const grpc::string& serialized_proto, + bool is_request); + + grpc::string GetSerializedProtoFromMessageTypeTextFormat( const grpc::string& message_type_name, const grpc::string& text_format_proto); + grpc::string GetSerializedProtoFromMessageTypeJsonFormat( + const grpc::string& message_type_name, + const grpc::string& json_format_proto); + grpc::string GetTextFormatFromMessageType( const grpc::string& message_type_name, const grpc::string& serialized_proto); + grpc::string GetJsonFormatFromMessageType( + const grpc::string& message_type_name, + const grpc::string& serialized_proto); + bool IsStreaming(const grpc::string& method, bool is_request); bool HasError() const { return has_error_; } -- cgit v1.2.3