Compare commits
	
		
			14 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 022e6a5b62 | |||
| 6d17a74ffb | |||
| 55fa1ee403 | |||
| f22d8ed055 | |||
| 3806fb9336 | |||
| a0385eff9c | |||
| 0f957b897b | |||
| 9bad6b85f3 | |||
| 9cc9e19e0b | |||
| 93e5f2ee90 | |||
| 255a9712dc | |||
| 7a1be4f903 | |||
| 489c58fae7 | |||
| 25ad4ed647 | 
@@ -23,4 +23,4 @@ $> protoc --go_micro_out=debug=true,components="micro|http":. input.proto
 | 
			
		||||
 | 
			
		||||
* Install the **go** compiler and tools from https://golang.org/doc/install
 | 
			
		||||
* Install **protoc-gen-go**: `go install google.golang.org/protobuf/cmd/protoc-gen-go`
 | 
			
		||||
* Install **protoc-gen-go-micro**: `go install go.unistack.org/protoc-gen-go-micro/v4`
 | 
			
		||||
* Install **protoc-gen-go-micro**: `go install go.unistack.org/protoc-gen-go-micro/v3`
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								ast.go
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								ast.go
									
									
									
									
									
								
							@@ -10,7 +10,7 @@ import (
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/fatih/structtag"
 | 
			
		||||
	tag_options "go.unistack.org/micro-proto/v4/tag"
 | 
			
		||||
	tag_options "go.unistack.org/micro-proto/v3/tag"
 | 
			
		||||
	"google.golang.org/protobuf/compiler/protogen"
 | 
			
		||||
	"google.golang.org/protobuf/proto"
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
@@ -2,28 +2,24 @@ syntax = "proto3";
 | 
			
		||||
 | 
			
		||||
package example;
 | 
			
		||||
 | 
			
		||||
option go_package = "github.com/unistack-org/protoc-gen-go-micro/v4/example;examplepb";
 | 
			
		||||
option go_package = "github.com/unistack-org/protoc-gen-go-micro/v3/example;examplepb";
 | 
			
		||||
 | 
			
		||||
import "tag/tag.proto";
 | 
			
		||||
import "api/annotations.proto";
 | 
			
		||||
import "openapiv2/annotations.proto";
 | 
			
		||||
import "google/protobuf/wrappers.proto";
 | 
			
		||||
import "openapiv3/annotations.proto";
 | 
			
		||||
//import "google/protobuf/wrappers.proto";
 | 
			
		||||
import "graphql/graphql.proto";
 | 
			
		||||
 | 
			
		||||
service Example {
 | 
			
		||||
	rpc Call(CallReq) returns (CallRsp) {
 | 
			
		||||
		option (micro.openapiv3.openapiv3_operation) = {
 | 
			
		||||
    option (micro.graphql.rpc) = {type: QUERY};
 | 
			
		||||
    option (micro.openapiv3.openapiv3_operation) = {
 | 
			
		||||
      operation_id: "Call";
 | 
			
		||||
        responses: {
 | 
			
		||||
          key: "default";
 | 
			
		||||
          value: {
 | 
			
		||||
            description: "Error response";
 | 
			
		||||
            schema: {
 | 
			
		||||
              json_schema: {
 | 
			
		||||
                ref: ".example.Error";
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      responses: {
 | 
			
		||||
        default: {
 | 
			
		||||
          reference: {_ref: ".example.Error"};
 | 
			
		||||
        };
 | 
			
		||||
      };
 | 
			
		||||
    };
 | 
			
		||||
		option (micro.api.http) = { post: "/v1/example/call/{name}"; body: "*"; };
 | 
			
		||||
		option (micro.api.micro_method) = { timeout: "5s"; };
 | 
			
		||||
@@ -31,7 +27,7 @@ service Example {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
message CallReq {
 | 
			
		||||
  string name = 1 [(micro.tag.tags) = "xml:\",attr\"" ];
 | 
			
		||||
  string name = 1 [(micro.graphql.field) = {required: true}]; 
 | 
			
		||||
  string req = 2;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										11
									
								
								example/schema.graphql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								example/schema.graphql
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
directive @Example on FIELD_DEFINITION
 | 
			
		||||
input CallReqInput {
 | 
			
		||||
	name: String!
 | 
			
		||||
	req: String
 | 
			
		||||
}
 | 
			
		||||
type CallRsp {
 | 
			
		||||
	rsp: String
 | 
			
		||||
}
 | 
			
		||||
type Query {
 | 
			
		||||
	exampleCall(in: CallReqInput): CallRsp
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										3
									
								
								generate.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								generate.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
//go:generate sh -xc "protoc -I./example -I. -I$(go list -f '{{ .Dir }}' -m go.unistack.org/micro-proto/v3) --go-micro_out=components=graphqls,graphql_file=./schema.graphql:./example example/example.proto"
 | 
			
		||||
							
								
								
									
										35
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										35
									
								
								go.mod
									
									
									
									
									
								
							@@ -1,18 +1,35 @@
 | 
			
		||||
module go.unistack.org/protoc-gen-go-micro/v4
 | 
			
		||||
module go.unistack.org/protoc-gen-go-micro/v3
 | 
			
		||||
 | 
			
		||||
go 1.19
 | 
			
		||||
go 1.22.0
 | 
			
		||||
 | 
			
		||||
toolchain go1.23.4
 | 
			
		||||
 | 
			
		||||
require (
 | 
			
		||||
	github.com/fatih/structtag v1.2.0
 | 
			
		||||
	go.unistack.org/micro-proto/v4 v4.0.1
 | 
			
		||||
	golang.org/x/tools v0.11.0
 | 
			
		||||
	google.golang.org/protobuf v1.31.0
 | 
			
		||||
	github.com/jhump/protoreflect v1.17.0
 | 
			
		||||
	github.com/vektah/gqlparser/v2 v2.5.20
 | 
			
		||||
	go.unistack.org/micro-proto/v3 v3.4.1
 | 
			
		||||
	golang.org/x/tools v0.28.0
 | 
			
		||||
	google.golang.org/protobuf v1.35.2
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
require (
 | 
			
		||||
	github.com/golang/protobuf v1.5.3 // indirect
 | 
			
		||||
	github.com/google/gnostic v0.6.9 // indirect
 | 
			
		||||
	golang.org/x/mod v0.12.0 // indirect
 | 
			
		||||
	golang.org/x/sys v0.10.0 // indirect
 | 
			
		||||
	github.com/agnivade/levenshtein v1.2.0 // indirect
 | 
			
		||||
	github.com/bufbuild/protocompile v0.14.1 // indirect
 | 
			
		||||
	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
 | 
			
		||||
	github.com/golang/protobuf v1.5.4 // indirect
 | 
			
		||||
	github.com/google/gnostic v0.7.0 // indirect
 | 
			
		||||
	github.com/google/gnostic-models v0.6.9 // indirect
 | 
			
		||||
	github.com/kr/pretty v0.3.1 // indirect
 | 
			
		||||
	github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
 | 
			
		||||
	github.com/rogpeppe/go-internal v1.11.0 // indirect
 | 
			
		||||
	github.com/sergi/go-diff v1.3.1 // indirect
 | 
			
		||||
	github.com/stretchr/testify v1.10.0 // indirect
 | 
			
		||||
	golang.org/x/mod v0.22.0 // indirect
 | 
			
		||||
	golang.org/x/sync v0.10.0 // indirect
 | 
			
		||||
	golang.org/x/text v0.21.0 // indirect
 | 
			
		||||
	google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect
 | 
			
		||||
	google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 // indirect
 | 
			
		||||
	google.golang.org/grpc v1.68.1 // indirect
 | 
			
		||||
	gopkg.in/yaml.v3 v3.0.1 // indirect
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										9
									
								
								graphql.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								graphql.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"google.golang.org/protobuf/compiler/protogen"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (g *Generator) graphqlGenerate(plugin *protogen.Plugin) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										32
									
								
								graphql/callstack.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								graphql/callstack.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
			
		||||
package generator
 | 
			
		||||
 | 
			
		||||
type Callstack interface {
 | 
			
		||||
	Push(entry interface{})
 | 
			
		||||
	Pop(entry interface{})
 | 
			
		||||
	Has(entry interface{}) bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewCallstack() Callstack {
 | 
			
		||||
	return &callstack{stack: make(map[interface{}]int), index: 0}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type callstack struct {
 | 
			
		||||
	stack  map[interface{}]int
 | 
			
		||||
	sorted []string
 | 
			
		||||
	index  int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *callstack) Pop(entry interface{}) {
 | 
			
		||||
	delete(c.stack, entry)
 | 
			
		||||
	c.index--
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *callstack) Push(entry interface{}) {
 | 
			
		||||
	c.stack[entry] = c.index
 | 
			
		||||
	c.index++
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *callstack) Has(entry interface{}) bool {
 | 
			
		||||
	_, ok := c.stack[entry]
 | 
			
		||||
	return ok
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										77
									
								
								graphql/descriptors.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								graphql/descriptors.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,77 @@
 | 
			
		||||
package generator
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/jhump/protoreflect/desc"
 | 
			
		||||
	"github.com/vektah/gqlparser/v2/ast"
 | 
			
		||||
	any "google.golang.org/protobuf/types/known/anypb"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type ObjectDescriptor struct {
 | 
			
		||||
	*ast.Definition
 | 
			
		||||
	desc.Descriptor
 | 
			
		||||
 | 
			
		||||
	types      []*ObjectDescriptor
 | 
			
		||||
	fields     []*FieldDescriptor
 | 
			
		||||
	fieldNames map[string]*FieldDescriptor
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *ObjectDescriptor) AsGraphql() *ast.Definition {
 | 
			
		||||
	return o.Definition
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *ObjectDescriptor) uniqueName(f *desc.FieldDescriptor) string {
 | 
			
		||||
	return strings.Title(f.GetName())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *ObjectDescriptor) IsInput() bool {
 | 
			
		||||
	return o.Kind == ast.InputObject
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *ObjectDescriptor) GetFields() []*FieldDescriptor {
 | 
			
		||||
	return o.fields
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *ObjectDescriptor) GetTypes() []*ObjectDescriptor {
 | 
			
		||||
	return o.types
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *ObjectDescriptor) IsMessage() bool {
 | 
			
		||||
	_, ok := o.Descriptor.(*desc.MessageDescriptor)
 | 
			
		||||
	return ok
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// same isEmpty but for mortals
 | 
			
		||||
func IsEmpty(o *desc.MessageDescriptor) bool { return isEmpty(o, NewCallstack()) }
 | 
			
		||||
 | 
			
		||||
// make sure objects are fulled with all objects
 | 
			
		||||
func isEmpty(o *desc.MessageDescriptor, callstack Callstack) bool {
 | 
			
		||||
	callstack.Push(o)
 | 
			
		||||
	defer callstack.Pop(o)
 | 
			
		||||
 | 
			
		||||
	if len(o.GetFields()) == 0 {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	for _, f := range o.GetFields() {
 | 
			
		||||
		objType := f.GetMessageType()
 | 
			
		||||
		if objType == nil {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// check if the call stack already contains a reference to this type and prevent it from calling itself again
 | 
			
		||||
		if callstack.Has(objType) {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
		if !isEmpty(objType, callstack) {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TODO maybe not compare by strings
 | 
			
		||||
func IsAny(o *desc.MessageDescriptor) bool {
 | 
			
		||||
	return string((&any.Any{}).ProtoReflect().Descriptor().FullName()) == o.GetFullyQualifiedName()
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										761
									
								
								graphql/generator.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										761
									
								
								graphql/generator.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,761 @@
 | 
			
		||||
package generator
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/jhump/protoreflect/desc"
 | 
			
		||||
	"github.com/vektah/gqlparser/v2/ast"
 | 
			
		||||
	"google.golang.org/protobuf/compiler/protogen"
 | 
			
		||||
	descriptor "google.golang.org/protobuf/types/descriptorpb"
 | 
			
		||||
 | 
			
		||||
	gqlpb "go.unistack.org/micro-proto/v3/graphql"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	fieldPrefix        = "Field"
 | 
			
		||||
	inputSuffix        = "Input"
 | 
			
		||||
	typeSep            = "_"
 | 
			
		||||
	packageSep         = "."
 | 
			
		||||
	anyTypeDescription = "Any is any json type"
 | 
			
		||||
	scalarBytes        = "Bytes"
 | 
			
		||||
	goFieldDirective   = "goField"
 | 
			
		||||
 | 
			
		||||
	DefaultExtension = "graphql"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func NewSchemas(descs []*desc.FileDescriptor, mergeSchemas, genServiceDesc bool, plugin *protogen.Plugin) (schemas SchemaDescriptorList, err error) {
 | 
			
		||||
	var files []*descriptor.FileDescriptorProto
 | 
			
		||||
	for _, d := range descs {
 | 
			
		||||
		files = append(files, d.AsFileDescriptorProto())
 | 
			
		||||
	}
 | 
			
		||||
	var goref GoRef
 | 
			
		||||
	if plugin != nil {
 | 
			
		||||
		goref, err = NewGoRef(plugin)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if mergeSchemas {
 | 
			
		||||
		schema := NewSchemaDescriptor(genServiceDesc, goref)
 | 
			
		||||
		for _, file := range descs {
 | 
			
		||||
			err := generateFile(file, schema)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return []*SchemaDescriptor{schema}, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, file := range descs {
 | 
			
		||||
		schema := NewSchemaDescriptor(genServiceDesc, goref)
 | 
			
		||||
		err := generateFile(file, schema)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		schemas = append(schemas, schema)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func generateFile(file *desc.FileDescriptor, schema *SchemaDescriptor) error {
 | 
			
		||||
	schema.FileDescriptors = append(schema.FileDescriptors, file)
 | 
			
		||||
 | 
			
		||||
	for _, svc := range file.GetServices() {
 | 
			
		||||
		svcOpts := GraphqlServiceOptions(svc.AsServiceDescriptorProto().GetOptions())
 | 
			
		||||
		if svcOpts != nil && svcOpts.Ignore != nil && *svcOpts.Ignore {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		for _, rpc := range svc.GetMethods() {
 | 
			
		||||
			rpcOpts := GraphqlMethodOptions(rpc.AsMethodDescriptorProto().GetOptions())
 | 
			
		||||
			if rpcOpts != nil && rpcOpts.Ignore != nil && *rpcOpts.Ignore {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			in, err := schema.CreateObjects(rpc.GetInputType(), true)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			out, err := schema.CreateObjects(rpc.GetOutputType(), false)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if rpc.IsServerStreaming() && rpc.IsClientStreaming() {
 | 
			
		||||
				schema.GetMutation().addMethod(svc, rpc, in, out)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if rpc.IsServerStreaming() {
 | 
			
		||||
				schema.GetSubscription().addMethod(svc, rpc, in, out)
 | 
			
		||||
			} else {
 | 
			
		||||
				switch GetRequestType(rpcOpts, svcOpts) {
 | 
			
		||||
				case gqlpb.Type_QUERY:
 | 
			
		||||
					schema.GetQuery().addMethod(svc, rpc, in, out)
 | 
			
		||||
				default:
 | 
			
		||||
					schema.GetMutation().addMethod(svc, rpc, in, out)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type SchemaDescriptorList []*SchemaDescriptor
 | 
			
		||||
 | 
			
		||||
func (s SchemaDescriptorList) AsGraphql() (astSchema []*ast.Schema) {
 | 
			
		||||
	for _, ss := range s {
 | 
			
		||||
		astSchema = append(astSchema, ss.AsGraphql())
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s SchemaDescriptorList) GetForDescriptor(file *protogen.File) *SchemaDescriptor {
 | 
			
		||||
	for _, schema := range s {
 | 
			
		||||
		for _, d := range schema.FileDescriptors {
 | 
			
		||||
			if d.AsFileDescriptorProto() == file.Proto {
 | 
			
		||||
				return schema
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewSchemaDescriptor(genServiceDesc bool, goref GoRef) *SchemaDescriptor {
 | 
			
		||||
	sd := &SchemaDescriptor{
 | 
			
		||||
		Directives:                 map[string]*ast.DirectiveDefinition{},
 | 
			
		||||
		reservedNames:              map[string]desc.Descriptor{},
 | 
			
		||||
		createdObjects:             map[createdObjectKey]*ObjectDescriptor{},
 | 
			
		||||
		generateServiceDescriptors: genServiceDesc,
 | 
			
		||||
		goRef:                      goref,
 | 
			
		||||
	}
 | 
			
		||||
	for _, name := range graphqlReservedNames {
 | 
			
		||||
		sd.reservedNames[name] = nil
 | 
			
		||||
	}
 | 
			
		||||
	return sd
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type SchemaDescriptor struct {
 | 
			
		||||
	Directives      map[string]*ast.DirectiveDefinition
 | 
			
		||||
	FileDescriptors []*desc.FileDescriptor
 | 
			
		||||
 | 
			
		||||
	files []*desc.FileDescriptor
 | 
			
		||||
 | 
			
		||||
	query        *RootDefinition
 | 
			
		||||
	mutation     *RootDefinition
 | 
			
		||||
	subscription *RootDefinition
 | 
			
		||||
 | 
			
		||||
	objects []*ObjectDescriptor
 | 
			
		||||
 | 
			
		||||
	reservedNames  map[string]desc.Descriptor
 | 
			
		||||
	createdObjects map[createdObjectKey]*ObjectDescriptor
 | 
			
		||||
 | 
			
		||||
	generateServiceDescriptors bool
 | 
			
		||||
 | 
			
		||||
	goRef GoRef
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type createdObjectKey struct {
 | 
			
		||||
	desc  desc.Descriptor
 | 
			
		||||
	input bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *SchemaDescriptor) AsGraphql() *ast.Schema {
 | 
			
		||||
	queryDef := *s.GetQuery().Definition
 | 
			
		||||
	mutationDef := *s.GetMutation().Definition
 | 
			
		||||
	subscriptionsDef := *s.GetSubscription().Definition
 | 
			
		||||
	schema := &ast.Schema{Types: map[string]*ast.Definition{}, Directives: s.Directives}
 | 
			
		||||
	schema.Query = &queryDef
 | 
			
		||||
	schema.Types["Query"] = &queryDef
 | 
			
		||||
	if s.query.methods == nil {
 | 
			
		||||
		schema.Query.Fields = append(schema.Query.Fields, &ast.FieldDefinition{
 | 
			
		||||
			Name: "dummy",
 | 
			
		||||
			Type: ast.NamedType("Boolean", &ast.Position{}),
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
	if s.mutation.methods != nil {
 | 
			
		||||
		schema.Mutation = &mutationDef
 | 
			
		||||
		schema.Types["Mutation"] = &mutationDef
 | 
			
		||||
	}
 | 
			
		||||
	if s.subscription.methods != nil {
 | 
			
		||||
		schema.Subscription = &subscriptionsDef
 | 
			
		||||
		schema.Types["Subscription"] = &subscriptionsDef
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, o := range s.objects {
 | 
			
		||||
		def := o.AsGraphql()
 | 
			
		||||
		schema.Types[def.Name] = def
 | 
			
		||||
	}
 | 
			
		||||
	return schema
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *SchemaDescriptor) Objects() []*ObjectDescriptor {
 | 
			
		||||
	return s.objects
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *SchemaDescriptor) GetMutation() *RootDefinition {
 | 
			
		||||
	if s.mutation == nil {
 | 
			
		||||
		s.mutation = NewRootDefinition(Mutation, s)
 | 
			
		||||
	}
 | 
			
		||||
	return s.mutation
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *SchemaDescriptor) GetSubscription() *RootDefinition {
 | 
			
		||||
	if s.subscription == nil {
 | 
			
		||||
		s.subscription = NewRootDefinition(Subscription, s)
 | 
			
		||||
	}
 | 
			
		||||
	return s.subscription
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *SchemaDescriptor) GetQuery() *RootDefinition {
 | 
			
		||||
	if s.query == nil {
 | 
			
		||||
		s.query = NewRootDefinition(Query, s)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return s.query
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// make name be unique
 | 
			
		||||
// just create a map and register every name
 | 
			
		||||
func (s *SchemaDescriptor) uniqueName(d desc.Descriptor, input bool) (name string) {
 | 
			
		||||
	var collisionPrefix string
 | 
			
		||||
	var suffix string
 | 
			
		||||
	if _, ok := d.(*desc.MessageDescriptor); input && ok {
 | 
			
		||||
		suffix = inputSuffix
 | 
			
		||||
	}
 | 
			
		||||
	name = strings.Title(CamelCaseSlice(strings.Split(strings.TrimPrefix(d.GetFullyQualifiedName(), d.GetFile().GetPackage()+packageSep), packageSep)) + suffix)
 | 
			
		||||
 | 
			
		||||
	if _, ok := d.(*desc.FieldDescriptor); ok {
 | 
			
		||||
		collisionPrefix = fieldPrefix
 | 
			
		||||
		name = CamelCaseSlice(strings.Split(strings.Trim(d.GetParent().GetName()+packageSep+strings.Title(d.GetName()), packageSep), packageSep))
 | 
			
		||||
	} else {
 | 
			
		||||
		collisionPrefix = CamelCaseSlice(strings.Split(d.GetFile().GetPackage(), packageSep))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	originalName := name
 | 
			
		||||
	for uniqueSuffix := 0; ; uniqueSuffix++ {
 | 
			
		||||
		d2, ok := s.reservedNames[name]
 | 
			
		||||
		if !ok {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		if d2 == d {
 | 
			
		||||
			return name
 | 
			
		||||
		}
 | 
			
		||||
		if uniqueSuffix == 0 {
 | 
			
		||||
			name = collisionPrefix + typeSep + originalName
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		name = collisionPrefix + typeSep + originalName + strconv.Itoa(uniqueSuffix)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	s.reservedNames[name] = d
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *SchemaDescriptor) CreateObjects(d desc.Descriptor, input bool) (obj *ObjectDescriptor, err error) {
 | 
			
		||||
	// the case if trying to resolve a primitive as a object. In this case we just return nil
 | 
			
		||||
	if d == nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if obj, ok := s.createdObjects[createdObjectKey{d, input}]; ok {
 | 
			
		||||
		return obj, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	obj = &ObjectDescriptor{
 | 
			
		||||
		Definition: &ast.Definition{
 | 
			
		||||
			Description: getDescription(d),
 | 
			
		||||
			Name:        s.uniqueName(d, input),
 | 
			
		||||
			Position:    &ast.Position{},
 | 
			
		||||
		},
 | 
			
		||||
		Descriptor: d,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	s.createdObjects[createdObjectKey{d, input}] = obj
 | 
			
		||||
 | 
			
		||||
	switch dd := d.(type) {
 | 
			
		||||
	case *desc.MessageDescriptor:
 | 
			
		||||
		if IsEmpty(dd) {
 | 
			
		||||
			return obj, nil
 | 
			
		||||
		}
 | 
			
		||||
		if IsAny(dd) {
 | 
			
		||||
			// TODO find a better way to handle any types
 | 
			
		||||
			delete(s.createdObjects, createdObjectKey{d, input})
 | 
			
		||||
			any := s.createScalar(s.uniqueName(dd, false), anyTypeDescription)
 | 
			
		||||
			return any, nil
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		kind := ast.Object
 | 
			
		||||
		if input {
 | 
			
		||||
			kind = ast.InputObject
 | 
			
		||||
		}
 | 
			
		||||
		fields := FieldDescriptorList{}
 | 
			
		||||
		outputOneofRegistrar := map[*desc.OneOfDescriptor]struct{}{}
 | 
			
		||||
 | 
			
		||||
		for _, df := range dd.GetFields() {
 | 
			
		||||
			fieldOpts := GraphqlFieldOptions(df.AsFieldDescriptorProto().GetOptions())
 | 
			
		||||
			if fieldOpts != nil && fieldOpts.Ignore != nil && *fieldOpts.Ignore {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			var fieldDirective []*ast.Directive
 | 
			
		||||
			if df.GetType() == descriptor.FieldDescriptorProto_TYPE_MESSAGE && IsEmpty(df.GetMessageType()) {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Internally `optional` fields are represented as a oneof, and as such should be skipped.
 | 
			
		||||
			if oneof := df.GetOneOf(); oneof != nil && !df.AsFieldDescriptorProto().GetProto3Optional() {
 | 
			
		||||
				opts := GraphqlOneofOptions(oneof.AsOneofDescriptorProto().GetOptions())
 | 
			
		||||
				if opts.GetIgnore() {
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				if !input {
 | 
			
		||||
					if _, ok := outputOneofRegistrar[oneof]; ok {
 | 
			
		||||
						continue
 | 
			
		||||
					}
 | 
			
		||||
					outputOneofRegistrar[oneof] = struct{}{}
 | 
			
		||||
					field, err := s.createUnion(oneof)
 | 
			
		||||
					if err != nil {
 | 
			
		||||
						return nil, err
 | 
			
		||||
					}
 | 
			
		||||
					fields = append(fields, field)
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				// create oneofs as directives for input objects
 | 
			
		||||
				directive := &ast.DirectiveDefinition{
 | 
			
		||||
					Description: getDescription(oneof),
 | 
			
		||||
					Name:        s.uniqueName(oneof, input),
 | 
			
		||||
					Locations:   []ast.DirectiveLocation{ast.LocationInputFieldDefinition},
 | 
			
		||||
					Position:    &ast.Position{Src: &ast.Source{}},
 | 
			
		||||
				}
 | 
			
		||||
				s.Directives[directive.Name] = directive
 | 
			
		||||
				fieldDirective = append(fieldDirective, &ast.Directive{
 | 
			
		||||
					Name:     directive.Name,
 | 
			
		||||
					Position: &ast.Position{Src: &ast.Source{}},
 | 
			
		||||
					// ParentDefinition: obj.Definition, TODO
 | 
			
		||||
					Definition: directive,
 | 
			
		||||
					Location:   ast.LocationInputFieldDefinition,
 | 
			
		||||
				})
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			fieldObj, err := s.CreateObjects(resolveFieldType(df), input)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
			if fieldObj == nil && df.GetMessageType() != nil {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			f, err := s.createField(df, fieldObj)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
			f.Directives = append(f.Directives, fieldDirective...)
 | 
			
		||||
			fields = append(fields, f)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		obj.Definition.Fields = fields.AsGraphql()
 | 
			
		||||
		obj.Definition.Kind = kind
 | 
			
		||||
		obj.fields = fields
 | 
			
		||||
	case *desc.EnumDescriptor:
 | 
			
		||||
		obj.Definition.Kind = ast.Enum
 | 
			
		||||
		obj.Definition.EnumValues = enumValues(dd.GetValues())
 | 
			
		||||
	default:
 | 
			
		||||
		panic(fmt.Sprintf("received unexpected value %v of type %T", dd, dd))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	s.objects = append(s.objects, obj)
 | 
			
		||||
	return obj, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func resolveFieldType(field *desc.FieldDescriptor) desc.Descriptor {
 | 
			
		||||
	msgType := field.GetMessageType()
 | 
			
		||||
	enumType := field.GetEnumType()
 | 
			
		||||
	if msgType != nil {
 | 
			
		||||
		return msgType
 | 
			
		||||
	}
 | 
			
		||||
	if enumType != nil {
 | 
			
		||||
		return enumType
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func enumValues(evals []*desc.EnumValueDescriptor) (vlist ast.EnumValueList) {
 | 
			
		||||
	for _, eval := range evals {
 | 
			
		||||
		vlist = append(vlist, &ast.EnumValueDefinition{
 | 
			
		||||
			Description: getDescription(eval),
 | 
			
		||||
			Name:        eval.GetName(),
 | 
			
		||||
			Position:    &ast.Position{},
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return vlist
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type FieldDescriptorList []*FieldDescriptor
 | 
			
		||||
 | 
			
		||||
func (fl FieldDescriptorList) AsGraphql() (dl []*ast.FieldDefinition) {
 | 
			
		||||
	for _, f := range fl {
 | 
			
		||||
		dl = append(dl, f.FieldDefinition)
 | 
			
		||||
	}
 | 
			
		||||
	return dl
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type FieldDescriptor struct {
 | 
			
		||||
	*ast.FieldDefinition
 | 
			
		||||
	*desc.FieldDescriptor
 | 
			
		||||
 | 
			
		||||
	typ *ObjectDescriptor
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *FieldDescriptor) GetType() *ObjectDescriptor {
 | 
			
		||||
	return f.typ
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type MethodDescriptor struct {
 | 
			
		||||
	*desc.ServiceDescriptor
 | 
			
		||||
	*desc.MethodDescriptor
 | 
			
		||||
 | 
			
		||||
	*ast.FieldDefinition
 | 
			
		||||
 | 
			
		||||
	input  *ObjectDescriptor
 | 
			
		||||
	output *ObjectDescriptor
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MethodDescriptor) AsGraphql() *ast.FieldDefinition {
 | 
			
		||||
	return m.FieldDefinition
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MethodDescriptor) GetInput() *ObjectDescriptor {
 | 
			
		||||
	return m.input
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MethodDescriptor) GetOutput() *ObjectDescriptor {
 | 
			
		||||
	return m.output
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type RootDefinition struct {
 | 
			
		||||
	*ast.Definition
 | 
			
		||||
 | 
			
		||||
	Parent *SchemaDescriptor
 | 
			
		||||
 | 
			
		||||
	methods       []*MethodDescriptor
 | 
			
		||||
	reservedNames map[string]ServiceAndMethod
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ServiceAndMethod struct {
 | 
			
		||||
	svc *descriptor.ServiceDescriptorProto
 | 
			
		||||
	rpc *descriptor.MethodDescriptorProto
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *RootDefinition) UniqueName(svc *descriptor.ServiceDescriptorProto, rpc *descriptor.MethodDescriptorProto) (name string) {
 | 
			
		||||
	rpcOpts := GraphqlMethodOptions(rpc.GetOptions())
 | 
			
		||||
	svcOpts := GraphqlServiceOptions(svc.GetOptions())
 | 
			
		||||
	if rpcOpts != nil && rpcOpts.Name != nil {
 | 
			
		||||
		name = *rpcOpts.Name
 | 
			
		||||
	} else if svcOpts != nil && svcOpts.Name != nil {
 | 
			
		||||
		if *svcOpts.Name == "" {
 | 
			
		||||
			name = ToLowerFirst(rpc.GetName())
 | 
			
		||||
		} else {
 | 
			
		||||
			name = *svcOpts.Name + strings.Title(rpc.GetName())
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		name = ToLowerFirst(svc.GetName()) + strings.Title(rpc.GetName())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	originalName := name
 | 
			
		||||
	for uniqueSuffix := 0; ; uniqueSuffix++ {
 | 
			
		||||
		snm, ok := r.reservedNames[name]
 | 
			
		||||
		if !ok {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		if svc == snm.svc && snm.rpc == rpc {
 | 
			
		||||
			return name
 | 
			
		||||
		}
 | 
			
		||||
		name = originalName + strconv.Itoa(uniqueSuffix)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r.reservedNames[name] = ServiceAndMethod{svc, rpc}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *RootDefinition) Methods() []*MethodDescriptor {
 | 
			
		||||
	return r.methods
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *RootDefinition) addMethod(svc *desc.ServiceDescriptor, rpc *desc.MethodDescriptor, in, out *ObjectDescriptor) {
 | 
			
		||||
	var args ast.ArgumentDefinitionList
 | 
			
		||||
 | 
			
		||||
	if in != nil && (in.Descriptor != nil && !IsEmpty(in.Descriptor.(*desc.MessageDescriptor)) || in.Definition.Kind == ast.Scalar) {
 | 
			
		||||
		args = append(args, &ast.ArgumentDefinition{
 | 
			
		||||
			Name:     "in",
 | 
			
		||||
			Type:     ast.NamedType(in.Name, &ast.Position{}),
 | 
			
		||||
			Position: &ast.Position{},
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
	objType := ast.NamedType("Boolean", &ast.Position{})
 | 
			
		||||
	if out != nil && (out.Descriptor != nil && !IsEmpty(out.Descriptor.(*desc.MessageDescriptor)) || in.Definition.Kind == ast.Scalar) {
 | 
			
		||||
		objType = ast.NamedType(out.Name, &ast.Position{})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	svcDir := &ast.DirectiveDefinition{
 | 
			
		||||
		Description: getDescription(svc),
 | 
			
		||||
		Name:        svc.GetName(),
 | 
			
		||||
		Locations:   []ast.DirectiveLocation{ast.LocationFieldDefinition},
 | 
			
		||||
		Position:    &ast.Position{Src: &ast.Source{}},
 | 
			
		||||
	}
 | 
			
		||||
	r.Parent.Directives[svcDir.Name] = svcDir
 | 
			
		||||
 | 
			
		||||
	m := &MethodDescriptor{
 | 
			
		||||
		ServiceDescriptor: svc,
 | 
			
		||||
		MethodDescriptor:  rpc,
 | 
			
		||||
		FieldDefinition: &ast.FieldDefinition{
 | 
			
		||||
			Description: getDescription(rpc),
 | 
			
		||||
			Name:        r.UniqueName(svc.AsServiceDescriptorProto(), rpc.AsMethodDescriptorProto()),
 | 
			
		||||
			Arguments:   args,
 | 
			
		||||
			Type:        objType,
 | 
			
		||||
			Position:    &ast.Position{},
 | 
			
		||||
		},
 | 
			
		||||
		input:  in,
 | 
			
		||||
		output: out,
 | 
			
		||||
	}
 | 
			
		||||
	if r.Parent.generateServiceDescriptors {
 | 
			
		||||
		m.Directives = []*ast.Directive{{
 | 
			
		||||
			Name:       svcDir.Name,
 | 
			
		||||
			Position:   &ast.Position{},
 | 
			
		||||
			Definition: svcDir,
 | 
			
		||||
			Location:   svcDir.Locations[0],
 | 
			
		||||
		}}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r.methods = append(r.methods, m)
 | 
			
		||||
	// TODO maybe not do it here?
 | 
			
		||||
	r.Definition.Fields = append(r.Definition.Fields, m.FieldDefinition)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type rootName string
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	Mutation     rootName = "Mutation"
 | 
			
		||||
	Query        rootName = "Query"
 | 
			
		||||
	Subscription rootName = "Subscription"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func NewRootDefinition(name rootName, parent *SchemaDescriptor) *RootDefinition {
 | 
			
		||||
	return &RootDefinition{Definition: &ast.Definition{
 | 
			
		||||
		Kind:     ast.Object,
 | 
			
		||||
		Name:     string(name),
 | 
			
		||||
		Position: &ast.Position{},
 | 
			
		||||
	}, Parent: parent, reservedNames: map[string]ServiceAndMethod{}}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getDescription(descs ...desc.Descriptor) string {
 | 
			
		||||
	var description []string
 | 
			
		||||
	for _, d := range descs {
 | 
			
		||||
		info := d.GetSourceInfo()
 | 
			
		||||
		if info == nil {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if info.LeadingComments != nil {
 | 
			
		||||
			description = append(description, *info.LeadingComments)
 | 
			
		||||
		}
 | 
			
		||||
		if info.TrailingComments != nil {
 | 
			
		||||
			description = append(description, *info.TrailingComments)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return strings.Join(description, "\n")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *SchemaDescriptor) createField(field *desc.FieldDescriptor, obj *ObjectDescriptor) (_ *FieldDescriptor, err error) {
 | 
			
		||||
	fieldAst := &ast.FieldDefinition{
 | 
			
		||||
		Description: getDescription(field),
 | 
			
		||||
		Name:        ToLowerFirst(CamelCase(field.GetName())),
 | 
			
		||||
		Type:        &ast.Type{Position: &ast.Position{}},
 | 
			
		||||
		Position:    &ast.Position{},
 | 
			
		||||
	}
 | 
			
		||||
	fieldOpts := GraphqlFieldOptions(field.AsFieldDescriptorProto().GetOptions())
 | 
			
		||||
	if fieldOpts != nil && fieldOpts.Name != nil {
 | 
			
		||||
		fieldAst.Name = *fieldOpts.Name
 | 
			
		||||
		directive := &ast.DirectiveDefinition{
 | 
			
		||||
			Name: goFieldDirective,
 | 
			
		||||
			Arguments: []*ast.ArgumentDefinition{{
 | 
			
		||||
				Name:     "forceResolver",
 | 
			
		||||
				Type:     ast.NamedType("Boolean", &ast.Position{}),
 | 
			
		||||
				Position: &ast.Position{},
 | 
			
		||||
			}, {
 | 
			
		||||
				Name:     "name",
 | 
			
		||||
				Type:     ast.NamedType("String", &ast.Position{}),
 | 
			
		||||
				Position: &ast.Position{},
 | 
			
		||||
			}},
 | 
			
		||||
			Locations: []ast.DirectiveLocation{ast.LocationInputFieldDefinition, ast.LocationFieldDefinition},
 | 
			
		||||
			Position:  &ast.Position{Src: &ast.Source{}},
 | 
			
		||||
		}
 | 
			
		||||
		s.Directives[directive.Name] = directive
 | 
			
		||||
		if s.goRef != nil {
 | 
			
		||||
			fieldAst.Directives = []*ast.Directive{{
 | 
			
		||||
				Name: directive.Name,
 | 
			
		||||
				Arguments: []*ast.Argument{{
 | 
			
		||||
					Name: "name",
 | 
			
		||||
					Value: &ast.Value{
 | 
			
		||||
						Raw:      s.goRef.FindGoField(field.GetFullyQualifiedName()).GoName,
 | 
			
		||||
						Kind:     ast.StringValue,
 | 
			
		||||
						Position: &ast.Position{},
 | 
			
		||||
					},
 | 
			
		||||
					Position: &ast.Position{},
 | 
			
		||||
				}},
 | 
			
		||||
				Position: &ast.Position{},
 | 
			
		||||
				// ParentDefinition: nil, TODO
 | 
			
		||||
				Definition: directive,
 | 
			
		||||
			}}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	switch field.GetType() {
 | 
			
		||||
	case descriptor.FieldDescriptorProto_TYPE_DOUBLE,
 | 
			
		||||
		descriptor.FieldDescriptorProto_TYPE_FLOAT:
 | 
			
		||||
		fieldAst.Type.NamedType = ScalarFloat
 | 
			
		||||
 | 
			
		||||
	case descriptor.FieldDescriptorProto_TYPE_BYTES:
 | 
			
		||||
		scalar := s.createScalar(scalarBytes, "")
 | 
			
		||||
		fieldAst.Type.NamedType = scalar.Name
 | 
			
		||||
 | 
			
		||||
	case descriptor.FieldDescriptorProto_TYPE_INT64,
 | 
			
		||||
		descriptor.FieldDescriptorProto_TYPE_SINT64,
 | 
			
		||||
		descriptor.FieldDescriptorProto_TYPE_SFIXED64,
 | 
			
		||||
		descriptor.FieldDescriptorProto_TYPE_INT32,
 | 
			
		||||
		descriptor.FieldDescriptorProto_TYPE_SINT32,
 | 
			
		||||
		descriptor.FieldDescriptorProto_TYPE_SFIXED32,
 | 
			
		||||
		descriptor.FieldDescriptorProto_TYPE_UINT32,
 | 
			
		||||
		descriptor.FieldDescriptorProto_TYPE_FIXED32,
 | 
			
		||||
		descriptor.FieldDescriptorProto_TYPE_UINT64,
 | 
			
		||||
		descriptor.FieldDescriptorProto_TYPE_FIXED64:
 | 
			
		||||
		fieldAst.Type.NamedType = ScalarInt
 | 
			
		||||
 | 
			
		||||
	case descriptor.FieldDescriptorProto_TYPE_BOOL:
 | 
			
		||||
		fieldAst.Type.NamedType = ScalarBoolean
 | 
			
		||||
 | 
			
		||||
	case descriptor.FieldDescriptorProto_TYPE_STRING:
 | 
			
		||||
		fieldAst.Type.NamedType = ScalarString
 | 
			
		||||
 | 
			
		||||
	case descriptor.FieldDescriptorProto_TYPE_GROUP:
 | 
			
		||||
		return nil, fmt.Errorf("proto2 groups are not supported please use proto3 syntax")
 | 
			
		||||
 | 
			
		||||
	case descriptor.FieldDescriptorProto_TYPE_ENUM:
 | 
			
		||||
		fieldAst.Type.NamedType = obj.Name
 | 
			
		||||
 | 
			
		||||
	case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
 | 
			
		||||
		fieldAst.Type.NamedType = obj.Name
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		panic("unknown proto field type")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if isRepeated(field) {
 | 
			
		||||
		fieldAst.Type = ast.ListType(fieldAst.Type, &ast.Position{})
 | 
			
		||||
		fieldAst.Type.Elem.NonNull = true
 | 
			
		||||
	}
 | 
			
		||||
	if isRequired(field) {
 | 
			
		||||
		fieldAst.Type.NonNull = true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &FieldDescriptor{
 | 
			
		||||
		FieldDefinition: fieldAst,
 | 
			
		||||
		FieldDescriptor: field,
 | 
			
		||||
		typ:             obj,
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *SchemaDescriptor) createScalar(name string, description string) *ObjectDescriptor {
 | 
			
		||||
	obj := &ObjectDescriptor{
 | 
			
		||||
		Definition: &ast.Definition{
 | 
			
		||||
			Kind:        ast.Scalar,
 | 
			
		||||
			Description: description,
 | 
			
		||||
			Name:        name,
 | 
			
		||||
			Position:    &ast.Position{},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	s.objects = append(s.objects, obj)
 | 
			
		||||
	return obj
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *SchemaDescriptor) createUnion(oneof *desc.OneOfDescriptor) (*FieldDescriptor, error) {
 | 
			
		||||
	var types []string
 | 
			
		||||
	var objTypes []*ObjectDescriptor
 | 
			
		||||
	for _, choice := range oneof.GetChoices() {
 | 
			
		||||
		obj, err := s.CreateObjects(resolveFieldType(choice), false)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		f, err := s.createField(choice, obj)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		obj = &ObjectDescriptor{
 | 
			
		||||
			Definition: &ast.Definition{
 | 
			
		||||
				Kind:        ast.Object,
 | 
			
		||||
				Description: getDescription(f),
 | 
			
		||||
				Name:        s.uniqueName(choice, false),
 | 
			
		||||
				Fields:      ast.FieldList{f.FieldDefinition},
 | 
			
		||||
				Position:    &ast.Position{},
 | 
			
		||||
			},
 | 
			
		||||
			Descriptor: f,
 | 
			
		||||
			fields:     []*FieldDescriptor{f},
 | 
			
		||||
			fieldNames: map[string]*FieldDescriptor{},
 | 
			
		||||
		}
 | 
			
		||||
		s.objects = append(s.objects, obj)
 | 
			
		||||
		types = append(types, obj.Name)
 | 
			
		||||
		objTypes = append(objTypes, obj)
 | 
			
		||||
	}
 | 
			
		||||
	obj := &ObjectDescriptor{
 | 
			
		||||
		Definition: &ast.Definition{
 | 
			
		||||
			Kind:        ast.Union,
 | 
			
		||||
			Description: getDescription(oneof),
 | 
			
		||||
			Name:        s.uniqueName(oneof, false),
 | 
			
		||||
			Types:       types,
 | 
			
		||||
			Position:    &ast.Position{},
 | 
			
		||||
		},
 | 
			
		||||
		Descriptor: oneof,
 | 
			
		||||
		types:      objTypes,
 | 
			
		||||
	}
 | 
			
		||||
	s.objects = append(s.objects, obj)
 | 
			
		||||
	name := ToLowerFirst(CamelCase(oneof.GetName()))
 | 
			
		||||
	opts := GraphqlOneofOptions(oneof.AsOneofDescriptorProto().GetOptions())
 | 
			
		||||
	if opts.GetName() != "" {
 | 
			
		||||
		name = opts.GetName()
 | 
			
		||||
	}
 | 
			
		||||
	return &FieldDescriptor{
 | 
			
		||||
		FieldDefinition: &ast.FieldDefinition{
 | 
			
		||||
			Description: getDescription(oneof),
 | 
			
		||||
			Name:        name,
 | 
			
		||||
			Type:        ast.NamedType(obj.Name, &ast.Position{}),
 | 
			
		||||
			Position:    &ast.Position{},
 | 
			
		||||
		},
 | 
			
		||||
		FieldDescriptor: nil,
 | 
			
		||||
		typ:             obj,
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isRepeated(field *desc.FieldDescriptor) bool {
 | 
			
		||||
	return field.GetLabel() == descriptor.FieldDescriptorProto_LABEL_REPEATED
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isRequired(field *desc.FieldDescriptor) bool {
 | 
			
		||||
	if v := GraphqlFieldOptions(field.AsFieldDescriptorProto().GetOptions()); v != nil {
 | 
			
		||||
		return v.GetRequired()
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	ScalarInt     = "Int"
 | 
			
		||||
	ScalarFloat   = "Float"
 | 
			
		||||
	ScalarString  = "String"
 | 
			
		||||
	ScalarBoolean = "Boolean"
 | 
			
		||||
	ScalarID      = "ID"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var graphqlReservedNames = []string{"__Directive", "__Type", "__Field", "__EnumValue", "__InputValue", "__Schema", "Int", "Float", "String", "Boolean", "ID"}
 | 
			
		||||
							
								
								
									
										30
									
								
								graphql/goreference.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								graphql/goreference.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
			
		||||
package generator
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"google.golang.org/protobuf/compiler/protogen"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type GoRef interface {
 | 
			
		||||
	FindGoField(field string) *protogen.Field
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewGoRef(p *protogen.Plugin) (GoRef, error) {
 | 
			
		||||
	return goRef{p}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type goRef struct {
 | 
			
		||||
	*protogen.Plugin
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (g goRef) FindGoField(field string) *protogen.Field {
 | 
			
		||||
	for _, file := range g.Files {
 | 
			
		||||
		for _, msg := range file.Messages {
 | 
			
		||||
			for _, f := range msg.Fields {
 | 
			
		||||
				if string(f.Desc.FullName()) == field {
 | 
			
		||||
					return f
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										139
									
								
								graphql/registry.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								graphql/registry.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,139 @@
 | 
			
		||||
package generator
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"sync"
 | 
			
		||||
 | 
			
		||||
	"github.com/jhump/protoreflect/desc"
 | 
			
		||||
	"github.com/vektah/gqlparser/v2/ast"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Registry interface {
 | 
			
		||||
	FindMethodByName(op ast.Operation, name string) *desc.MethodDescriptor
 | 
			
		||||
	FindObjectByName(name string) *desc.MessageDescriptor
 | 
			
		||||
 | 
			
		||||
	// FindObjectByFullyQualifiedName TODO maybe find a better way to get ast definition
 | 
			
		||||
	FindObjectByFullyQualifiedName(fqn string) (*desc.MessageDescriptor, *ast.Definition)
 | 
			
		||||
 | 
			
		||||
	// FindFieldByName given the proto Descriptor and the graphql field name get the the proto FieldDescriptor
 | 
			
		||||
	FindFieldByName(msg desc.Descriptor, name string) *desc.FieldDescriptor
 | 
			
		||||
 | 
			
		||||
	// FindUnionFieldByMessageFQNAndName given the proto Descriptor and the graphql field name get the the proto FieldDescriptor
 | 
			
		||||
	FindUnionFieldByMessageFQNAndName(fqn, name string) *desc.FieldDescriptor
 | 
			
		||||
 | 
			
		||||
	FindGraphqlFieldByProtoField(msg *ast.Definition, name string) *ast.FieldDefinition
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewRegistry(files SchemaDescriptorList) Registry {
 | 
			
		||||
	v := &repository{
 | 
			
		||||
		mu:                       &sync.RWMutex{},
 | 
			
		||||
		methodsByName:            map[ast.Operation]map[string]*desc.MethodDescriptor{},
 | 
			
		||||
		objectsByName:            map[string]*desc.MessageDescriptor{},
 | 
			
		||||
		objectsByFQN:             map[string]*ObjectDescriptor{},
 | 
			
		||||
		graphqlFieldsByName:      map[desc.Descriptor]map[string]*desc.FieldDescriptor{},
 | 
			
		||||
		graphqlUnionFieldsByName: map[string]map[string]*desc.FieldDescriptor{},
 | 
			
		||||
		protoFieldsByName:        map[*ast.Definition]map[string]*ast.FieldDefinition{},
 | 
			
		||||
	}
 | 
			
		||||
	for _, f := range files {
 | 
			
		||||
		v.methodsByName[ast.Mutation] = map[string]*desc.MethodDescriptor{}
 | 
			
		||||
		for _, m := range f.GetMutation().Methods() {
 | 
			
		||||
			v.methodsByName[ast.Mutation][m.Name] = m.MethodDescriptor
 | 
			
		||||
		}
 | 
			
		||||
		v.methodsByName[ast.Query] = map[string]*desc.MethodDescriptor{}
 | 
			
		||||
		for _, m := range f.GetQuery().Methods() {
 | 
			
		||||
			v.methodsByName[ast.Query][m.Name] = m.MethodDescriptor
 | 
			
		||||
		}
 | 
			
		||||
		v.methodsByName[ast.Subscription] = map[string]*desc.MethodDescriptor{}
 | 
			
		||||
		for _, m := range f.GetSubscription().Methods() {
 | 
			
		||||
			v.methodsByName[ast.Subscription][m.Name] = m.MethodDescriptor
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	for _, f := range files {
 | 
			
		||||
		for _, m := range f.Objects() {
 | 
			
		||||
			switch m.Kind {
 | 
			
		||||
			case ast.Union:
 | 
			
		||||
				fqn := m.Descriptor.GetParent().GetFullyQualifiedName()
 | 
			
		||||
				if _, ok := v.graphqlUnionFieldsByName[fqn]; !ok {
 | 
			
		||||
					v.graphqlUnionFieldsByName[fqn] = map[string]*desc.FieldDescriptor{}
 | 
			
		||||
				}
 | 
			
		||||
				for _, tt := range m.GetTypes() {
 | 
			
		||||
					for _, f := range tt.GetFields() {
 | 
			
		||||
						v.graphqlUnionFieldsByName[fqn][f.Name] = f.FieldDescriptor
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			case ast.Object:
 | 
			
		||||
				v.protoFieldsByName[m.Definition] = map[string]*ast.FieldDefinition{}
 | 
			
		||||
				for _, f := range m.GetFields() {
 | 
			
		||||
					if f.FieldDescriptor != nil {
 | 
			
		||||
						v.protoFieldsByName[m.Definition][f.FieldDescriptor.GetName()] = f.FieldDefinition
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			case ast.InputObject:
 | 
			
		||||
				v.graphqlFieldsByName[m.Descriptor] = map[string]*desc.FieldDescriptor{}
 | 
			
		||||
				for _, f := range m.GetFields() {
 | 
			
		||||
					v.graphqlFieldsByName[m.Descriptor][f.Name] = f.FieldDescriptor
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			switch msgDesc := m.Descriptor.(type) {
 | 
			
		||||
			case *desc.MessageDescriptor:
 | 
			
		||||
				v.objectsByFQN[m.GetFullyQualifiedName()] = m
 | 
			
		||||
				v.objectsByName[m.Name] = msgDesc
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return v
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type repository struct {
 | 
			
		||||
	files SchemaDescriptorList
 | 
			
		||||
	mu    *sync.RWMutex
 | 
			
		||||
 | 
			
		||||
	methodsByName            map[ast.Operation]map[string]*desc.MethodDescriptor
 | 
			
		||||
	objectsByName            map[string]*desc.MessageDescriptor
 | 
			
		||||
	objectsByFQN             map[string]*ObjectDescriptor
 | 
			
		||||
	graphqlFieldsByName      map[desc.Descriptor]map[string]*desc.FieldDescriptor
 | 
			
		||||
	protoFieldsByName        map[*ast.Definition]map[string]*ast.FieldDefinition
 | 
			
		||||
	graphqlUnionFieldsByName map[string]map[string]*desc.FieldDescriptor
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r repository) FindMethodByName(op ast.Operation, name string) *desc.MethodDescriptor {
 | 
			
		||||
	r.mu.RLock()
 | 
			
		||||
	defer r.mu.RUnlock()
 | 
			
		||||
	m, _ := r.methodsByName[op][name]
 | 
			
		||||
	return m
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r repository) FindObjectByName(name string) *desc.MessageDescriptor {
 | 
			
		||||
	r.mu.RLock()
 | 
			
		||||
	defer r.mu.RUnlock()
 | 
			
		||||
	o, _ := r.objectsByName[name]
 | 
			
		||||
	return o
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r repository) FindObjectByFullyQualifiedName(fqn string) (*desc.MessageDescriptor, *ast.Definition) {
 | 
			
		||||
	r.mu.RLock()
 | 
			
		||||
	defer r.mu.RUnlock()
 | 
			
		||||
	o, _ := r.objectsByFQN[fqn]
 | 
			
		||||
	msg, _ := o.Descriptor.(*desc.MessageDescriptor)
 | 
			
		||||
	return msg, o.Definition
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r repository) FindFieldByName(msg desc.Descriptor, name string) *desc.FieldDescriptor {
 | 
			
		||||
	r.mu.RLock()
 | 
			
		||||
	defer r.mu.RUnlock()
 | 
			
		||||
	f, _ := r.graphqlFieldsByName[msg][name]
 | 
			
		||||
	return f
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r repository) FindUnionFieldByMessageFQNAndName(fqn, name string) *desc.FieldDescriptor {
 | 
			
		||||
	r.mu.RLock()
 | 
			
		||||
	defer r.mu.RUnlock()
 | 
			
		||||
	f, _ := r.graphqlUnionFieldsByName[fqn][name]
 | 
			
		||||
	return f
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r repository) FindGraphqlFieldByProtoField(msg *ast.Definition, name string) *ast.FieldDefinition {
 | 
			
		||||
	r.mu.RLock()
 | 
			
		||||
	defer r.mu.RUnlock()
 | 
			
		||||
	f, _ := r.protoFieldsByName[msg][name]
 | 
			
		||||
	return f
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										291
									
								
								graphql/utils.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										291
									
								
								graphql/utils.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,291 @@
 | 
			
		||||
package generator
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"strings"
 | 
			
		||||
	"unicode"
 | 
			
		||||
	"unicode/utf8"
 | 
			
		||||
 | 
			
		||||
	"github.com/jhump/protoreflect/desc"
 | 
			
		||||
	"google.golang.org/protobuf/proto"
 | 
			
		||||
	descriptor "google.golang.org/protobuf/types/descriptorpb"
 | 
			
		||||
	"google.golang.org/protobuf/types/pluginpb"
 | 
			
		||||
 | 
			
		||||
	gqlpb "go.unistack.org/micro-proto/v3/graphql"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func GraphqlMethodOptions(opts proto.Message) *gqlpb.Rpc {
 | 
			
		||||
	if opts != nil {
 | 
			
		||||
		v := proto.GetExtension(opts, gqlpb.E_Rpc)
 | 
			
		||||
		if v != nil {
 | 
			
		||||
			return v.(*gqlpb.Rpc)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GraphqlServiceOptions(opts proto.Message) *gqlpb.Svc {
 | 
			
		||||
	if opts != nil {
 | 
			
		||||
		v := proto.GetExtension(opts, gqlpb.E_Svc)
 | 
			
		||||
		if v != nil {
 | 
			
		||||
			return v.(*gqlpb.Svc)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GraphqlFieldOptions(opts proto.Message) *gqlpb.Field {
 | 
			
		||||
	if opts != nil {
 | 
			
		||||
		v := proto.GetExtension(opts, gqlpb.E_Field)
 | 
			
		||||
		if v != nil && v.(*gqlpb.Field) != nil {
 | 
			
		||||
			return v.(*gqlpb.Field)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GraphqlOneofOptions(opts proto.Message) *gqlpb.Oneof {
 | 
			
		||||
	if opts != nil {
 | 
			
		||||
		v := proto.GetExtension(opts, gqlpb.E_Oneof)
 | 
			
		||||
		if v != nil && v.(*gqlpb.Oneof) != nil {
 | 
			
		||||
			return v.(*gqlpb.Oneof)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GoCamelCase camel-cases a protobuf name for use as a Go identifier.
 | 
			
		||||
//
 | 
			
		||||
// If there is an interior underscore followed by a lower case letter,
 | 
			
		||||
// drop the underscore and convert the letter to upper case.
 | 
			
		||||
func GoCamelCase(s string) string {
 | 
			
		||||
	// Invariant: if the next letter is lower case, it must be converted
 | 
			
		||||
	// to upper case.
 | 
			
		||||
	// That is, we process a word at a time, where words are marked by _ or
 | 
			
		||||
	// upper case letter. Digits are treated as words.
 | 
			
		||||
	var b []byte
 | 
			
		||||
	for i := 0; i < len(s); i++ {
 | 
			
		||||
		c := s[i]
 | 
			
		||||
		switch {
 | 
			
		||||
		case c == '.' && i+1 < len(s) && isASCIILower(s[i+1]):
 | 
			
		||||
			// Skip over '.' in ".{{lowercase}}".
 | 
			
		||||
		case c == '.':
 | 
			
		||||
			b = append(b, '_') // convert '.' to '_'
 | 
			
		||||
		case c == '_' && (i == 0 || s[i-1] == '.'):
 | 
			
		||||
			// Convert initial '_' to ensure we start with a capital letter.
 | 
			
		||||
			// Do the same for '_' after '.' to match historic behavior.
 | 
			
		||||
			b = append(b, 'X') // convert '_' to 'X'
 | 
			
		||||
		case c == '_' && i+1 < len(s) && isASCIILower(s[i+1]):
 | 
			
		||||
			// Skip over '_' in "_{{lowercase}}".
 | 
			
		||||
		case isASCIIDigit(c):
 | 
			
		||||
			b = append(b, c)
 | 
			
		||||
		default:
 | 
			
		||||
			// Assume we have a letter now - if not, it's a bogus identifier.
 | 
			
		||||
			// The next word is a sequence of characters that must start upper case.
 | 
			
		||||
			if isASCIILower(c) {
 | 
			
		||||
				c -= 'a' - 'A' // convert lowercase to uppercase
 | 
			
		||||
			}
 | 
			
		||||
			b = append(b, c)
 | 
			
		||||
 | 
			
		||||
			// Accept lower case sequence that follows.
 | 
			
		||||
			for ; i+1 < len(s) && isASCIILower(s[i+1]); i++ {
 | 
			
		||||
				b = append(b, s[i+1])
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return string(b)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GetRequestType(rpcOpts *gqlpb.Rpc, svcOpts *gqlpb.Svc) gqlpb.Type {
 | 
			
		||||
	if rpcOpts != nil && rpcOpts.Type != nil {
 | 
			
		||||
		return *rpcOpts.Type
 | 
			
		||||
	}
 | 
			
		||||
	if svcOpts != nil && svcOpts.Type != nil {
 | 
			
		||||
		return *svcOpts.Type
 | 
			
		||||
	}
 | 
			
		||||
	return gqlpb.Type_DEFAULT
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func CreateDescriptorsFromProto(req *pluginpb.CodeGeneratorRequest) (descs []*desc.FileDescriptor, err error) {
 | 
			
		||||
	dd, err := desc.CreateFileDescriptorsFromSet(&descriptor.FileDescriptorSet{
 | 
			
		||||
		File: req.GetProtoFile(),
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	for _, d := range dd {
 | 
			
		||||
		for _, filename := range req.FileToGenerate {
 | 
			
		||||
			if filename == d.GetName() {
 | 
			
		||||
				descs = append(descs, d)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ResolveProtoFilesRecursively(descs []*desc.FileDescriptor) (files FileDescriptors) {
 | 
			
		||||
	for _, f := range descs {
 | 
			
		||||
		files = append(files, ResolveProtoFilesRecursively(f.GetDependencies())...)
 | 
			
		||||
		files = append(files, f)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return files
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type FileDescriptors []*desc.FileDescriptor
 | 
			
		||||
 | 
			
		||||
func (ds FileDescriptors) AsFileDescriptorProto() (files []*descriptor.FileDescriptorProto) {
 | 
			
		||||
	for _, d := range ds {
 | 
			
		||||
		files = append(files, d.AsFileDescriptorProto())
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Split splits the camelcase word and returns a list of words. It also
 | 
			
		||||
// supports digits. Both lower camel case and upper camel case are supported.
 | 
			
		||||
// For more info please check: http://en.wikipedia.org/wiki/CamelCase
 | 
			
		||||
//
 | 
			
		||||
// Examples
 | 
			
		||||
//
 | 
			
		||||
//	"" =>                     [""]
 | 
			
		||||
//	"lowercase" =>            ["lowercase"]
 | 
			
		||||
//	"Class" =>                ["Class"]
 | 
			
		||||
//	"MyClass" =>              ["My", "Class"]
 | 
			
		||||
//	"MyC" =>                  ["My", "C"]
 | 
			
		||||
//	"HTML" =>                 ["HTML"]
 | 
			
		||||
//	"PDFLoader" =>            ["PDF", "Loader"]
 | 
			
		||||
//	"AString" =>              ["A", "String"]
 | 
			
		||||
//	"SimpleXMLParser" =>      ["Simple", "XML", "Parser"]
 | 
			
		||||
//	"vimRPCPlugin" =>         ["vim", "RPC", "Plugin"]
 | 
			
		||||
//	"GL11Version" =>          ["GL", "11", "Version"]
 | 
			
		||||
//	"99Bottles" =>            ["99", "Bottles"]
 | 
			
		||||
//	"May5" =>                 ["May", "5"]
 | 
			
		||||
//	"BFG9000" =>              ["BFG", "9000"]
 | 
			
		||||
//	"BöseÜberraschung" =>     ["Böse", "Überraschung"]
 | 
			
		||||
//	"Two  spaces" =>          ["Two", "  ", "spaces"]
 | 
			
		||||
//	"BadUTF8\xe2\xe2\xa1" =>  ["BadUTF8\xe2\xe2\xa1"]
 | 
			
		||||
//
 | 
			
		||||
// Splitting rules
 | 
			
		||||
//
 | 
			
		||||
//  1. If string is not valid UTF-8, return it without splitting as
 | 
			
		||||
//     single item array.
 | 
			
		||||
//  2. Assign all unicode characters into one of 4 sets: lower case
 | 
			
		||||
//     letters, upper case letters, numbers, and all other characters.
 | 
			
		||||
//  3. Iterate through characters of string, introducing splits
 | 
			
		||||
//     between adjacent characters that belong to different sets.
 | 
			
		||||
//  4. Iterate through array of split strings, and if a given string
 | 
			
		||||
//     is upper case:
 | 
			
		||||
//     if subsequent string is lower case:
 | 
			
		||||
//     move last character of upper case string to beginning of
 | 
			
		||||
//     lower case string
 | 
			
		||||
func SplitCamelCase(src string) (entries []string) {
 | 
			
		||||
	// don't split invalid utf8
 | 
			
		||||
	if !utf8.ValidString(src) {
 | 
			
		||||
		return []string{src}
 | 
			
		||||
	}
 | 
			
		||||
	entries = []string{}
 | 
			
		||||
	var runes [][]rune
 | 
			
		||||
	lastClass := 0
 | 
			
		||||
	class := 0
 | 
			
		||||
	// split into fields based on class of unicode character
 | 
			
		||||
	for _, r := range src {
 | 
			
		||||
		switch true {
 | 
			
		||||
		case unicode.IsLower(r):
 | 
			
		||||
			class = 1
 | 
			
		||||
		case unicode.IsUpper(r):
 | 
			
		||||
			class = 2
 | 
			
		||||
		case unicode.IsDigit(r):
 | 
			
		||||
			class = 3
 | 
			
		||||
		default:
 | 
			
		||||
			class = 4
 | 
			
		||||
		}
 | 
			
		||||
		if class == lastClass {
 | 
			
		||||
			runes[len(runes)-1] = append(runes[len(runes)-1], r)
 | 
			
		||||
		} else {
 | 
			
		||||
			runes = append(runes, []rune{r})
 | 
			
		||||
		}
 | 
			
		||||
		lastClass = class
 | 
			
		||||
	}
 | 
			
		||||
	// handle upper case -> lower case sequences, e.g.
 | 
			
		||||
	// "PDFL", "oader" -> "PDF", "Loader"
 | 
			
		||||
	for i := 0; i < len(runes)-1; i++ {
 | 
			
		||||
		if unicode.IsUpper(runes[i][0]) && unicode.IsLower(runes[i+1][0]) {
 | 
			
		||||
			runes[i+1] = append([]rune{runes[i][len(runes[i])-1]}, runes[i+1]...)
 | 
			
		||||
			runes[i] = runes[i][:len(runes[i])-1]
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	// construct []string from results
 | 
			
		||||
	for _, s := range runes {
 | 
			
		||||
		if len(s) > 0 {
 | 
			
		||||
			entries = append(entries, string(s))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CamelCase returns the CamelCased name.
 | 
			
		||||
// If there is an interior underscore followed by a lower case letter,
 | 
			
		||||
// drop the underscore and convert the letter to upper case.
 | 
			
		||||
// There is a remote possibility of this rewrite causing a name collision,
 | 
			
		||||
// but it's so remote we're prepared to pretend it's nonexistent - since the
 | 
			
		||||
// C++ generator lowercases names, it's extremely unlikely to have two fields
 | 
			
		||||
// with different capitalizations.
 | 
			
		||||
// In short, _my_field_name_2 becomes XMyFieldName_2.
 | 
			
		||||
func CamelCase(s string) string {
 | 
			
		||||
	if s == "" {
 | 
			
		||||
		return ""
 | 
			
		||||
	}
 | 
			
		||||
	t := make([]byte, 0, 32)
 | 
			
		||||
	i := 0
 | 
			
		||||
	if s[0] == '_' {
 | 
			
		||||
		// Need a capital letter; drop the '_'.
 | 
			
		||||
		t = append(t, 'X')
 | 
			
		||||
		i++
 | 
			
		||||
	}
 | 
			
		||||
	// Invariant: if the next letter is lower case, it must be converted
 | 
			
		||||
	// to upper case.
 | 
			
		||||
	// That is, we process a word at a time, where words are marked by _ or
 | 
			
		||||
	// upper case letter. Digits are treated as words.
 | 
			
		||||
	for ; i < len(s); i++ {
 | 
			
		||||
		c := s[i]
 | 
			
		||||
		if c == '_' && i+1 < len(s) && isASCIILower(s[i+1]) {
 | 
			
		||||
			continue // Skip the underscore in s.
 | 
			
		||||
		}
 | 
			
		||||
		if isASCIIDigit(c) {
 | 
			
		||||
			t = append(t, c)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		// Assume we have a letter now - if not, it's a bogus identifier.
 | 
			
		||||
		// The next word is a sequence of characters that must start upper case.
 | 
			
		||||
		if isASCIILower(c) {
 | 
			
		||||
			c ^= ' ' // Make it a capital letter.
 | 
			
		||||
		}
 | 
			
		||||
		t = append(t, c) // Guaranteed not lower case.
 | 
			
		||||
		// Accept lower case sequence that follows.
 | 
			
		||||
		for i+1 < len(s) && isASCIILower(s[i+1]) {
 | 
			
		||||
			i++
 | 
			
		||||
			t = append(t, s[i])
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return string(t)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CamelCaseSlice is like CamelCase, but the argument is a slice of strings to
 | 
			
		||||
// be joined with "_".
 | 
			
		||||
func CamelCaseSlice(elem []string) string { return CamelCase(strings.Join(elem, "_")) }
 | 
			
		||||
 | 
			
		||||
// Is c an ASCII lower-case letter?
 | 
			
		||||
func isASCIILower(c byte) bool {
 | 
			
		||||
	return 'a' <= c && c <= 'z'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Is c an ASCII digit?
 | 
			
		||||
func isASCIIDigit(c byte) bool {
 | 
			
		||||
	return '0' <= c && c <= '9'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ToLowerFirst(s string) string {
 | 
			
		||||
	if len(s) > 0 {
 | 
			
		||||
		return string(unicode.ToLower(rune(s[0]))) + s[1:]
 | 
			
		||||
	}
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										56
									
								
								graphqls.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								graphqls.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,56 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"os"
 | 
			
		||||
 | 
			
		||||
	"github.com/vektah/gqlparser/v2/formatter"
 | 
			
		||||
	generator "go.unistack.org/protoc-gen-go-micro/v3/graphql"
 | 
			
		||||
	"google.golang.org/protobuf/compiler/protogen"
 | 
			
		||||
	"google.golang.org/protobuf/proto"
 | 
			
		||||
	"google.golang.org/protobuf/types/pluginpb"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (g *Generator) graphqlsGenerate(plugin *protogen.Plugin) error {
 | 
			
		||||
	descs, err := generator.CreateDescriptorsFromProto(plugin.Request)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	gqlDesc, err := generator.NewSchemas(descs, false, false, plugin)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var outFiles []*pluginpb.CodeGeneratorResponse_File
 | 
			
		||||
 | 
			
		||||
	for _, schema := range gqlDesc {
 | 
			
		||||
		buf := &bytes.Buffer{}
 | 
			
		||||
		formatter.NewFormatter(buf).FormatSchema(schema.AsGraphql())
 | 
			
		||||
 | 
			
		||||
		outFiles = append(outFiles, &pluginpb.CodeGeneratorResponse_File{
 | 
			
		||||
			Name:    proto.String(g.graphqlFile),
 | 
			
		||||
			Content: proto.String(buf.String()),
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	res := &pluginpb.CodeGeneratorResponse{
 | 
			
		||||
		File:              outFiles,
 | 
			
		||||
		SupportedFeatures: proto.Uint64(uint64(pluginpb.CodeGeneratorResponse_FEATURE_PROTO3_OPTIONAL)),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		res.Error = proto.String(err.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	out, err := proto.Marshal(res)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if _, err := os.Stdout.Write(out); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										19
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								main.go
									
									
									
									
									
								
							@@ -15,9 +15,10 @@ var (
 | 
			
		||||
	flagDebug         = flagSet.Bool("debug", false, "debug output")
 | 
			
		||||
	flagStandalone    = flagSet.Bool("standalone", false, "generate file to standalone dir")
 | 
			
		||||
	flagFieldaligment = flagSet.Bool("fieldaligment", false, "align struct fields in generated code")
 | 
			
		||||
	flagComponents    = flagSet.String("components", "micro|rpc|http|client|server|openapiv3", "specify components to generate")
 | 
			
		||||
	flagComponents    = flagSet.String("components", "micro|rpc|http|client|server|openapiv3|graphql", "specify components to generate")
 | 
			
		||||
	flagTagPath       = flagSet.String("tag_path", "", "tag rewriting dir")
 | 
			
		||||
	flagOpenapiFile   = flagSet.String("openapi_file", "apidocs.swagger.json", "openapi file name")
 | 
			
		||||
	flagOpenapiFile   = flagSet.String("openapi_file", "apidocs.swagger.yaml", "openapi file name")
 | 
			
		||||
	flagGraphqlFile   = flagSet.String("graphql_file", "schema.graphqls", "graphql file name")
 | 
			
		||||
	flagReflection    = flagSet.Bool("reflection", false, "enable server reflection support")
 | 
			
		||||
	flagHelp          = flagSet.Bool("help", false, "display help message")
 | 
			
		||||
)
 | 
			
		||||
@@ -27,7 +28,7 @@ func main() {
 | 
			
		||||
		ParamFunc: flagSet.Set,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	flagSet.Parse(os.Args[1:])
 | 
			
		||||
	_ = flagSet.Parse(os.Args[1:])
 | 
			
		||||
 | 
			
		||||
	if *flagHelp {
 | 
			
		||||
		flagSet.PrintDefaults()
 | 
			
		||||
@@ -46,6 +47,7 @@ type Generator struct {
 | 
			
		||||
	fieldaligment bool
 | 
			
		||||
	tagPath       string
 | 
			
		||||
	openapiFile   string
 | 
			
		||||
	graphqlFile   string
 | 
			
		||||
	reflection    bool
 | 
			
		||||
	plugin        *protogen.Plugin
 | 
			
		||||
}
 | 
			
		||||
@@ -60,11 +62,12 @@ func (g *Generator) Generate(plugin *protogen.Plugin) error {
 | 
			
		||||
	g.fieldaligment = *flagFieldaligment
 | 
			
		||||
	g.tagPath = *flagTagPath
 | 
			
		||||
	g.openapiFile = *flagOpenapiFile
 | 
			
		||||
	g.graphqlFile = *flagGraphqlFile
 | 
			
		||||
	g.reflection = *flagReflection
 | 
			
		||||
	plugin.SupportedFeatures = uint64(pluginpb.CodeGeneratorResponse_FEATURE_PROTO3_OPTIONAL)
 | 
			
		||||
 | 
			
		||||
	var genClient bool
 | 
			
		||||
	var genServer bool
 | 
			
		||||
	genClient := true
 | 
			
		||||
	genServer := true
 | 
			
		||||
	var genNone bool
 | 
			
		||||
 | 
			
		||||
	if strings.Contains(g.components, "server") {
 | 
			
		||||
@@ -102,7 +105,11 @@ func (g *Generator) Generate(plugin *protogen.Plugin) error {
 | 
			
		||||
		case "chi":
 | 
			
		||||
			err = g.chiGenerate(component, plugin)
 | 
			
		||||
		case "openapiv3":
 | 
			
		||||
			err = g.openapiv3Generate(component, plugin)
 | 
			
		||||
			err = g.openapiv3Generate(plugin)
 | 
			
		||||
		case "graphqls":
 | 
			
		||||
			err = g.graphqlsGenerate(plugin)
 | 
			
		||||
		case "graphql":
 | 
			
		||||
			err = g.graphqlGenerate(plugin)
 | 
			
		||||
		case "none":
 | 
			
		||||
			break
 | 
			
		||||
		default:
 | 
			
		||||
 
 | 
			
		||||
@@ -24,8 +24,8 @@ import (
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
 | 
			
		||||
	"go.unistack.org/micro-proto/v4/api"
 | 
			
		||||
	v3 "go.unistack.org/micro-proto/v4/openapiv3"
 | 
			
		||||
	"go.unistack.org/micro-proto/v3/api"
 | 
			
		||||
	v3 "go.unistack.org/micro-proto/v3/openapiv3"
 | 
			
		||||
	"google.golang.org/protobuf/compiler/protogen"
 | 
			
		||||
	"google.golang.org/protobuf/proto"
 | 
			
		||||
	"google.golang.org/protobuf/reflect/protoreflect"
 | 
			
		||||
@@ -62,7 +62,7 @@ func protofilesAdd(plugin *protogen.Plugin) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// openapiv3Generate creates a new generator for a protoc plugin invocation.
 | 
			
		||||
func (g *Generator) openapiv3Generate(component string, plugin *protogen.Plugin) error {
 | 
			
		||||
func (g *Generator) openapiv3Generate(plugin *protogen.Plugin) error {
 | 
			
		||||
	og := &openapiv3Generator{
 | 
			
		||||
		circularDepth: 2,
 | 
			
		||||
		plugin:        plugin,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import v3 "go.unistack.org/micro-proto/v4/openapiv3"
 | 
			
		||||
import v3 "go.unistack.org/micro-proto/v3/openapiv3"
 | 
			
		||||
 | 
			
		||||
func getMediaType(eopt interface{}) string {
 | 
			
		||||
	ctype := "application/json"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										7
									
								
								tools.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								tools.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
//go:build ignore
 | 
			
		||||
 | 
			
		||||
package tools
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	_ "go.unistack.org/micro-proto/v3/graphql"
 | 
			
		||||
)
 | 
			
		||||
							
								
								
									
										75
									
								
								util.go
									
									
									
									
									
								
							
							
						
						
									
										75
									
								
								util.go
									
									
									
									
									
								
							@@ -8,9 +8,9 @@ import (
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	api_options "go.unistack.org/micro-proto/v4/api"
 | 
			
		||||
	v2 "go.unistack.org/micro-proto/v4/openapiv2"
 | 
			
		||||
	v3 "go.unistack.org/micro-proto/v4/openapiv3"
 | 
			
		||||
	api_options "go.unistack.org/micro-proto/v3/api"
 | 
			
		||||
	v2 "go.unistack.org/micro-proto/v3/openapiv2"
 | 
			
		||||
	v3 "go.unistack.org/micro-proto/v3/openapiv3"
 | 
			
		||||
	"google.golang.org/protobuf/compiler/protogen"
 | 
			
		||||
	"google.golang.org/protobuf/proto"
 | 
			
		||||
)
 | 
			
		||||
@@ -32,7 +32,7 @@ func unexport(s string) string {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (g *Generator) generateServiceClient(gfile *protogen.GeneratedFile, file *protogen.File, service *protogen.Service) {
 | 
			
		||||
	serviceName := getServiceName(service)
 | 
			
		||||
	serviceName := service.GoName
 | 
			
		||||
	// if rule, ok := getMicroApiService(service); ok {
 | 
			
		||||
	//		gfile.P("// client wrappers ", strings.Join(rule.ClientWrappers, ", "))
 | 
			
		||||
	//	}
 | 
			
		||||
@@ -52,7 +52,7 @@ func (g *Generator) generateServiceClient(gfile *protogen.GeneratedFile, file *p
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (g *Generator) generateServiceClientMethods(gfile *protogen.GeneratedFile, service *protogen.Service, component string) {
 | 
			
		||||
	serviceName := getServiceName(service)
 | 
			
		||||
	serviceName := service.GoName
 | 
			
		||||
	for _, method := range service.Methods {
 | 
			
		||||
		methodName := fmt.Sprintf("%s.%s", serviceName, method.GoName)
 | 
			
		||||
		if component == "drpc" {
 | 
			
		||||
@@ -324,7 +324,7 @@ func (g *Generator) generateServiceClientMethods(gfile *protogen.GeneratedFile,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (g *Generator) generateServiceServer(gfile *protogen.GeneratedFile, file *protogen.File, service *protogen.Service) {
 | 
			
		||||
	serviceName := getServiceName(service)
 | 
			
		||||
	serviceName := service.GoName
 | 
			
		||||
	gfile.P("type ", unexport(serviceName), "Server struct {")
 | 
			
		||||
	if g.standalone {
 | 
			
		||||
		gfile.P(file.GoImportPath.Ident(serviceName), "Server")
 | 
			
		||||
@@ -336,7 +336,7 @@ func (g *Generator) generateServiceServer(gfile *protogen.GeneratedFile, file *p
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (g *Generator) generateServiceServerMethods(gfile *protogen.GeneratedFile, service *protogen.Service) {
 | 
			
		||||
	serviceName := getServiceName(service)
 | 
			
		||||
	serviceName := service.GoName
 | 
			
		||||
	for _, method := range service.Methods {
 | 
			
		||||
		generateServerFuncSignature(gfile, serviceName, method, true)
 | 
			
		||||
		if rule, ok := getMicroApiMethod(method); ok {
 | 
			
		||||
@@ -486,11 +486,11 @@ func (g *Generator) generateServiceServerMethods(gfile *protogen.GeneratedFile,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (g *Generator) generateServiceRegister(gfile *protogen.GeneratedFile, file *protogen.File, service *protogen.Service, component string) {
 | 
			
		||||
	serviceName := getServiceName(service)
 | 
			
		||||
	serviceName := service.GoName
 | 
			
		||||
	if g.standalone {
 | 
			
		||||
		gfile.P("func Register", serviceName, "Server(s ", microServerPackage.Ident("Server"), ", sh ", file.GoImportPath.Ident(serviceName), "Server, opts ...", microOptionsPackage.Ident("Option"), ") error {")
 | 
			
		||||
		gfile.P("func Register", serviceName, "Server(s ", microServerPackage.Ident("Server"), ", sh ", file.GoImportPath.Ident(serviceName), "Server, opts ...", microServerPackage.Ident("HandlerOption"), ") error {")
 | 
			
		||||
	} else {
 | 
			
		||||
		gfile.P("func Register", serviceName, "Server(s ", microServerPackage.Ident("Server"), ", sh ", serviceName, "Server, opts ...", microOptionsPackage.Ident("Option"), ") error {")
 | 
			
		||||
		gfile.P("func Register", serviceName, "Server(s ", microServerPackage.Ident("Server"), ", sh ", serviceName, "Server, opts ...", microServerPackage.Ident("HandlerOption"), ") error {")
 | 
			
		||||
	}
 | 
			
		||||
	gfile.P("type ", unexport(serviceName), " interface {")
 | 
			
		||||
	for _, method := range service.Methods {
 | 
			
		||||
@@ -501,7 +501,7 @@ func (g *Generator) generateServiceRegister(gfile *protogen.GeneratedFile, file
 | 
			
		||||
	gfile.P(unexport(serviceName))
 | 
			
		||||
	gfile.P("}")
 | 
			
		||||
	gfile.P("h := &", unexport(serviceName), "Server{sh}")
 | 
			
		||||
	gfile.P("var nopts []", microOptionsPackage.Ident("Option"))
 | 
			
		||||
	gfile.P("var nopts []", microServerPackage.Ident("HandlerOption"))
 | 
			
		||||
	if component == "http" {
 | 
			
		||||
		//	if g.standalone {
 | 
			
		||||
		//		gfile.P("nopts = append(nopts, ", microServerHttpPackage.Ident("HandlerEndpoints"), "(", file.GoImportPath.Ident(serviceName), "ServerEndpoints))")
 | 
			
		||||
@@ -509,7 +509,7 @@ func (g *Generator) generateServiceRegister(gfile *protogen.GeneratedFile, file
 | 
			
		||||
		gfile.P("nopts = append(nopts, ", microServerHttpPackage.Ident("HandlerEndpoints"), "(", serviceName, "ServerEndpoints))")
 | 
			
		||||
		//	}
 | 
			
		||||
	}
 | 
			
		||||
	gfile.P("return s.Handle(&", serviceName, "{h}, append(nopts, opts...)...)")
 | 
			
		||||
	gfile.P("return s.Handle(s.NewHandler(&", serviceName, "{h}, append(nopts, opts...)...))")
 | 
			
		||||
	gfile.P("}")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -568,11 +568,11 @@ func (g *Generator) generateClientFuncSignature(gfile *protogen.GeneratedFile, s
 | 
			
		||||
	if !method.Desc.IsStreamingClient() {
 | 
			
		||||
		args = append(args, "req *", gfile.QualifiedGoIdent(method.Input.GoIdent), ", ")
 | 
			
		||||
	}
 | 
			
		||||
	args = append(args, "opts ...", microOptionsPackage.Ident("Option"), ") (")
 | 
			
		||||
	args = append(args, "opts ...", microClientPackage.Ident("CallOption"), ") (")
 | 
			
		||||
	if !method.Desc.IsStreamingClient() && !method.Desc.IsStreamingServer() {
 | 
			
		||||
		args = append(args, "*", gfile.QualifiedGoIdent(method.Output.GoIdent))
 | 
			
		||||
	} else {
 | 
			
		||||
		args = append(args, gfile.QualifiedGoIdent(protogen.GoIdent{GoName: serviceName + "_" + method.GoName + "Client", GoImportPath: method.Output.GoIdent.GoImportPath}))
 | 
			
		||||
		args = append(args, serviceName, "_", method.GoName, "Client")
 | 
			
		||||
	}
 | 
			
		||||
	args = append(args, ", error) {")
 | 
			
		||||
	gfile.P(args...)
 | 
			
		||||
@@ -586,7 +586,7 @@ func generateClientSignature(gfile *protogen.GeneratedFile, serviceName string,
 | 
			
		||||
	if !method.Desc.IsStreamingClient() {
 | 
			
		||||
		args = append(args, "req *", gfile.QualifiedGoIdent(method.Input.GoIdent), ", ")
 | 
			
		||||
	}
 | 
			
		||||
	args = append(args, "opts ...", microOptionsPackage.Ident("Option"), ") (")
 | 
			
		||||
	args = append(args, "opts ...", microClientPackage.Ident("CallOption"), ") (")
 | 
			
		||||
	if !method.Desc.IsStreamingClient() && !method.Desc.IsStreamingServer() {
 | 
			
		||||
		args = append(args, "*", gfile.QualifiedGoIdent(method.Output.GoIdent))
 | 
			
		||||
	} else {
 | 
			
		||||
@@ -597,7 +597,7 @@ func generateClientSignature(gfile *protogen.GeneratedFile, serviceName string,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (g *Generator) generateServiceClientInterface(gfile *protogen.GeneratedFile, service *protogen.Service) {
 | 
			
		||||
	serviceName := getServiceName(service)
 | 
			
		||||
	serviceName := service.GoName
 | 
			
		||||
	gfile.P("type ", serviceName, "Client interface {")
 | 
			
		||||
	for _, method := range service.Methods {
 | 
			
		||||
		generateClientSignature(gfile, serviceName, method)
 | 
			
		||||
@@ -607,7 +607,7 @@ func (g *Generator) generateServiceClientInterface(gfile *protogen.GeneratedFile
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (g *Generator) generateServiceServerInterface(gfile *protogen.GeneratedFile, service *protogen.Service) {
 | 
			
		||||
	serviceName := getServiceName(service)
 | 
			
		||||
	serviceName := service.GoName
 | 
			
		||||
	gfile.P("type ", serviceName, "Server interface {")
 | 
			
		||||
	for _, method := range service.Methods {
 | 
			
		||||
		generateServerSignature(gfile, serviceName, method, false)
 | 
			
		||||
@@ -617,7 +617,7 @@ func (g *Generator) generateServiceServerInterface(gfile *protogen.GeneratedFile
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (g *Generator) generateServiceClientStreamInterface(gfile *protogen.GeneratedFile, service *protogen.Service) {
 | 
			
		||||
	serviceName := getServiceName(service)
 | 
			
		||||
	serviceName := service.GoName
 | 
			
		||||
	for _, method := range service.Methods {
 | 
			
		||||
		if !method.Desc.IsStreamingClient() && !method.Desc.IsStreamingServer() {
 | 
			
		||||
			continue
 | 
			
		||||
@@ -645,7 +645,7 @@ func (g *Generator) generateServiceClientStreamInterface(gfile *protogen.Generat
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (g *Generator) generateServiceServerStreamInterface(gfile *protogen.GeneratedFile, service *protogen.Service) {
 | 
			
		||||
	serviceName := getServiceName(service)
 | 
			
		||||
	serviceName := service.GoName
 | 
			
		||||
	for _, method := range service.Methods {
 | 
			
		||||
		if !method.Desc.IsStreamingClient() && !method.Desc.IsStreamingServer() {
 | 
			
		||||
			continue
 | 
			
		||||
@@ -657,7 +657,7 @@ func (g *Generator) generateServiceServerStreamInterface(gfile *protogen.Generat
 | 
			
		||||
		gfile.P("RecvMsg(msg interface{}) error")
 | 
			
		||||
		if method.Desc.IsStreamingClient() && !method.Desc.IsStreamingServer() {
 | 
			
		||||
			gfile.P("SendAndClose(msg *", gfile.QualifiedGoIdent(method.Output.GoIdent), ") error")
 | 
			
		||||
			//	gfile.P("CloseSend() error")
 | 
			
		||||
			gfile.P("CloseSend() error")
 | 
			
		||||
		}
 | 
			
		||||
		gfile.P("Close() error")
 | 
			
		||||
		if method.Desc.IsStreamingClient() {
 | 
			
		||||
@@ -836,7 +836,7 @@ func getGoIdentByMessage(messages []*protogen.Message, msg string) (protogen.GoI
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (g *Generator) generateServiceDesc(gfile *protogen.GeneratedFile, file *protogen.File, service *protogen.Service) {
 | 
			
		||||
	serviceName := getServiceName(service)
 | 
			
		||||
	serviceName := service.GoName
 | 
			
		||||
 | 
			
		||||
	gfile.P("// ", serviceName, "_ServiceDesc", " is the ", grpcPackage.Ident("ServiceDesc"), " for ", serviceName, " service.")
 | 
			
		||||
	gfile.P("// It's only intended for direct use with ", grpcPackage.Ident("RegisterService"), ",")
 | 
			
		||||
@@ -878,7 +878,7 @@ func (g *Generator) generateServiceDesc(gfile *protogen.GeneratedFile, file *pro
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (g *Generator) generateServiceName(gfile *protogen.GeneratedFile, service *protogen.Service) {
 | 
			
		||||
	serviceName := getServiceName(service)
 | 
			
		||||
	serviceName := service.GoName
 | 
			
		||||
	gfile.P("var (")
 | 
			
		||||
	gfile.P(serviceName, "Name", "=", `"`, serviceName, `"`)
 | 
			
		||||
	gfile.P(")")
 | 
			
		||||
@@ -888,7 +888,7 @@ func (g *Generator) generateServiceEndpoints(gfile *protogen.GeneratedFile, serv
 | 
			
		||||
	if component != "http" {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	serviceName := getServiceName(service)
 | 
			
		||||
	serviceName := service.GoName
 | 
			
		||||
 | 
			
		||||
	gfile.P("var (")
 | 
			
		||||
	gfile.P(serviceName, "ServerEndpoints = []", microServerHttpPackage.Ident("EndpointMetadata"), "{")
 | 
			
		||||
@@ -914,13 +914,6 @@ func (g *Generator) generateServiceEndpoints(gfile *protogen.GeneratedFile, serv
 | 
			
		||||
	gfile.P(")")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getServiceName(s *protogen.Service) string {
 | 
			
		||||
	if strings.HasSuffix(s.GoName, "Service") {
 | 
			
		||||
		return s.GoName
 | 
			
		||||
	}
 | 
			
		||||
	return s.GoName + "Service"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (g *Generator) writeErrors(plugin *protogen.Plugin) error {
 | 
			
		||||
	errorsMap := make(map[string]struct{})
 | 
			
		||||
 | 
			
		||||
@@ -942,7 +935,9 @@ func (g *Generator) writeErrors(plugin *protogen.Plugin) error {
 | 
			
		||||
									if xref[0] == '.' {
 | 
			
		||||
										xref = xref[1:]
 | 
			
		||||
									}
 | 
			
		||||
									errorsMap[xref] = struct{}{}
 | 
			
		||||
									if g.fileMessage(plugin.Files, xref) {
 | 
			
		||||
										errorsMap[xref] = struct{}{}
 | 
			
		||||
									}
 | 
			
		||||
								}
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
@@ -964,7 +959,9 @@ func (g *Generator) writeErrors(plugin *protogen.Plugin) error {
 | 
			
		||||
									if xref[0] == '.' {
 | 
			
		||||
										xref = xref[1:]
 | 
			
		||||
									}
 | 
			
		||||
									errorsMap[xref] = struct{}{}
 | 
			
		||||
									if g.fileMessage(plugin.Files, xref) {
 | 
			
		||||
										errorsMap[xref] = struct{}{}
 | 
			
		||||
									}
 | 
			
		||||
								}
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
@@ -988,6 +985,7 @@ func (g *Generator) writeErrors(plugin *protogen.Plugin) error {
 | 
			
		||||
			if len(file.Services) == 0 {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			packageName = string(file.GoPackageName)
 | 
			
		||||
			importPath = file.GoImportPath
 | 
			
		||||
			break
 | 
			
		||||
@@ -1023,12 +1021,23 @@ func (g *Generator) writeErrors(plugin *protogen.Plugin) error {
 | 
			
		||||
				return fmt.Errorf("failed generate Error() string interface for %s message %s already have Error field", field.Location.SourceFile, msg.Desc.Name())
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		gfile.P(`func (m *`, msg.GoIdent.GoName, `) Error() string {`)
 | 
			
		||||
		gfile.P(`buf, _ := marshaler.Marshal(m)`)
 | 
			
		||||
		gfile.P("return string(buf)")
 | 
			
		||||
		gfile.P(`}`)
 | 
			
		||||
		// log.Printf("xref %#+v %v\n", msg.GoIdent.GoName, err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (g *Generator) fileMessage(files []*protogen.File, xref string) bool {
 | 
			
		||||
	for _, file := range files {
 | 
			
		||||
		for _, msg := range file.Messages {
 | 
			
		||||
			if xref == string(msg.Desc.FullName()) && file.Generate {
 | 
			
		||||
				return true
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										19
									
								
								variables.go
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								variables.go
									
									
									
									
									
								
							@@ -3,6 +3,8 @@ package main
 | 
			
		||||
import "google.golang.org/protobuf/compiler/protogen"
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	ioPackage              = protogen.GoImportPath("io")
 | 
			
		||||
	graphqlPackage         = protogen.GoImportPath("github.com/99designs/gqlgen/graphql")
 | 
			
		||||
	reflectPackage         = protogen.GoImportPath("reflect")
 | 
			
		||||
	stringsPackage         = protogen.GoImportPath("strings")
 | 
			
		||||
	fmtPackage             = protogen.GoImportPath("fmt")
 | 
			
		||||
@@ -11,17 +13,16 @@ var (
 | 
			
		||||
	gorillaMuxPackage      = protogen.GoImportPath("github.com/gorilla/mux")
 | 
			
		||||
	chiPackage             = protogen.GoImportPath("github.com/go-chi/chi/v5")
 | 
			
		||||
	chiMiddlewarePackage   = protogen.GoImportPath("github.com/go-chi/chi/v5/middleware")
 | 
			
		||||
	microMetadataPackage   = protogen.GoImportPath("go.unistack.org/micro/v4/metadata")
 | 
			
		||||
	microClientPackage     = protogen.GoImportPath("go.unistack.org/micro/v4/client")
 | 
			
		||||
	microServerPackage     = protogen.GoImportPath("go.unistack.org/micro/v4/server")
 | 
			
		||||
	microClientHttpPackage = protogen.GoImportPath("go.unistack.org/micro-client-http/v4")
 | 
			
		||||
	microServerHttpPackage = protogen.GoImportPath("go.unistack.org/micro-server-http/v4")
 | 
			
		||||
	microCodecPackage      = protogen.GoImportPath("go.unistack.org/micro-proto/v4/codec")
 | 
			
		||||
	microErrorsPackage     = protogen.GoImportPath("go.unistack.org/micro/v4/errors")
 | 
			
		||||
	microOptionsPackage    = protogen.GoImportPath("go.unistack.org/micro/v4/options")
 | 
			
		||||
	microMetadataPackage   = protogen.GoImportPath("go.unistack.org/micro/v3/metadata")
 | 
			
		||||
	microClientPackage     = protogen.GoImportPath("go.unistack.org/micro/v3/client")
 | 
			
		||||
	microServerPackage     = protogen.GoImportPath("go.unistack.org/micro/v3/server")
 | 
			
		||||
	microClientHttpPackage = protogen.GoImportPath("go.unistack.org/micro-client-http/v3")
 | 
			
		||||
	microServerHttpPackage = protogen.GoImportPath("go.unistack.org/micro-server-http/v3")
 | 
			
		||||
	microCodecPackage      = protogen.GoImportPath("go.unistack.org/micro-proto/v3/codec")
 | 
			
		||||
	microErrorsPackage     = protogen.GoImportPath("go.unistack.org/micro/v3/errors")
 | 
			
		||||
	grpcPackage            = protogen.GoImportPath("google.golang.org/grpc")
 | 
			
		||||
	protojsonPackage       = protogen.GoImportPath("google.golang.org/protobuf/encoding/protojson")
 | 
			
		||||
	timePackage            = protogen.GoImportPath("time")
 | 
			
		||||
	deprecationComment     = "// Deprecated: Do not use."
 | 
			
		||||
	versionComment         = "v4.0.2"
 | 
			
		||||
	versionComment         = "v3.10.4"
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user