Skip to content

Commit

Permalink
refactor: code generation works with new generator engine
Browse files Browse the repository at this point in the history
  • Loading branch information
daniel vladco committed Nov 30, 2020
1 parent dabc07a commit 3cf14e1
Show file tree
Hide file tree
Showing 85 changed files with 23,418 additions and 7,515 deletions.
50 changes: 31 additions & 19 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,19 +1,31 @@
.PHONY: install example

install:
go install github.com/golang/protobuf/protoc-gen-go
protoc --go_out=paths=source_relative:./pb -I=./pb ./pb/*.proto
go install ./protoc-gen-gql
go install ./protoc-gen-gogqlgen
go install ./protoc-gen-gqlgencfg

example:
protoc \
--go_out=plugins=grpc,paths=source_relative:. \
--gqlgencfg_out=paths=source_relative:. \
--gql_out=svcdir=true,paths=source_relative:. \
--gogqlgen_out=paths=source_relative,gogoimport=false:. \
-I=. -I=./example/ ./example/*.proto

example2:
protoc --go_out=plugins=grpc,paths=source_relative:. -I=. -I=./reflect/examples/optionsserver/pb/ ./reflect/examples/optionsserver/pb/*.proto
# Go tools dependencies
GO_TOOLS := \
google.golang.org/grpc/cmd/protoc-gen-go-grpc \
google.golang.org/protobuf/cmd/protoc-gen-go \
github.com/99designs/gqlgen \
./protoc-gen-gql \
./protoc-gen-gogql

GOPATH := $(shell go env GOPATH)

.PHONY: all
all: all-tools pb/graphql.pb.go

.PHONY: all-tools
all-tools: ${GOPATH}/bin/protoc go-tools

.PHONY: go-tools
go-tools: $(foreach l, ${GO_TOOLS}, ${GOPATH}/bin/$(notdir $(l)))

define LIST_RULE
${GOPATH}/bin/$(notdir $(1)): go.mod
go install $(1)
endef

$(foreach l, $(GO_TOOLS), $(eval $(call LIST_RULE, $(l) )))

pb/graphql.pb.go: ./pb/graphql.proto all-tools
protoc --go_out=paths=source_relative:. ./pb/graphql.proto

${GOPATH}/bin/protoc:
./scripts/install-protoc.sh
37 changes: 13 additions & 24 deletions Readme.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Protoc plugins for generating graphql schema
Protoc plugins for generating graphql schema and go graphql code

If you use micro-service architecture with grpc for backend and graphql api gateway for frontend you will find yourself
If you use micro-service architecture with grpc for back-end and graphql api gateway for front-end, you will find yourself
repeating a lot of code for translating from one transport layer to another (which many times may be a source of bugs)

This repository aims to simplify working with grpc trough protocol buffers and graphql by generating code.
Expand All @@ -10,8 +10,7 @@ Install:

```sh
go install github.com/danielvladco/go-proto-gql/protoc-gen-gql
go install github.com/danielvladco/go-proto-gql/protoc-gen-gogqlgen
go install github.com/danielvladco/go-proto-gql/protoc-gen-gqlgencfg
go install github.com/danielvladco/go-proto-gql/protoc-gen-gogql
```

Usage Examples:
Expand All @@ -23,7 +22,7 @@ export PATH=${PATH}:${GOPATH}/bin
```

---
`--gql_out` plugin will generate graphql files with extension .graphqls
`--gql_out` plugin will generate graphql files with extension `.graphqls`
rather than go code which means it can be further used for any other language or framework.

Example:
Expand All @@ -36,38 +35,28 @@ http://github.com/99designs/gqlgen plugin, and map all the generated go types wi
Luckily `--gqlgencfg_out` plugin does exactly this.

---
`--gqlgencfg_out` plugin generates yaml configs that can be used further by the http://github.com/99designs/gqlgen library.

Example:
```sh
protoc --gqlgencfg_out=paths=source_relative:. -I=. -I=./example/ ./example/*.proto
```

The generated go code will work fine if you don't have any `enum`s, `oneof`s or `map`s. For this purpose use `--gogqlgen_out` plugin.

---
`--gogqlgen_out` plugin generates generates methods for implementing
`--gogql_out` plugin generates methods for implementing
`github.com/99designs/gqlgen/graphql.Marshaler` and `github.com/99designs/gqlgen/graphql.Unmarshaler` interfaces. Now proto `enum`s, `oneof`s and `map`s will work fine with graphql.

This plugin also creates convenience methods that will implement generated by the `gqlgen` `MutationResolver` and `QueryResolver` interfaces.

NOTE: to generate with gogo import add `gogoimport=true` as a parameter

Example:
```sh
protoc --gogqlgen_out=gogoimport=false,paths=source_relative:. -I=. -I=./example/ ./example/*.proto
protoc --gogql_out=gogoimport=false,paths=source_relative:. -I=. -I=./example/ ./example/*.proto
```

---
See `/example` folder for more examples.

TODO:
- Create a better implementation of `map`s and `oneof`s.
- Add comments from proto to gql for documentation purposes.
- Add more documentation.
## Gateway (alpha)
A unified gateway is also possible. Right now a gateway can be spawn up
pointing to a list of grpc endpoints (grpc reflection must be enabled on the grpc servers).
The gateway will query the servers for protobuf descriptors and generate a graphql schema abstract tree.
The requests to the gateway will be transformed on the fly to grpc servers without any additional code generation
or writing any code at all. See `examples/gateway` for usage more info.

## Community:
I am one on this. Will be very glad for any contributions so feel free to create issues and forks.
Will be very glad for any contributions so feel free to create issues, forks and PRs.

## License:

Expand Down
11 changes: 5 additions & 6 deletions reflect/gateway/server.go → cmd/gateway/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@ import (
"context"
"encoding/json"
"flag"
"github.com/danielvladco/go-proto-gql/reflect/gateway/internal/generator"
"log"
"net/http"
"os"

"github.com/danielvladco/go-proto-gql/reflect/gateway/internal/server"
"github.com/mitchellh/mapstructure"
"github.com/nautilus/gateway"
"github.com/nautilus/graphql"

"github.com/danielvladco/go-proto-gql/pkg/generator"
"github.com/danielvladco/go-proto-gql/pkg/server"
)

type Config struct {
Expand Down Expand Up @@ -46,16 +47,14 @@ func main() {
log.Fatal(err)
}

//gqlDesc := server.GenerateGQLComponents(res, descs)
gqlDesc, err := generator.NewSchemas(descs, true)
gqlDesc, err := generator.NewSchemas(descs, true, true)
fatalOnErr(err)

repo := generator.NewInmemRepository(gqlDesc)
repo := generator.NewRegistry(gqlDesc)

queryFactory := gateway.QueryerFactory(func(ctx *gateway.PlanningContext, url string) graphql.Queryer {
return server.QueryerLogger{server.NewQueryer(repo, caller)}
})

sources := []*graphql.RemoteSchema{{URL: "url1"}}
sources[0].Schema = gqlDesc.AsGraphql()[0]

Expand Down
94 changes: 94 additions & 0 deletions cmd/protogql/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package main

import (
"flag"
"log"
"os"
"path"
"path/filepath"
"strings"

"github.com/jhump/protoreflect/desc/protoparse"
"github.com/vektah/gqlparser/v2/formatter"

"github.com/danielvladco/go-proto-gql/pkg/generator"
)

type arrayFlags []string

func (i *arrayFlags) String() string {
return "str list"
}

func (i *arrayFlags) Set(value string) error {
*i = append(*i, value)
return nil
}

var (
importPath = arrayFlags{}
fileNames = arrayFlags{}
svc = flag.Bool("svc", false, "")
merge = flag.Bool("merge", false, "")
)

func main() {
flag.Var(&importPath, "I", "path")
flag.Var(&fileNames, "f", "path")
flag.Parse()

newFileNames, err := protoparse.ResolveFilenames(importPath, fileNames...)
if err != nil {
log.Fatal(err)
}
descs, err := protoparse.Parser{ImportPaths: importPath}.ParseFiles(newFileNames...)
if err != nil {
log.Fatal(err)
}
gqlDesc, err := generator.NewSchemas(descs, *merge, *svc)
if err != nil {
log.Fatal(err)
}
for _, schema := range gqlDesc {
if len(schema.FileDescriptors) < 1 {
log.Fatalf("unexpected number of proto descriptors: %d for gql schema", len(schema.FileDescriptors))
}
if len(schema.FileDescriptors) > 1 {
if err := generateFile(schema, true); err != nil {
log.Fatal(err)
}
break
}
if err := generateFile(schema, *merge); err != nil {
log.Fatal(err)
}
}
}

func generateFile(schema *generator.SchemaDescriptor, merge bool) error {
sc, err := os.Create(resolveGraphqlFilename(schema.FileDescriptors[0].GetName(), merge))
if err != nil {
return err
}
defer sc.Close()

formatter.NewFormatter(sc).FormatSchema(schema.AsGraphql())
return nil
}

func resolveGraphqlFilename(protoFileName string, merge bool) string {
if merge {
gqlFileName := "schema.graphqls"
absProtoFileName, err := filepath.Abs(protoFileName)
if err == nil {
protoDirSlice := strings.Split(filepath.Dir(absProtoFileName), string(filepath.Separator))
if len(protoDirSlice) > 0 {
gqlFileName = protoDirSlice[len(protoDirSlice)-1] + ".graphqls"
}
}
protoDir, _ := path.Split(protoFileName)
return path.Join(protoDir, gqlFileName)
}

return strings.TrimSuffix(protoFileName, path.Ext(protoFileName)) + ".graphqls"
}
22 changes: 22 additions & 0 deletions example/codegen/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
.PHONY: generate start

start: generate
go run ./main.go

generate: gql/constructs/generated.go gql/options/generated.go

pb/%.gqlgen.pb.go: pb/%.proto pb/%_grpc.pb.go pb/%.pb.go
protoc --gogql_out=paths=source_relative:. -I . -I ../../ ./pb/$*.proto

pb/%_grpc.pb.go: pb/%.proto pb/%.pb.go
protoc --go-grpc_out=paths=source_relative:. -I . -I ../../ ./pb/$*.proto

pb/%.pb.go: pb/%.proto
protoc --go_out=paths=source_relative:. -I . -I ../../ ./pb/$*.proto

pb/%.graphqls: pb/%.proto
protoc --gql_out=svc=true:. -I . -I ../../ ./pb/$*.proto

gql/%/generated.go: pb/%.graphqls pb/%.gqlgen.pb.go
gqlgen --config ./gqlgen-$*.yaml

Loading

0 comments on commit 3cf14e1

Please sign in to comment.