Jig lets you use jsonnet to implement gRPC methods, e.g.:
// Greeter.Hello
function(input) {
response: {
greeting: '💃 : Hello ' + input.request.firstName,
},
}
The jsonnet method definitions can be updated in real time without restarting jig.
Jig uses protobuf FileDescriptorSets
, generated by protoc
into .pb
files,
as a description of valid gRPC method names, request and response types. Jig
does not need any further pre-generated or pre-compiled code.
Generate a FileDescriptorSet for the services to stub with:
protoc --descriptor_set_out service.pb --include_imports service.proto
Put jsonnet method definitions together in a directory, each file named
<pkg>.<service>.<method>.jsonnet
. The jsonnet file is (re-)evaluated when the
gRPC server receives a call to that method.
You can generate skeleton jsonnet method definitions using jig bones
:
jig bones --proto-set=dir/service.pb --method-dir=dir
Request protobuf messages are marshaled to JSON and passed to the jsonnet method
definition function as the input
parameter. If the method is a unary,
server-streaming or bidirectional streaming method, the request message is
placed in the request
field of input
:
{
request: { ...json-encoded gRPC request protobuf... }
}
If the method is a client-streaming method, the stream of request messages is
placed in the stream
field of input
as an array:
{
stream: [ {request1}, {request2}, ...]
}
For bidirectional streaming methods, the jsonnet method definition is evaluated
once for each message on the request stream (with a single message in the
request
field). Once EOF
has been received on the request stream, the
jsonnet method definition is evaluated one more time with the request
field
set to null
.
Response protobuf messages are unmarshaled from the jsonnet evaluation of the method definition. The result must evaluate as an object with fields describing the response to send back to the gRPC client.
If the method is a unary or client-streaming method, the result must have a
response
field that contains the response message encoded as JSON:
function(input) {
response: { ...json-encoded gRPC response protobuf... }
}
If the method is a server- or bidirectional streaming method, the result must
have a stream
field that contains an array of response messages encoded as
JSON:
function(input) {
stream: [ {response1}, {response2}, ... ]
}
A gRPC status can be returned in the status
field:
function(input) {
status: {
code: 3,
message: 'Field "foo" failed validation: 0 < foo < 10',
details: [
{
'@type': 'type.googleapis.com/google.protobuf.Duration',
value: '15.2s',
},
],
},
}
If a result has a status
field, it must not have a response
or stream
field.
The response can reference fields of the input using regular jsonnet references. See the testdata samples.
The request
and response
fields are encoded from/to protobuf messages
according to the protojson encoding rules.
To serve these jsonnet methods, run:
jig serve <dir>
Build and start jig on the test data:
. ./bin/activate-hermit
make install
jig serve --http serve/testdata/greet
in a second terminal call it with:
client world
To see streaming, call it with:
client --stream=server world
client --stream=client you me world
client --stream=bidi you me world
The --http
flag passed to jig serve
above allows you to make HTTP requests:
curl \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{"firstName": "Kitty"}' \
localhost:8080/api/greet/hello
Experiment with the jsonnet method files in the testdata directory.
Alternatively there is a traditional, generated gRPC server that the same client can interact with. Start it with:
server
To get started on writing a jsonnet method definition, the jig bones
subcommand generates a skeleton showing the input and output forms of a method.
To see a message with all the different types of message fields:
jig bones --proto-set pb/exemplar/exemplar.pb
To see the structure of the different method streaming types:
jig bones --proto-set pb/greet/greeter.pb
. ./bin/activate-hermit
make ci
Run make help
for help on other make targets.