Skip to content

Commit

Permalink
simplify extension handling and clarify its experimental status (#196)
Browse files Browse the repository at this point in the history
* simplify extension handling and clarify its experimental status in the README.

* simplifications and cleanup of the extension generator.

* Simplifications to extension.proto
  • Loading branch information
timburks authored Jul 15, 2020
1 parent 40cf3c0 commit 5a22f61
Show file tree
Hide file tree
Showing 9 changed files with 154 additions and 180 deletions.
76 changes: 31 additions & 45 deletions compiler/extension-handler.go → compiler/extensions.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,13 @@ package compiler

import (
"bytes"
"errors"
"fmt"
"os/exec"

"strings"

"github.com/golang/protobuf/proto"
"github.com/golang/protobuf/ptypes/any"
ext_plugin "github.com/googleapis/gnostic/extensions"
extensions "github.com/googleapis/gnostic/extensions"
yaml "gopkg.in/yaml.v3"
)

Expand All @@ -34,64 +32,52 @@ type ExtensionHandler struct {
}

// CallExtension calls a binary extension handler.
func CallExtension(context *Context, in *yaml.Node, extensionName string) (bool, *any.Any, error) {
handled := false
var errFromPlugin error
var outFromPlugin *any.Any

if context != nil && context.ExtensionHandlers != nil && len(*(context.ExtensionHandlers)) != 0 {
for _, customAnyProtoGenerator := range *(context.ExtensionHandlers) {
outFromPlugin, errFromPlugin = customAnyProtoGenerator.handle(in, extensionName)
if outFromPlugin == nil {
continue
} else {
handled = true
break
}
func CallExtension(context *Context, in *yaml.Node, extensionName string) (handled bool, response *any.Any, err error) {
if context == nil || context.ExtensionHandlers == nil {
return false, nil, nil
}
handled = false
for _, handler := range *(context.ExtensionHandlers) {
response, err = handler.handle(in, extensionName)
if response == nil {
continue
} else {
handled = true
break
}
}
return handled, outFromPlugin, errFromPlugin
return handled, response, err
}

func (extensionHandlers *ExtensionHandler) handle(in *yaml.Node, extensionName string) (*any.Any, error) {
if extensionHandlers.Name != "" {
binary, _ := yaml.Marshal(in)

request := &ext_plugin.ExtensionHandlerRequest{}

version := &ext_plugin.Version{}
version.Major = 0
version.Minor = 1
version.Patch = 0
request.CompilerVersion = version

request.Wrapper = &ext_plugin.Wrapper{}

request.Wrapper.Version = "v2"
request.Wrapper.Yaml = string(binary)
request.Wrapper.ExtensionName = extensionName

yamlData, _ := yaml.Marshal(in)
request := &extensions.ExtensionHandlerRequest{
CompilerVersion: &extensions.Version{
Major: 0,
Minor: 1,
Patch: 0,
},
Wrapper: &extensions.Wrapper{
Version: "unknown", // TODO: set this to the type/version of spec being parsed.
Yaml: string(yamlData),
ExtensionName: extensionName,
},
}
requestBytes, _ := proto.Marshal(request)
cmd := exec.Command(extensionHandlers.Name)
cmd.Stdin = bytes.NewReader(requestBytes)
output, err := cmd.Output()
if err != nil {
fmt.Printf("Error: %+v\n", err)
return nil, err
}
response := &ext_plugin.ExtensionHandlerResponse{}
response := &extensions.ExtensionHandlerResponse{}
err = proto.Unmarshal(output, response)
if err != nil {
fmt.Printf("Error: %+v\n", err)
fmt.Printf("%s\n", string(output))
if err != nil || !response.Handled {
return nil, err
}
if !response.Handled {
return nil, nil
}
if len(response.Error) != 0 {
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, ","))
return nil, errors.New(message)
if len(response.Errors) != 0 {
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, ","))
}
return response.Value, nil
}
Expand Down
12 changes: 9 additions & 3 deletions extensions/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
# Extensions

This directory contains support code for building Gnostic extensions and
**Extension Support is experimental.**

This directory contains support code for building Gnostic extensio handlers and
associated examples.

Extensions are used to compile vendor or specification extensions into protocol
buffer structures.
Extension handlers can be used to compile vendor or specification extensions
into protocol buffer structures.

Like plugins, extension handlers are built as separate executables. Extension
bodies are written to extension handlers as serialized
ExtensionHandlerRequests.
115 changes: 57 additions & 58 deletions extensions/extension.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 13 additions & 13 deletions extensions/extension.proto
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@

syntax = "proto3";

package gnostic.extension.v1;

import "google/protobuf/any.proto";
package openapiextension.v1;

// This option lets the proto compiler generate Java code inside the package
// name (see below) instead of inside an outer class. It creates a simpler
Expand All @@ -26,7 +27,7 @@ option java_multiple_files = true;
// The Java outer classname should be the filename in UpperCamelCase. This
// class is only used to hold proto descriptor, so developers don't need to
// work with it directly.
option java_outer_classname = "OpenAPIExtensionV1";
option java_outer_classname = "GnosticExtension";

// The Java package name must be proto package name with proper prefix.
option java_package = "org.gnostic.v1";
Expand All @@ -37,12 +38,12 @@ option java_package = "org.gnostic.v1";
// hopefully unique enough to not conflict with things that may come along in
// the future. 'GPB' is reserved for the protocol buffer implementation itself.
//
option objc_class_prefix = "OAE"; // "OpenAPI Extension"
option objc_class_prefix = "GNX"; // "Gnostic Extension"

// The Go package name.
option go_package = "extensions;openapiextension_v1";
option go_package = "extensions;gnostic_extension_v1";

// The version number of OpenAPI compiler.
// The version number of Gnostic.
message Version {
int32 major = 1;
int32 minor = 2;
Expand All @@ -55,12 +56,11 @@ message Version {
// An encoded Request is written to the ExtensionHandler's stdin.
message ExtensionHandlerRequest {

// The OpenAPI descriptions that were explicitly listed on the command line.
// The specifications will appear in the order they are specified to gnostic.
// The extension to process.
Wrapper wrapper = 1;

// The version number of openapi compiler.
Version compiler_version = 3;
// The version number of Gnostic.
Version compiler_version = 2;
}

// The extensions writes an encoded ExtensionHandlerResponse to stdout.
Expand All @@ -69,7 +69,7 @@ message ExtensionHandlerResponse {
// true if the extension is handled by the extension handler; false otherwise
bool handled = 1;

// Error message. If non-empty, the extension handling failed.
// Error message(s). If non-empty, the extension handling failed.
// The extension handler process should exit with status code zero
// even if it reports an error in this way.
//
Expand All @@ -78,7 +78,7 @@ message ExtensionHandlerResponse {
// itself -- such as the input Document being unparseable -- should be
// reported by writing a message to stderr and exiting with a non-zero
// status code.
repeated string error = 2;
repeated string errors = 2;

// text output
google.protobuf.Any value = 3;
Expand All @@ -88,9 +88,9 @@ message Wrapper {
// version of the OpenAPI specification in which this extension was written.
string version = 1;

// Name of the extension
// Name of the extension.
string extension_name = 2;

// Must be a valid yaml for the proto
// YAML-formatted extension value.
string yaml = 3;
}
Loading

0 comments on commit 5a22f61

Please sign in to comment.