foxygoat / jig

gRPC mocks with Jsonnet
Apache License 2.0
13 stars 3 forks source link

gRPC mocking with ~pony~ jig #1

Closed juliaogris closed 2 years ago

juliaogris commented 2 years ago

Notes from brainstorming session. PXL_20211126_092657633

juliaogris commented 2 years ago

Cam's poc generating jsonnet from protos:

package main
​
import (
    "fmt"
    "log"
    "os"
    "text/template"
​
    "google.golang.org/protobuf/proto"
    "google.golang.org/protobuf/reflect/protodesc"
    "google.golang.org/protobuf/reflect/protoreflect"
    "google.golang.org/protobuf/types/descriptorpb"
)
​
func main() {
    if len(os.Args) == 1 {
        log.Fatal("Missing file argument\n")
    } else if len(os.Args) > 2 {
        log.Fatal("Too many file arguments\n")
    }
    b, err := os.ReadFile(os.Args[1])
    if err != nil {
        log.Fatal(err)
    }
    fds := &descriptorpb.FileDescriptorSet{}
    if err := proto.Unmarshal(b, fds); err != nil {
        log.Fatal(err)
    }
    files, err := protodesc.NewFiles(fds)
    if err != nil {
        log.Fatal(err)
    }
    files.RangeFiles(printMethods)
}
​
func printMethods(fd protoreflect.FileDescriptor) bool {
    sds := fd.Services()
    for i := 0; i < sds.Len(); i++ {
        sd := sds.Get(i)
        mds := sd.Methods()
        for j := 0; j < mds.Len(); j++ {
            printResponseJsonnet(mds.Get(j))
        }
    }
    return true
}
​
func printResponseJsonnet(md protoreflect.MethodDescriptor) {
    err := tmpl.Execute(os.Stdout, md)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
    }
}
​
var tmplStr = `
// {{.ParentFile.Package}}.{{.Parent.Name}}.{{.Name}}
function(input) {
  response: {
  {{- range (fields .Output.Fields) }}
    {{ .JSONName }}:
  {{ end -}}
  },
  error: null,
}
​
`
​
var tmpl = template.Must(template.New("").Funcs(map[string]interface{}{"fields": fields}).Parse(tmplStr[1:]))
​
func fields(flds protoreflect.FieldDescriptors) []protoreflect.FieldDescriptor {
    result := make([]protoreflect.FieldDescriptor, flds.Len())
    for i := 0; i < flds.Len(); i++ {
        result[i] = flds.Get(i)
    }
    return result
}
camh- commented 2 years ago

image

camh- commented 2 years ago

suggested jsonnet structure:

input = {
  request: ...
  metadata: ...
}

output = {
  response: ...
  error: ...
  metadata: ... ?
}

Thinking about the original pony, it could proxy to another service, doing data translation in the middle. It would be nice to be able to do this, in which case the output would need to be more flexible. For example:

output = {
  respond: {
    message: {...} | error: {...}
    metadata: {...}
  }
}

or

output = {
  call: {
    address: foo.com:1234
    method: pkg.svc.method
    input: {
      message: {...}
      metadata: {...}
    }
  }
}

Not suggesting this right away, but we should consider the output format to be able to make it possible.

juliaogris commented 2 years ago

pony grpc mocking would be run as:

pony servegrpc -p proto.pb [-m method_dir]

with method_dir defaulting to .

The method dir has got a jsonnet file each for a method to be mocked as:

<pkg>.<service>.<method>.jsonnet 
juliaogris commented 2 years ago

Well that got done quicker than expected. Thank you @camh-