moul / protoc-gen-gotemplate

:open_file_folder: generic protocol generator based on golang's text/template (grpc/protobuf)
https://manfred.life/protoc-gen-gotemplate
MIT License
437 stars 70 forks source link

Messages types from imported proto files #92

Closed bruth closed 6 years ago

bruth commented 6 years ago

Hi! I have the following service.proto file that imports from another proto file:

syntax = "proto3";
package main;

option go_package = "main";

import "iam/v1/authn.proto";

service Service {
  rpc Authenticate (arcus.apis.iam.v1.AuthenticateRequest) returns (arcus.apis.iam.v1.AuthenticateReply);
}

I've validated the import paths are correct because I can generate gRPC code for this service properly.

I found the import example and tried it with my service, but the method is not written out. It appears the with $t := $m.InputType | getMessageType $.File returns nil so the block exits. Any idea to why this is failing to get the message type?

weitzj commented 6 years ago

You could try some of the plugin options - this is just a wild guess

--gotemplate_out=single-package-mode=true,debug=true,all=true
bruth commented 6 years ago

@weitzj Thanks! That led me in the right direction. I found the single-package-mode example which is close to what I want.

I am now having a separate issue with the GoPackage struct not respecting the package alias in the go_package proto option, e.g. option go_package = "github.com/acus/apis/iam/v1;iam"; which gRPC generated code appears to handle.

weitzj commented 6 years ago

Maybe this helps:

    {{- $dependencyProtoFile := . | getProtoFile -}}
    {{- $pathToImport := regexSplit ";" $dependencyProtoFile.Options.GoPackage -1 | first -}}

Edit: Some more snippets

{{- $currentFile := $file.Name | getProtoFile}}
{{/* e.g. "github.com/acus/apis/iam/v1;iam" will get turned into: */}}
{{- $in := .method.InputType | getMessageType $file}}
{{- $inType := $in.GoType $currentFile.GoPkg.Path -}}

{{/*       Resolve relative imports, i.e.    */}}
{{- $inPath := regexSplit ";" $inType -1 | last }}
bruth commented 6 years ago

I appreciate it, thank you. I was trying a similar approach..

  {{- $path := $dependency.GoPkg.Path | splitList ";" | first}}
  {{- $alias := $dependency.GoPkg.Path | splitList ";" | last}}
bruth commented 6 years ago

After a bit of trial and error, the following template is working:

// File generated with protoc-gen-gotemplate.
// DO NOT EDIT.
package {{.File.Package}}

{{- $file := .File}}
{{- $currentFile := $file.Name | getProtoFile}}

import (
  "golang.org/x/net/context"

  {{- range .File.Dependency}}
    {{- $dependency := . | getProtoFile}}
    {{- $path := $dependency.GoPkg.Path | splitList ";" | first}}
    {{- $alias := $dependency.GoPkg.Path | splitList ";" | last}}
  {{$alias}} "{{$path}}"
  {{- end}}
)

type Service interface {
{{- range .Service.Method}}
{{- $in := .InputType | getMessageType $file}}
{{- $inType := regexSplit ";" ($in.GoType $currentFile.GoPkg.Path) -1 | last}}

{{- $out := .OutputType | getMessageType $file}}
{{- $outType := regexSplit ";" ($out.GoType $currentFile.GoPkg.Path) -1 | last}}
  {{.Name}}(context.Context, *{{- $inType}}) (*{{- $outType}}, error)
{{- end}}
}

Given this protobuf file (which intentionally imports a couple arbitrary things.. including a duplicate package name)..

syntax = "proto3";
package main;

import "iam/v1/authn.proto";
import "iam/v1/authz.proto";
import "cas/v1/messages.proto";

service Service {
  rpc Authenticate (arcus.apis.iam.v1.AuthenticateRequest) returns (arcus.apis.iam.v1.AuthenticateReply);
  rpc Authorize (arcus.apis.iam.v1.AuthorizeRequest) returns (arcus.apis.iam.v1.AuthorizeReply);
  rpc Put (arcus.apis.cas.v1.PutRequest) returns (arcus.apis.cas.v1.PutReply);
}

Will result in the following after goimports -w to remove redundant imports and format.

// File generated with protoc-gen-gotemplate.
// DO NOT EDIT.
package main

import (
    cas "github.com/arcus/apis/cas/v1"
    iam "github.com/arcus/apis/iam/v1"
    "golang.org/x/net/context"
)

type Service interface {
    Authenticate(context.Context, *iam.AuthenticateRequest) (*iam.AuthenticateReply, error)
    Authorize(context.Context, *iam.AuthorizeRequest) (*iam.AuthorizeReply, error)
    Put(context.Context, *cas.PutRequest) (*cas.PutReply, error)
}
weitzj commented 6 years ago

Looks like we eventually ended up with the same approach: running goimports to cleanup duplicate imports. :)

So.Probably the issue can be closed, correct?

bruth commented 6 years ago

Yes definitely. I appreciate the help.