1// Copyright 2017 Google LLC. 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 15package compiler 16 17import ( 18 "bytes" 19 "fmt" 20 "os/exec" 21 "strings" 22 23 "github.com/golang/protobuf/proto" 24 "github.com/golang/protobuf/ptypes/any" 25 extensions "github.com/googleapis/gnostic/extensions" 26 yaml "gopkg.in/yaml.v3" 27) 28 29// ExtensionHandler describes a binary that is called by the compiler to handle specification extensions. 30type ExtensionHandler struct { 31 Name string 32} 33 34// CallExtension calls a binary extension handler. 35func CallExtension(context *Context, in *yaml.Node, extensionName string) (handled bool, response *any.Any, err error) { 36 if context == nil || context.ExtensionHandlers == nil { 37 return false, nil, nil 38 } 39 handled = false 40 for _, handler := range *(context.ExtensionHandlers) { 41 response, err = handler.handle(in, extensionName) 42 if response == nil { 43 continue 44 } else { 45 handled = true 46 break 47 } 48 } 49 return handled, response, err 50} 51 52func (extensionHandlers *ExtensionHandler) handle(in *yaml.Node, extensionName string) (*any.Any, error) { 53 if extensionHandlers.Name != "" { 54 yamlData, _ := yaml.Marshal(in) 55 request := &extensions.ExtensionHandlerRequest{ 56 CompilerVersion: &extensions.Version{ 57 Major: 0, 58 Minor: 1, 59 Patch: 0, 60 }, 61 Wrapper: &extensions.Wrapper{ 62 Version: "unknown", // TODO: set this to the type/version of spec being parsed. 63 Yaml: string(yamlData), 64 ExtensionName: extensionName, 65 }, 66 } 67 requestBytes, _ := proto.Marshal(request) 68 cmd := exec.Command(extensionHandlers.Name) 69 cmd.Stdin = bytes.NewReader(requestBytes) 70 output, err := cmd.Output() 71 if err != nil { 72 return nil, err 73 } 74 response := &extensions.ExtensionHandlerResponse{} 75 err = proto.Unmarshal(output, response) 76 if err != nil || !response.Handled { 77 return nil, err 78 } 79 if len(response.Errors) != 0 { 80 return nil, fmt.Errorf("Errors when parsing: %+v for field %s by vendor extension handler %s. Details %+v", in, extensionName, extensionHandlers.Name, strings.Join(response.Errors, ",")) 81 } 82 return response.Value, nil 83 } 84 return nil, nil 85} 86