Skip to content

Commit

Permalink
Merge pull request #260 from cloudwego/release/v0.3.2
Browse files Browse the repository at this point in the history
chore: release v0.3.2
  • Loading branch information
FGYFFFF committed Sep 20, 2022
2 parents 0bbfc48 + 8bd424c commit 31f6d7b
Show file tree
Hide file tree
Showing 24 changed files with 808 additions and 66 deletions.
36 changes: 29 additions & 7 deletions cmd/hz/internal/config/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,27 +38,49 @@ func lookupTool(idlType string) (string, error) {
path, err := exec.LookPath(tool)
logs.Debugf("[DEBUG]path:%v", path)
if err != nil {
logs.Warnf("Failed to find %q from $PATH: %s. Try $GOPATH/bin/%s instead\n", path, err.Error(), tool)
p, err := exec.LookPath(tool)
goPath, err := util.GetGOPATH()
if err != nil {
return "", fmt.Errorf("failed to find %q from $PATH or $GOPATH/bin: %s", tool, err)
return "", fmt.Errorf("get 'GOPATH' failed for find %s : %v", tool, path)
}
path = filepath.Join(p, "bin", tool)
path = filepath.Join(goPath, "bin", tool)
}

isExist, err := util.PathExist(path)
if err != nil {
return "", fmt.Errorf("check '%s' path error: %v", path, err)
}

if !isExist {
return "", fmt.Errorf("%s is not installed, please install it first", tool)
if tool == meta.TpCompilerThrift {
// If thriftgo does not exist, the latest version will be installed automatically.
err := util.InstallAndCheckThriftgo()
if err != nil {
return "", fmt.Errorf("can't install '%s' automatically, please install it manually for https://github.com/cloudwego/thriftgo, err : %v", tool, err)
}
} else {
// todo: protoc automatic installation
return "", fmt.Errorf("%s is not installed, please install it first", tool)
}
}

if tool == meta.TpCompilerThrift {
// If thriftgo exists, the version is detected; if the version is lower than v0.2.0 then the latest version of thriftgo is automatically installed.
err := util.CheckAndUpdateThriftgo()
if err != nil {
return "", fmt.Errorf("update thriftgo version failed, please install it manually for https://github.com/cloudwego/thriftgo, err: %v", err)
}
}

exe, err := os.Executable()
if err != nil {
return "", fmt.Errorf("failed to get executable path: %s", err)
}
dir := filepath.Dir(path)
gopath, err := util.GetGOPATH()
if err != nil {
return "", err
}
// softlink the plugin to "$GOPATH/bin"
dir := gopath + string(os.PathSeparator) + "bin"
if tool == meta.TpCompilerProto {
pgh, err := exec.LookPath(meta.ProtocPluginName)
linkName := filepath.Join(dir, meta.ProtocPluginName)
Expand Down Expand Up @@ -183,6 +205,6 @@ func (arg *Argument) GetThriftgoOptions() (string, error) {
if arg.JSONEnumStr {
arg.ThriftOptions = append(arg.ThriftOptions, "json_enum_as_text")
}
gas := "go:" + strings.Join(arg.ThriftOptions, ",") + ",reserve_comments"
gas := "go:" + strings.Join(arg.ThriftOptions, ",") + ",reserve_comments,gen_json_tag=false"
return gas, nil
}
2 changes: 1 addition & 1 deletion cmd/hz/internal/meta/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ package meta
import "runtime"

// Version hz version
const Version = "v0.2.1"
const Version = "v0.3.0"

// Mode hz run modes
type Mode int
Expand Down
13 changes: 9 additions & 4 deletions cmd/hz/internal/protobuf/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,17 +69,22 @@ type PackageReference struct {
func getReferPkgMap(pkgMap map[string]string, incs []*descriptorpb.FileDescriptorProto) (map[string]*PackageReference, error) {
var err error
out := make(map[string]*PackageReference, len(pkgMap))
pkgAliasMap := make(map[string]string, len(incs))
for _, inc := range incs {
pkg := getGoPackage(inc, pkgMap)
path := inc.GetName()
base := util.BaseName(path, ".proto")
fileName := inc.GetName()
pkgName := util.BaseName(pkg, "")
pkgName, err = util.GetPackageUniqueName(pkgName)
if err != nil {
return nil, fmt.Errorf("get package unique name failed, err: %v", err)
if pn, exist := pkgAliasMap[pkg]; exist {
pkgName = pn
} else {
pkgName, err = util.GetPackageUniqueName(pkgName)
pkgAliasMap[pkg] = pkgName
if err != nil {
return nil, fmt.Errorf("get package unique name failed, err: %v", err)
}
}

out[fileName] = &PackageReference{base, path, &model.Model{
FilePath: path,
Package: pkg,
Expand Down
153 changes: 153 additions & 0 deletions cmd/hz/internal/protobuf/tag_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/*
* Copyright 2022 CloudWeGo Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package protobuf

import (
"io/ioutil"
"strings"
"testing"

"google.golang.org/protobuf/compiler/protogen"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/pluginpb"
)

func TestTagGenerate(t *testing.T) {
type TagStruct struct {
Annotation string
GeneratedTag string
ActualTag string
}

tagList := []TagStruct{
{
Annotation: "query",
GeneratedTag: "protobuf:\"bytes,1,opt,name=QueryTag\" json:\"QueryTag,omitempty\" query:\"query\"",
},
{
Annotation: "raw_body",
GeneratedTag: "protobuf:\"bytes,2,opt,name=RawBodyTag\" json:\"RawBodyTag,omitempty\" raw_body:\"raw_body\"",
},
{
Annotation: "path",
GeneratedTag: "protobuf:\"bytes,3,opt,name=PathTag\" json:\"PathTag,omitempty\" path:\"path\"",
},
{
Annotation: "form",
GeneratedTag: "protobuf:\"bytes,4,opt,name=FormTag\" json:\"FormTag,omitempty\" form:\"form\"",
},
{
Annotation: "cookie",
GeneratedTag: "protobuf:\"bytes,5,opt,name=CookieTag\" json:\"CookieTag,omitempty\" cookie:\"cookie\"",
},
{
Annotation: "header",
GeneratedTag: "protobuf:\"bytes,6,opt,name=HeaderTag\" json:\"HeaderTag,omitempty\" header:\"header\"",
},
{
Annotation: "body",
GeneratedTag: "bytes,7,opt,name=BodyTag\" form:\"body\" json:\"body,omitempty\"",
},
{
Annotation: "go.tag",
GeneratedTag: "bytes,8,opt,name=GoTag\" json:\"json\" form:\"form\" goTag:\"tag\" header:\"header\" query:\"query\"",
},
{
Annotation: "vd",
GeneratedTag: "bytes,9,opt,name=VdTag\" json:\"VdTag,omitempty\" form:\"VdTag\" query:\"VdTag\" vd:\"$!='?'\"",
},
{
Annotation: "non",
GeneratedTag: "bytes,10,opt,name=DefaultTag\" json:\"DefaultTag,omitempty\" form:\"DefaultTag\" query:\"DefaultTag\"",
},
{
Annotation: "query required",
GeneratedTag: "bytes,11,req,name=ReqQuery\" json:\"ReqQuery,required\" query:\"query,required\"",
},
{
Annotation: "query optional",
GeneratedTag: "bytes,12,opt,name=OptQuery\" json:\"OptQuery,omitempty\" query:\"query\"",
},
{
Annotation: "body required",
GeneratedTag: "protobuf:\"bytes,13,req,name=ReqBody\" form:\"body,required\" json:\"body,required\"",
},
{
Annotation: "body optional",
GeneratedTag: "protobuf:\"bytes,14,opt,name=OptBody\" form:\"body\" json:\"body,omitempty\"",
},
{
Annotation: "go.tag required",
GeneratedTag: "protobuf:\"bytes,15,req,name=ReqGoTag\" query:\"ReqGoTag,required\" form:\"ReqGoTag,required\" json:\"json\"",
},
{
Annotation: "go.tag optional",
GeneratedTag: "bytes,16,opt,name=OptGoTag\" query:\"OptGoTag\" form:\"OptGoTag\" json:\"json\"",
},
{
Annotation: "go tag cover query",
GeneratedTag: "bytes,17,req,name=QueryGoTag\" json:\"QueryGoTag,required\" query:\"queryTag\"",
},
}

in, err := ioutil.ReadFile("./test_data/protobuf_tag_test.out")
if err != nil {
t.Fatal(err)
}

req := &pluginpb.CodeGeneratorRequest{}
err = proto.Unmarshal(in, req)
if err != nil {
t.Fatalf("unmarshal stdin request error: %v", err)
}

opts := protogen.Options{}
gen, err := opts.New(req)

for _, f := range gen.Files {
if f.Proto.GetName() == "test_tag.proto" {
fileInfo := newFileInfo(f)
for _, message := range fileInfo.allMessages {
for idx, field := range message.Fields {
tags := structTags{
{"protobuf", fieldProtobufTagValue(field)},
}
err = injectTagsToStructTags(field.Desc, &tags, true)
if err != nil {
t.Fatal(err)
}
var actualTag string
for i, tag := range tags {
if i == 0 {
actualTag = tag[0] + ":" + "\"" + tag[1] + "\""
} else {
actualTag = actualTag + " " + tag[0] + ":" + "\"" + tag[1] + "\""
}
}
tagList[idx].ActualTag = actualTag

}
}
}
}

for i := range tagList {
if !strings.Contains(tagList[i].ActualTag, tagList[i].GeneratedTag) {
t.Fatalf("expected tag: '%s', but autual tag: '%s'", tagList[i].GeneratedTag, tagList[i].ActualTag)
}
}
}
34 changes: 28 additions & 6 deletions cmd/hz/internal/protobuf/tags.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,13 @@ var (
}

BindingTags = map[*protoimpl.ExtensionInfo]string{
api.E_Path: "path",
api.E_Query: "query",
api.E_Path: "path",
api.E_Query: "query",
api.E_Header: "header",
api.E_Cookie: "cookie",
api.E_Body: "json",
// Do not change the relative order of "api.E_Form" and "api.E_Body", so that "api.E_Form" can overwrite the form tag generated by "api.E_Body"
api.E_Form: "form",
api.E_Header: "header",
api.E_Cookie: "cookie",
api.E_Body: "json",
api.E_RawBody: "raw_body",
}

Expand Down Expand Up @@ -272,6 +273,22 @@ func reflectJsonTag(f protoreflect.FieldDescriptor) (ret model.Tag) {
func defaultBindingStructTags(f protoreflect.FieldDescriptor) []model.Tag {
opts := f.Options()
out := make([]model.Tag, 3)
bindingTags := []*protoimpl.ExtensionInfo{
api.E_Path,
api.E_Query,
api.E_Form,
api.E_Header,
api.E_Cookie,
api.E_Body,
api.E_RawBody,
}
for _, tag := range bindingTags {
if vv := checkFirstOption(tag, opts); vv != nil {
out[0] = reflectJsonTag(f)
return out[:1]
}
}

if v := checkFirstOption(api.E_Body, opts); v != nil {
val := getStructJsonValue(f, v.(string))
out[0] = tag("json", val)
Expand Down Expand Up @@ -306,8 +323,12 @@ func injectTagsToStructTags(f protoreflect.FieldDescriptor, out *structTags, nee
for k, v := range BindingTags {
if vv := checkFirstOption(k, as); vv != nil {
tags.Remove(v)
// body annotation will generate "json" & "form" tag for protobuf
if v == "json" {
formVal := vv
vv = getStructJsonValue(f, vv.(string))
formVal = checkStructRequire(f, formVal.(string))
tags = append(tags, tag("form", formVal))
} else {
vv = checkStructRequire(f, vv.(string))
}
Expand Down Expand Up @@ -342,7 +363,8 @@ func injectTagsToStructTags(f protoreflect.FieldDescriptor, out *structTags, nee
}
}

sort.Sort(tags)
// protobuf tag as first
sort.Sort(tags[1:])
for _, t := range tags {
*out = append(*out, m2s(t))
}
Expand Down
Binary file not shown.
32 changes: 32 additions & 0 deletions cmd/hz/internal/protobuf/test_data/test_tag.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
syntax = "proto2";

package test;

option go_package = "cloudwego.hertz.hz";

import "api.proto";

message MultiTagReq {
// basic feature
optional string QueryTag = 1 [(api.query)="query"];
optional string RawBodyTag = 2 [(api.raw_body)="raw_body"];
optional string PathTag = 3 [(api.path)="path"];
optional string FormTag = 4 [(api.form)="form"];
optional string CookieTag = 5 [(api.cookie)="cookie"];
optional string HeaderTag = 6 [(api.header)="header"];
optional string BodyTag = 7 [(api.body)="body"];
optional string GoTag = 8 [(api.go_tag)="json:\"json\" query:\"query\" form:\"form\" header:\"header\" goTag:\"tag\""];
optional string VdTag = 9 [(api.vd)="$!='?'"];
optional string DefaultTag = 10;

// optional / required
required string ReqQuery = 11 [(api.query)="query"];
optional string OptQuery = 12 [(api.query)="query"];
required string ReqBody = 13 [(api.body)="body"];
optional string OptBody = 14 [(api.body)="body"];
required string ReqGoTag = 15 [(api.go_tag)="json:\"json\""];
optional string OptGoTag = 16 [(api.go_tag)="json:\"json\""];

// gotag cover feature
required string QueryGoTag = 17 [(api.query)="query", (api.go_tag)="query:\"queryTag\""];
}
Loading

0 comments on commit 31f6d7b

Please sign in to comment.