Coder Social home page Coder Social logo

go2gql's Introduction

go2gql

Build Status Coverage Status

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
}
  1. reading config
  2. plugins initialization ( calling Init() method of each plugin )
  3. plugins preparation ( calling Prepare() method of each plugin )
  4. 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
  • ServiceNameMutations or ServiceAliasMutations

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 ServiceAliasMutations

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.

go2gql's People

Contributors

cheshir avatar damour avatar saturn4er avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

go2gql's Issues

Proto2gql: Implement bytes scalartype

Hello,

When I tried to gen go code I had the following error.

panic: failed to prepare generator: failed to prepare plugin proto2gql:
  failed to prepare file for generation: failed to prepare file input objects:
  failed to resolve field type: failed to get input type resolver: unimplemented scalar type: bytes  goroutine 1 [running]:
  main.main.func1(0xc4200c8000, 0xc4200c8000, 0x4)
  /Users/godefroy/.go/src/github.com/EGT-Ukraine/go2gql/cmd/go2gql/main.go:55 +0x35b
  github.com/urfave/cli.(*App).Run(0xc4203f4000, 0xc4200a8090, 0x3, 0x3, 0x0, 0x0)
  /Users/godefroy/.go/src/github.com/urfave/cli/app.go:244 +0x738 main.main()
  /Users/godefroy/.go/src/github.com/EGT-Ukraine/go2gql/cmd/go2gql/main.go:100 +0x38d

Then I tried to fix it in #1
Can you please review it and say me if I did it well.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.