Skip to content

Conversation

@peteboothroyd
Copy link

JSON Output

  • Adding JSON option to list command to allow easy parsing of output.
  • Altering with_message output to be json which can be copy and pasted straight into a call command.
  • Now printing response message template as this may be useful for users to determine which service they want to call.
  • Updating tests to reflect this update.

@@ -0,0 +1,20 @@
package me.dinowernli.grpc.polyglot.command;

public class Method {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need to define these custom types (here and services below)?

Can we not just use the methoddescriptor produced by protobuf?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, we could hard code the logic to print straight to JSON from the protobuf descriptors. I think though that if there is going to be more than one output format option that it will be much easier to use pre-existing tools to process the java object, eg using gson takes one line to go to JSON, I imagine there will be other options to go to other formats just as easily.

Copy link
Member

@dinowernli dinowernli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

General approach looks sound, but I don't think we should be introducing those mutable types for service and method.


if (jsonOutput.isPresent() && jsonOutput.get()) {
Gson gson = new Gson();
output.writeLine(gson.toJson(services));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed above, can we not just json-print the descriptors rather than converting them to our custom data type?

Copy link
Author

@peteboothroyd peteboothroyd Aug 7, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I looked into using the JSON printing but when I tried this it looked like it would take more work. The JSON printed did not resolve all the way down to the primitive level as the as the output currently does. Say a proto message is defined:

"foo": {
    "name": "STRING",
    "id": "INT"
}

The JSON output would be:

"foo": "Foo"

This doesn't give enough information to people to allow them to construct their own messages.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's what you get when you print a message descriptor. What you actually want is the service descriptor [1]. That should get you the service name, the method name, and the type name for the messages. Service descriptors are already conveniently returns by listServices()

[1] https://github.com/google/protobuf/blob/master/src/google/protobuf/descriptor.proto#L230


//TODO: Move to a "list_services"-specific flag container
@Option(
name = "--json_output",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we have this be a --list_output_format and have "json" be one of the valid flag values?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure this should be pretty easy and will be more extensible in the future

import com.google.protobuf.DescriptorProtos.FileDescriptorSet;
import me.dinowernli.grpc.polyglot.command.ServiceCall;
import me.dinowernli.grpc.polyglot.command.ServiceList;
//import me.dinowernli.grpc.polyglot.command.Server;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spurious comment


if (jsonOutput.isPresent() && jsonOutput.get()) {
Gson gson = new Gson();
output.writeLine(gson.toJson(services));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's what you get when you print a message descriptor. What you actually want is the service descriptor [1]. That should get you the service name, the method name, and the type name for the messages. Service descriptors are already conveniently returns by listServices()

[1] https://github.com/google/protobuf/blob/master/src/google/protobuf/descriptor.proto#L230

@peteboothroyd
Copy link
Author

I've updated with your suggestions. The merging process went a little awry on my end, the main changes are in the ServiceList, Main, and CommandLineArgs.

Changes:

  • There is now a list_output_format flag with two options, json or the default readable option
  • Tests updated and added for command line argument parsing
  • Listing the methods now reports true/false for client & server streaming
  • Tests updated for ServiceList

@googlebot
Copy link

Thanks for your pull request. It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

📝 Please visit https://cla.developers.google.com/ to sign.

Once you've signed, please reply here (e.g. I signed it!) and we'll verify. Thanks.


  • If you've already signed a CLA, it's possible we don't have your GitHub username or you're using a different email address. Check your existing CLA data and verify that your email is set on your git commits.
  • If your company signed a CLA, they designated a Point of Contact who decides which employees are authorized to participate. You may need to contact the Point of Contact for your company and ask to be added to the group of authorized contributors. If you don't know who your Point of Contact is, direct the project maintainer to go/cla#troubleshoot.
  • In order to pass this check, please resolve this problem and have the pull request author add another comment and the bot will run again.

@peteboothroyd
Copy link
Author

peteboothroyd commented Aug 17, 2017 via email

@googlebot
Copy link

CLAs look good, thanks!

!serviceFilter.isPresent()
|| descriptor.getFullName().toLowerCase().contains(serviceFilter.get().toLowerCase());

if (matchingDescriptor) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Almost there :) Any chance we can just print the json of descriptor.toProto()? That should give you the information you need and avoid manually constructing json output.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could try but will need some help. As I think I mentioned before as far as I can tell using the JsonPrinters do not do what we need. eg.
printing:String jsonFormat = JsonFormat.printer().print(descriptor.toProto().toBuilder());
Returns something similar to:
{ "name": "TestMethod", "inputType": ".polyglot.test.TestRequest", "outputType": ".polyglot.test.TestResponse", "options": {} }
There is not enough information about the types. I imagine you could recursively resolve every type which is more than one level deep to get down to the primitive level of strings, int etc. but this will require a decent change to the code.
I think this approach is the simplest method given what's already written but if you know a better way then let me know.


for (MethodDescriptor method : descriptor.getMethods()) {
if (!methodFilter.isPresent() || method.getName().contains(methodFilter.get())) {

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, you should be able to just json-format the method protos.

@peteboothroyd
Copy link
Author

peteboothroyd commented Sep 5, 2017

Changes

  • JSON output now prints out the service protos, and dependency file protos.
  • JSON output structure is
{
  serviceProtos: [//list of serviceDescriptorProtos],
  fileProtos: [//list of fileDescriptorProtos]
}

See src/test/java/me/dinowernli/grpc/polyglot/command/jsonOutputGold.txt for more illustrative example

  • MessageWriter can use custom separator string.
  • Separator string only used in between messages.

Notes

  • Services are filtered according to the serviceFilter flag, but the methodFilter flag is ignored for json output

.orElseThrow(() -> new IllegalArgumentException("Could not find named config: " + name));
.filter(config -> config.getName().equals(name))
.findAny()
.orElseThrow(() -> new IllegalArgumentException("Could not find named config: " + name));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: indentation here was correct, please revert

try {
output.write(jsonPrinter.print(message) + MESSAGE_SEPARATOR);
String outputString = jsonPrinter.print(message);
// Only separate messages if there are more than one
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't actually think this is the right thing to do. To make things easier to parse, I believe we alway want to append a separator, even if there is nothing else coming. Can discuss in person.

*/
public static <T extends Message> MessageWriter<T> create(Output output) {
return new MessageWriter<T>(JsonFormat.printer(), output);
public static <T extends Message> MessageWriter<T> create(Output output, String messageSeparator) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's have two factory methods, one called create(), and the other called createWithSeparator().

That way, there is a sensible default and we don't need the ServiceCall class to understand how these separators work.

.addFile(TestProto.getDescriptor().toProto())
.addFile(FooProto.getDescriptor().toProto())
.build();
.addFile(TestProto.getDescriptor().toProto())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indentation was correct, please revert (thoughout most of this file)


// @Test
// public void testServiceListOutputWithMessageDetailJsonFormatted() throws Throwable {
// ServiceList.listServices(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please remove this commented-out test

}

private static void printJsonOutput(Output output, ServiceResolver serviceResolver, Optional<String> serviceFilter) {
output.writeLine("{ \"serviceProtos\": [");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably not be constructing json ourselves here. If we want to wrap the existing protobuf output, we should define a proto which wraps the messages we need and print that a single time (this would also obviate the need for changing the messageprinter).

@peteboothroyd
Copy link
Author

Ok the things you mentioned should be addressed now. Formatting is almost definitely not correct, can you advise of an automated way to share whatever settings you have as doing this manually is soul destroying.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants