Zack Williams | e940c7a | 2019-08-21 14:25:39 -0700 | [diff] [blame] | 1 | // Copyright 2017 Google Inc. All Rights Reserved. |
| 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | // you may not use this file except in compliance with the License. |
| 5 | // You may obtain a copy of the License at |
| 6 | // |
| 7 | // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | // |
| 9 | // Unless required by applicable law or agreed to in writing, software |
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | // See the License for the specific language governing permissions and |
| 13 | // limitations under the License. |
| 14 | |
| 15 | package compiler |
| 16 | |
| 17 | import ( |
| 18 | "bytes" |
| 19 | "fmt" |
| 20 | "os/exec" |
| 21 | |
| 22 | "strings" |
| 23 | |
| 24 | "errors" |
| 25 | |
| 26 | "github.com/golang/protobuf/proto" |
| 27 | "github.com/golang/protobuf/ptypes/any" |
| 28 | ext_plugin "github.com/googleapis/gnostic/extensions" |
| 29 | yaml "gopkg.in/yaml.v2" |
| 30 | ) |
| 31 | |
| 32 | // ExtensionHandler describes a binary that is called by the compiler to handle specification extensions. |
| 33 | type ExtensionHandler struct { |
| 34 | Name string |
| 35 | } |
| 36 | |
| 37 | // HandleExtension calls a binary extension handler. |
| 38 | func HandleExtension(context *Context, in interface{}, extensionName string) (bool, *any.Any, error) { |
| 39 | handled := false |
| 40 | var errFromPlugin error |
| 41 | var outFromPlugin *any.Any |
| 42 | |
| 43 | if context != nil && context.ExtensionHandlers != nil && len(*(context.ExtensionHandlers)) != 0 { |
| 44 | for _, customAnyProtoGenerator := range *(context.ExtensionHandlers) { |
| 45 | outFromPlugin, errFromPlugin = customAnyProtoGenerator.handle(in, extensionName) |
| 46 | if outFromPlugin == nil { |
| 47 | continue |
| 48 | } else { |
| 49 | handled = true |
| 50 | break |
| 51 | } |
| 52 | } |
| 53 | } |
| 54 | return handled, outFromPlugin, errFromPlugin |
| 55 | } |
| 56 | |
| 57 | func (extensionHandlers *ExtensionHandler) handle(in interface{}, extensionName string) (*any.Any, error) { |
| 58 | if extensionHandlers.Name != "" { |
| 59 | binary, _ := yaml.Marshal(in) |
| 60 | |
| 61 | request := &ext_plugin.ExtensionHandlerRequest{} |
| 62 | |
| 63 | version := &ext_plugin.Version{} |
| 64 | version.Major = 0 |
| 65 | version.Minor = 1 |
| 66 | version.Patch = 0 |
| 67 | request.CompilerVersion = version |
| 68 | |
| 69 | request.Wrapper = &ext_plugin.Wrapper{} |
| 70 | |
| 71 | request.Wrapper.Version = "v2" |
| 72 | request.Wrapper.Yaml = string(binary) |
| 73 | request.Wrapper.ExtensionName = extensionName |
| 74 | |
| 75 | requestBytes, _ := proto.Marshal(request) |
| 76 | cmd := exec.Command(extensionHandlers.Name) |
| 77 | cmd.Stdin = bytes.NewReader(requestBytes) |
| 78 | output, err := cmd.Output() |
| 79 | |
| 80 | if err != nil { |
| 81 | fmt.Printf("Error: %+v\n", err) |
| 82 | return nil, err |
| 83 | } |
| 84 | response := &ext_plugin.ExtensionHandlerResponse{} |
| 85 | err = proto.Unmarshal(output, response) |
| 86 | if err != nil { |
| 87 | fmt.Printf("Error: %+v\n", err) |
| 88 | fmt.Printf("%s\n", string(output)) |
| 89 | return nil, err |
| 90 | } |
| 91 | if !response.Handled { |
| 92 | return nil, nil |
| 93 | } |
| 94 | if len(response.Error) != 0 { |
| 95 | message := fmt.Sprintf("Errors when parsing: %+v for field %s by vendor extension handler %s. Details %+v", in, extensionName, extensionHandlers.Name, strings.Join(response.Error, ",")) |
| 96 | return nil, errors.New(message) |
| 97 | } |
| 98 | return response.Value, nil |
| 99 | } |
| 100 | return nil, nil |
| 101 | } |