go2gql
![Coverage Status](https://coveralls.io/repos/github/EGT-Ukraine/go2gql/badge.svg?branch=master)
Tool, which generates GraphQL Schema for graphql-go package based on external data sources
Installation
$ go get github.com/EGT-Ukraine/go2gql/cmd/go2gql
Usage
$ ./go2gql -c "<config path>"
Generation process
Plugins
The generation process is built around plugins. Plugin is a go type that implements interface
type Plugin interface {
Init(*GenerateConfig, []Plugin) error
Prepare() error
Name() string
PrintInfo(info Infos)
Infos() map[string]string
Generate() error
}
- reading config
- plugins initialization ( calling Init() method of each plugin )
- plugins preparation ( calling Prepare() method of each plugin )
- plugins generation ( calling Generate() method of each plugin )
Default plugins
Default plugins places in ./generator/plugins
graphql
plugin
graphql
generates:
- GraphQL Schema based on config and relying on data sent to him from other packages
- GraphQL types and resolvers relying on data sent to him from other packages
config example
Here's a simple config example of how to generate such GraphQL schema
type Query {
fieldA FieldA
}
type FieldA {
srvField SomeServiceObj
}
type SomeServiceObj {
... SomeService methods
}
type Mutation {
... SomeServiceMutations methods
}
...
...
graphql_schemas:
- name: "ExampleSchema" # Schema name
output_path: "./services_api/schema/auth.go" # Where to put schema code
output_package: "schema" # Package name
queries:
type: "OBJECT"
fields:
- field: "fieldA"
type: "OBJECT" # Field type (OBJECT|SERVICE)
object_name: "FieldA"
fields:
- field: "srvField"
object_name: "SomeServiceObj"
service: "SomeService" # Service name
type: "SERVICE"
exclude_methods:
- "excluded_method_1"
filter_methods:
- "excluded_method_1"
- "excluded_method_3"
mutations:
# similary to queries
type: "SERVICE"
service: "SomeServiceMutations"
...
proto2gql
plugin
proto2gql
plugin parses .proto files, defined in config and pass them to graphql
plugin.
Service names
For each service of each proto file, proto2gql
adds two services to graphql
plugins with names:
ServiceName
or ServiceAlias
ServiceName
Mutations or ServiceAlias
Mutations
Config example
...
...
proto2gql:
output_path: "./proto_out/" # path, where to put generated code
paths: # path, where parser will search for imports
- "./vendor"
- "$GOPATH/src"
import_aliases: # imports aliases for all files
- google/protobuf/descriptor.proto: "github.com/golang/protobuf/protoc-gen-go/descriptor/descriptor.proto"
- google/protobuf/timestamp.proto: "github.com/golang/protobuf/ptypes/timestamp/timestamp.proto"
- google/protobuf/empty.proto: "github.com/golang/protobuf/ptypes/empty/empty.proto"
files:
# If you want to add some settings to imported .proto file, just add it here
- name: "Name" # name of .proto file
proto_path: "./a.proto" # path to .proto file
output_path: "./schema/name" # file-specific path, where to put generated code
proto_go_package: "github.com/gogo/protobuf/types" # Go package of .proto file grpc client (which was previously generated by `protoc`)
gql_messages_prefix: "Msg" # prefix, which will be added to all generated GraphQL Messages(including maps)
gql_enums_prefix: "Enm" # prefix, which will be added to all generated GraphQL Enums
paths: # file specific path, where parser will search for imports
- "./a_proto_deps/"
imports_aiases: # file specific imports aliases
- google/protobuf/timestamp.proto: "github.com/gogo/protobuf/protobuf/google/protobuf/timestamp.proto"
services: # services settings
"serviceName": # service name
service_name: "someAlias" # service name alias
methods:
"methodName": # method name
alias: "methodAlias"
request_type: "QUERY" # method type in GraphQL Schema (QUERY|MUTATION)
messages: # messages settings
- "Request$": # message name match regex
unwrap_field: true # unpack input message field. Useful for google.protobuf.wrappers.
fields:
"ip_address":
context_key: "ip" # context key, where unmarshaller will get this field from
- "Response$":
unwrap_field: true # In proto we can't use primitive or repeated type in method response.
# If unwrap_field = true unpack response gql object with 1 field.
error_field: "Error" # name of payload error field
- ...
- ...
...
...
swagger2gql
plugin
proto2gql
plugin parses swagger files, defined in config and pass them to graphql
plugin.
Service names
For each tag of each swagger file, proto2gql
adds two services to graphql
plugins with names:
pascalized(tag-name)
or ServiceAlias
pascalized(tag-name)
Mutations or ServiceAlias
Mutations
pascalized() here means, that it converts string to CamelCase format and removes all characters that is not valid in GraphQL Object name
Config example
...
...
swagger2gql:
output_path: "./swagger_out/" # Path, where to put generated code
objects: # GraphQL objects configs
- "Response$": # Object name
error_field: "error" # name of payload error field
- "InputParams$":
fields: # Object fields config
ip: # field name
context_key: "user_ip" # context key, where unmarshaller will get this field from
files:
- name: "Swagger file number 1" # swagger file name
path: "./service1/swagger.json" # path to swagger file
models_go_path: "github.com/myproject/service1/client/models" # path to generated using goswagger models
output_pkg: "gql_service1" # output go package name
output_path: "./service1/schema/" # file-specific path, where to put generated code
gql_objects_prefix: "Srv1" # prefix, which will be added to all generated GraphQL Objects
tags: # tags settings
"some-swagger-tag": # tag name
client_go_package: "github.com/myproject/service1/client/some_swagger_tag/client" # go client package
service_name: "SomeSwaggerTag" # service name alias
methods: # tag method settings
"/somes": # request path
get: # request method (get/post/put/options...)
alias: "get"
request_type: "QUERY" # request type (QUERY|MUTATIONS)
params_config: # file specific object parameters settings
- param_name: "user_id"
context_key: "user_id"
objects: # file specific objects settings
- "Request$":
fields:
"user_id":
context_key: "user_id"
...
...
Simple plugin example
Here's a simple plugin, that gets access to graphql plugin. Fetches information about services and their methods, and then, render them to .yml file
package main
import (
"os"
"github.com/pkg/errors"
"github.com/EGT-Ukraine/go2gql/generator"
"github.com/EGT-Ukraine/go2gql/generator/plugins/graphql"
)
const Name = "gql_services_rules"
type plugin struct {
gqlPlugin *graphql.Plugin
}
func (p *plugin) Init(_ *generator.GenerateConfig, plugins []generator.Plugin) error {
for _, plugin := range plugins {
if g, ok := plugin.(*graphql.Plugin); ok {
p.gqlPlugin = g
return nil
}
}
return errors.New("graphql plugin was not found")
}
func (p plugin) Generate() error {
file, err := os.OpenFile("./services_access.yml", os.O_TRUNC|os.O_WRONLY, 0666)
if err != nil {
return errors.Wrap(err, "failed to open services_access.yml")
}
defer file.Close()
for _, typesFile := range p.gqlPlugin.Types() {
for _, service := range typesFile.Services {
if len(service.QueryMethods) == 0 {
continue
}
file.WriteString(service.Name + ":\n")
for _, method := range service.QueryMethods {
file.WriteString(" " + method.Name + ":\n")
}
}
}
return nil
}
func (plugin) Name() string { return Name }
func (plugin) Prepare() error { return nil }
func (plugin) PrintInfo(info generator.Infos) {}
func (plugin) Infos() map[string]string { return nil }
func Plugin() generator.Plugin {
return new(plugin)
}
func main(){}
Dataloader support
Config example:
data_loaders:
output_path: "./generated/schema/loaders/"
proto2gql:
output_path: "./generated/schema"
paths:
- "vendor"
- "$GOPATH/src"
imports_aliases:
- google/protobuf/empty.proto: "github.com/golang/protobuf/ptypes/empty/empty.proto"
files:
- proto_path: "./apis/reviews.proto"
services:
ItemsReviewService:
methods:
List:
data_loaders:
ItemReviewsByIDs:
request_field: "filter.item_ids"
result_field: "reviews"
match_field: "item_id"
type: "1-N" # Relation type (1-N|1-1)
wait_duration: 1h1m1s ( WARNING!! this is only example, use some small duration)
- proto_path: "./apis/category.proto"
services:
CategoryService:
methods:
List:
data_loaders:
CategoriesByIDs:
request_field: "filter.ids"
result_field: "categories"
match_field: "id"
type: "1-1"
wait_duration: 1s
- proto_path: "./apis/user.proto"
services:
UserService:
methods:
List:
data_loaders:
UsersByIDs:
request_field: "filter.ids"
result_field: "users"
match_field: "id"
type: "1-1"
wait_duration: 1s
- proto_path: "./apis/items.proto"
services:
ItemsService:
methods:
List:
alias: "list"
request_type: "QUERY"
messages:
- "Item$":
data_loaders:
- field_name: "category"
key_field_name: "category_id"
data_loader_name: "CategoriesByIDs"
- field_name: "comments"
key_field_name: "id"
data_loader_name: "CommentsLoader"
- field_name: "reviews"
key_field_name: "id"
data_loader_name: "ItemReviewsByIDs"
swagger2gql:
output_path: "./generated/schema"
files:
- name: "Comments"
path: "apis/swagger.json"
models_go_path: "github.com/EGT-Ukraine/go2gql/tests/dataloader/generated/clients/models"
tags:
"comments-controller":
service_name: "CommentsService"
client_go_package: "github.com/EGT-Ukraine/go2gql/tests/dataloader/generated/clients/client/comments_controller"
methods:
"/items/comments/":
post:
data_loader_provider:
name: "CommentsLoader"
wait_duration_ms: 5
objects:
- "ItemComment$":
data_loaders:
- field_name: "user"
key_field_name: "user_id"
data_loader_name: "UserService"
graphql_schemas:
- name: "API"
output_path: "./generated/schema/api.go"
output_package: "schema"
queries:
type: "OBJECT"
fields:
- field: "items"
object_name: "Items"
service: "ItemsService"
type: "SERVICE"
Default wait duration 10ms.
Full example can be found in tests.
Note to users migrating from older releases
Migrating from 1.x to 2.0
Swagger plugin
service_name
config field is removed.
Use queries_service_name
& mutations_service_name
instead.
Proto plugin
alias
config field for services is removed.
Use queries_service_name
& mutations_service_name
instead.
Migrating from 2.x to 3.0
Swagger plugin
queries_service_name
& mutations_service_name
config fields is removed.
Use service_name
instead.
Proto plugin
queries_service_name
& mutations_service_name
config fields is removed.
Use service_name
instead.
Migrating from 3.x to 4.0
tracer.Tracer was replaced with opentracing.Tracer
Schema initialization before:
import "github.com/EGT-Ukraine/go2gql/tracer"
var tracer tracer.Tracer
sch, err := schema.GetAPISchema(schemaApiClientsFactory.GetAPIClients(), schemaApiInterceptor, tracer)
now:
import opentracing_go "github.com/opentracing/opentracing-go"
var tracer opentracing_go.Tracer
sch, err := schema.GetAPISchema(schemaApiClientsFactory.GetAPIClients(), schemaApiInterceptor, tracer)
Migrating from 4.x to 5.0
github.com/saturn4er/graphql
dependency was replaced with original github.com/graphql-go/graphql
package.
Migrating from 5.x to 6.0
Implicit service & method registration in proto plugin disabled.
For now you must declare each proto service & method in config.