From c2456559a66107441addb96f673191bde09d6977 Mon Sep 17 00:00:00 2001 From: Ondrej Fabry Date: Fri, 2 Oct 2020 16:36:32 +0200 Subject: Check retval value and convert to error in generated RPC client code Change-Id: I816b4802cb5fc46239f6db0480fa4cf3645fe2f0 Signed-off-by: Ondrej Fabry --- binapigen/binapigen.go | 28 ++++++++++---- binapigen/gen_http.go | 103 +++++++++++++++++++++++++++++++++++++++++++++++++ binapigen/gen_rest.go | 103 ------------------------------------------------- binapigen/gen_rpc.go | 12 +++++- binapigen/run.go | 3 +- 5 files changed, 135 insertions(+), 114 deletions(-) create mode 100644 binapigen/gen_http.go delete mode 100644 binapigen/gen_rest.go (limited to 'binapigen') diff --git a/binapigen/binapigen.go b/binapigen/binapigen.go index 35a07d0..de6a804 100644 --- a/binapigen/binapigen.go +++ b/binapigen/binapigen.go @@ -243,8 +243,8 @@ func newStruct(gen *Generator, file *File, apitype vppapi.StructType) *Struct { }, } gen.structsByName[typ.Name] = typ - for _, fieldType := range apitype.Fields { - field := newField(gen, file, typ, fieldType) + for i, fieldType := range apitype.Fields { + field := newField(gen, file, typ, fieldType, i) typ.Fields = append(typ.Fields, field) } return typ @@ -276,8 +276,8 @@ func newUnion(gen *Generator, file *File, apitype vppapi.UnionType) *Union { }, } gen.unionsByName[typ.Name] = typ - for _, fieldType := range apitype.Fields { - field := newField(gen, file, typ, fieldType) + for i, fieldType := range apitype.Fields { + field := newField(gen, file, typ, fieldType, i) typ.Fields = append(typ.Fields, field) } return typ @@ -302,11 +302,12 @@ const ( msgTypeEvent // msg_id, client_index ) -// message fields +// common message fields const ( fieldMsgID = "_vl_msg_id" fieldClientIndex = "client_index" fieldContext = "context" + fieldRetval = "retval" ) // field options @@ -343,7 +344,7 @@ func newMessage(gen *Generator, file *File, apitype vppapi.Message) *Message { } } n++ - field := newField(gen, file, msg, fieldType) + field := newField(gen, file, msg, fieldType, n) msg.Fields = append(msg.Fields, field) } return msg @@ -394,12 +395,24 @@ func getMsgType(m vppapi.Message) (msgType, error) { return typ, nil } +func getRetvalField(m *Message) *Field { + for _, field := range m.Fields { + if field.Name == fieldRetval { + return field + } + } + return nil +} + // Field represents a field for message or struct/union types. type Field struct { vppapi.Field GoName string + // Index defines field index in parent. + Index int + // DefaultValue is a default value of field or // nil if default value is not defined for field. DefaultValue interface{} @@ -423,10 +436,11 @@ type Field struct { FieldSizeFrom *Field } -func newField(gen *Generator, file *File, parent interface{}, apitype vppapi.Field) *Field { +func newField(gen *Generator, file *File, parent interface{}, apitype vppapi.Field, index int) *Field { typ := &Field{ Field: apitype, GoName: camelCaseName(apitype.Name), + Index: index, } switch p := parent.(type) { case *Message: diff --git a/binapigen/gen_http.go b/binapigen/gen_http.go new file mode 100644 index 0000000..4c9697e --- /dev/null +++ b/binapigen/gen_http.go @@ -0,0 +1,103 @@ +// Copyright (c) 2020 Cisco and/or its affiliates. +// +// 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 binapigen + +import ( + "path" + "strconv" +) + +func init() { + RegisterPlugin("http", GenerateHTTP) +} + +// library dependencies +const ( + httpPkg = GoImportPath("net/http") + ioutilPkg = GoImportPath("io/ioutil") + jsonPkg = GoImportPath("encoding/json") +) + +func GenerateHTTP(gen *Generator, file *File) *GenFile { + if file.Service == nil { + return nil + } + + logf("----------------------------") + logf(" Generate HTTP - %s", file.Desc.Name) + logf("----------------------------") + + filename := path.Join(file.FilenamePrefix, file.Desc.Name+"_http.ba.go") + g := gen.NewGenFile(filename, file.GoImportPath) + g.file = file + + // generate file header + g.P("// Code generated by GoVPP's binapi-generator. DO NOT EDIT.") + g.P() + g.P("package ", file.PackageName) + g.P() + + // generate RPC service + if len(file.Service.RPCs) > 0 { + genHTTPHandler(g, file.Service) + } + + return g +} + +func genHTTPHandler(g *GenFile, svc *Service) { + // generate handler constructor + g.P("func HTTPHandler(rpc ", serviceApiName, ") ", httpPkg.Ident("Handler"), " {") + g.P(" mux := ", httpPkg.Ident("NewServeMux"), "()") + + // generate http handlers for rpc + for _, rpc := range svc.RPCs { + if rpc.MsgReply == nil { + continue + } + if rpc.VPP.Stream { + continue // TODO: implement handler for streaming messages + } + g.P("mux.HandleFunc(", strconv.Quote("/"+rpc.VPP.Request), ", func(w ", httpPkg.Ident("ResponseWriter"), ", req *", httpPkg.Ident("Request"), ") {") + g.P("var request = new(", rpc.MsgRequest.GoName, ")") + if len(rpc.MsgRequest.Fields) > 0 { + g.P("b, err := ", ioutilPkg.Ident("ReadAll"), "(req.Body)") + g.P("if err != nil {") + g.P(" ", httpPkg.Ident("Error"), "(w, \"read body failed\", ", httpPkg.Ident("StatusBadRequest"), ")") + g.P(" return") + g.P("}") + g.P("if err := ", jsonPkg.Ident("Unmarshal"), "(b, request); err != nil {") + g.P(" ", httpPkg.Ident("Error"), "(w, \"unmarshal data failed\", ", httpPkg.Ident("StatusBadRequest"), ")") + g.P(" return") + g.P("}") + } + g.P("reply, err := rpc.", rpc.GoName, "(req.Context(), request)") + g.P("if err != nil {") + g.P(" ", httpPkg.Ident("Error"), "(w, \"request failed: \"+err.Error(), ", httpPkg.Ident("StatusInternalServerError"), ")") + g.P(" return") + g.P("}") + g.P("rep, err := ", jsonPkg.Ident("MarshalIndent"), "(reply, \"\", \" \")") + g.P("if err != nil {") + g.P(" ", httpPkg.Ident("Error"), "(w, \"marshal failed: \"+err.Error(), ", httpPkg.Ident("StatusInternalServerError"), ")") + g.P(" return") + g.P("}") + g.P("w.Write(rep)") + g.P("})") + } + + g.P("return ", httpPkg.Ident("HandlerFunc"), "(mux.ServeHTTP)") + g.P("}") + g.P() +} diff --git a/binapigen/gen_rest.go b/binapigen/gen_rest.go deleted file mode 100644 index 6ddb57a..0000000 --- a/binapigen/gen_rest.go +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright (c) 2020 Cisco and/or its affiliates. -// -// 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 binapigen - -import ( - "path" - "strconv" -) - -func init() { - RegisterPlugin("rest", GenerateREST) -} - -// library dependencies -const ( - httpPkg = GoImportPath("net/http") - ioutilPkg = GoImportPath("io/ioutil") - jsonPkg = GoImportPath("encoding/json") -) - -func GenerateREST(gen *Generator, file *File) *GenFile { - if file.Service == nil { - return nil - } - - logf("----------------------------") - logf(" Generate REST - %s", file.Desc.Name) - logf("----------------------------") - - filename := path.Join(file.FilenamePrefix, file.Desc.Name+"_rest.ba.go") - g := gen.NewGenFile(filename, file.GoImportPath) - g.file = file - - // generate file header - g.P("// Code generated by GoVPP's binapi-generator. DO NOT EDIT.") - g.P() - g.P("package ", file.PackageName) - g.P() - - // generate RPC service - if len(file.Service.RPCs) > 0 { - genRESTHandler(g, file.Service) - } - - return g -} - -func genRESTHandler(g *GenFile, svc *Service) { - // generate handler constructor - g.P("func RESTHandler(rpc ", serviceApiName, ") ", httpPkg.Ident("Handler"), " {") - g.P(" mux := ", httpPkg.Ident("NewServeMux"), "()") - - // generate http handlers for rpc - for _, rpc := range svc.RPCs { - if rpc.MsgReply == nil { - continue - } - if rpc.VPP.Stream { - continue // TODO: implement handler for streaming messages - } - g.P("mux.HandleFunc(", strconv.Quote("/"+rpc.VPP.Request), ", func(w ", httpPkg.Ident("ResponseWriter"), ", req *", httpPkg.Ident("Request"), ") {") - g.P("var request = new(", rpc.MsgRequest.GoName, ")") - if len(rpc.MsgRequest.Fields) > 0 { - g.P("b, err := ", ioutilPkg.Ident("ReadAll"), "(req.Body)") - g.P("if err != nil {") - g.P(" ", httpPkg.Ident("Error"), "(w, \"read body failed\", ", httpPkg.Ident("StatusBadRequest"), ")") - g.P(" return") - g.P("}") - g.P("if err := ", jsonPkg.Ident("Unmarshal"), "(b, request); err != nil {") - g.P(" ", httpPkg.Ident("Error"), "(w, \"unmarshal data failed\", ", httpPkg.Ident("StatusBadRequest"), ")") - g.P(" return") - g.P("}") - } - g.P("reply, err := rpc.", rpc.GoName, "(req.Context(), request)") - g.P("if err != nil {") - g.P(" ", httpPkg.Ident("Error"), "(w, \"request failed: \"+err.Error(), ", httpPkg.Ident("StatusInternalServerError"), ")") - g.P(" return") - g.P("}") - g.P("rep, err := ", jsonPkg.Ident("MarshalIndent"), "(reply, \"\", \" \")") - g.P("if err != nil {") - g.P(" ", httpPkg.Ident("Error"), "(w, \"marshal failed: \"+err.Error(), ", httpPkg.Ident("StatusInternalServerError"), ")") - g.P(" return") - g.P("}") - g.P("w.Write(rep)") - g.P("})") - } - - g.P("return ", httpPkg.Ident("HandlerFunc"), "(mux.ServeHTTP)") - g.P("}") - g.P() -} diff --git a/binapigen/gen_rpc.go b/binapigen/gen_rpc.go index ba23f4a..a5974c3 100644 --- a/binapigen/gen_rpc.go +++ b/binapigen/gen_rpc.go @@ -71,7 +71,7 @@ func GenerateRPC(gen *Generator, file *File) *GenFile { func genService(g *GenFile, svc *Service) { // generate comment - g.P("// ", serviceApiName, " defines RPC service ", g.file.Desc.Name, ".") + g.P("// ", serviceApiName, " defines RPC service ", g.file.Desc.Name, ".") // generate service interface g.P("type ", serviceApiName, " interface {") @@ -160,7 +160,15 @@ func genService(g *GenFile, svc *Service) { g.P("out := new(", rpc.MsgReply.GoIdent, ")") g.P("err := c.conn.Invoke(ctx, in, out)") g.P("if err != nil { return nil, err }") - g.P("return out, nil") + if retvalField := getRetvalField(rpc.MsgReply); retvalField != nil { + if fieldType := getFieldType(g, retvalField); fieldType == "int32" { + g.P("return out, ", govppApiPkg.Ident("RetvalToVPPApiError"), "(out.", retvalField.GoName, ")") + } else { + g.P("return out, ", govppApiPkg.Ident("RetvalToVPPApiError"), "(int32(out.", retvalField.GoName, "))") + } + } else { + g.P("return out, nil") + } } else { g.P("stream, err := c.conn.NewStream(ctx)") g.P("if err != nil { return err }") diff --git a/binapigen/run.go b/binapigen/run.go index 8bb88fb..d632519 100644 --- a/binapigen/run.go +++ b/binapigen/run.go @@ -53,7 +53,7 @@ func run(apiDir string, filesToGenerate []string, opts Options, fn func(*Generat if err != nil { return fmt.Errorf("cannot resolve import path for output dir %s: %w", opts.OutputDir, err) } - logrus.Infof("resolved import path prefix: %s", opts.ImportPrefix) + logrus.Debugf("resolved import path prefix: %s", opts.ImportPrefix) } gen, err := New(opts, apiFiles, filesToGenerate) @@ -73,7 +73,6 @@ func run(apiDir string, filesToGenerate []string, opts Options, fn func(*Generat return err } } - if err = gen.Generate(); err != nil { return err } -- cgit 1.2.3-korg