Interview Assignment
Overview
You assignment is to create a Protocol Buffers (Protobuf) plugin, which is executed with the protoc
compiler. In this exercise, we expect you to use v3.13.0
, so please install it from the the releases.
We expect this assignment to take between 2-3 hours to complete. A significant portion of this assignment is to test your ability to research and learn how to interact with a foreign tool. The implementation itself is not intended to be overly complex - the real challenge is getting a program that can run correctly with protoc
.
What is a plugin, and how do I write one?
A protoc plugin allows users to generate custom code according to the structured representation of Protobuf files. In simpler terms, plugins are programs that take Protobuf files as input, and produce code as output.
As you'll notice in some of the additional plugin documentation, the plugin must either be discoverable by your machine's PATH
, or be manually targeted with the --plugin
flag. In this case, we recommend placing the plugin on your PATH
so that you can quickly go install
between test invocations, but this is up to you.
For references, there are many examples of plugins you can find on the web.
What does the plugin need to do?
The plugin must be implemented in Go.
The plugin must output one YAML file for each of the input files specified in the protoc
invocation.
The YAML file must conform to the following constraints:
- The plugin is named
protoc-gen-yaml
. - The plugin outputs one YAML file for every input file.
- Each of the files should be named by adding the
.yaml
suffix to their filename.- For example, an input file named
proto/echo.proto
will createproto/echo.proto.yaml
.
- For example, an input file named
- The YAML output will represent a subset of the Protobuf file's content. Specifically, the plugin will create two
maps
in each file: one formessages
and another forservices
. Each map key have alist
ofmessages
andservices
, respectively. - For each message, a
list
of fields will be represented under thefields
key. - For each method, a
list
of methods will be represented under themethods
key.
A template representation of the expected output is shown below. Pay close attention to the names of the map keys, as well as the required values associated with them.
If your are confused at all by any of these definitions, such as a
field
number, please refer to the Language Guide. The Testing section below gives an example that will make it easier to understand.
messages:
# For each message in this file, sorted by name...
- name: <$message_name>
fields:
# For each field in this message, sorted by number...
- name: <$field_name>
number: <$field_number>
services:
# For each service in this file, sorted by name...
- name: <$service_name>
# For each method in this service, sorted by name...
methods:
- name: <$method_name>
input_type: <$input_type>
output_type: <$output_type>
Hint: Use
Marshal
from gopkg.in/yaml.v2 instead of using gopkg.in/yaml.v3 to get the correct indentation.
Testing
The following file located in test/input/proto/echo.proto
.
syntax = "proto3";
package echo.v1;
message EchoRequest {
string value = 1;
}
message EchoResponse {
Foo foo = 1;
}
message Foo {
message Bar {
string value = 1;
}
string two = 2;
string one = 1;
}
service EchoService {
rpc Echo(EchoRequest) returns (EchoResponse);
}
Assuming that your plugin is installed in PATH
, we should be able to execute the plugin like so:
$ protoc -I test/input test/input/proto/echo.proto --yaml_out=./output
The expected output for the test/input/proto/echo.proto
file is shown below:
messages:
- name: echo.v1.EchoRequest
fields:
- name: value
number: 1
- name: echo.v1.EchoResponse
fields:
- name: foo
number: 1
- name: echo.v1.Foo
fields:
- name: one
number: 1
- name: two
number: 2
- name: echo.v1.Foo.Bar
fields:
- name: value
number: 1
services:
- name: echo.v1.EchoService
methods:
- name: Echo
input_type: echo.v1.EchoRequest
output_type: echo.v1.EchoResponse
You can verify if you've implemented the correct solution with diff
:
$ protoc -I test/input test/input/proto/echo.proto --yaml_out=./output
$ diff ./output/proto/echo.proto.yaml ./test/output/proto/echo.proto.yaml
Note that the ouptut is sanitized, such that any
.
prefixes are trimmed in the relevant places. A proper solution will need to handle these cases, so pay special attention!
Submitting
Send a tar ball containing your solution and, optionally, any additional test cases you've created. We should be able to build the plugin with go build
.