Skip to content

Commit

Permalink
Merge pull request #61 from googleapis/plugins
Browse files Browse the repository at this point in the history
Expand plugin model
  • Loading branch information
timburks authored Sep 5, 2017
2 parents 091ca4d + 123c61d commit ee43cbb
Show file tree
Hide file tree
Showing 58 changed files with 28,893 additions and 3,545 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ Packages
.vscode
.DS_Store
*~
Package.resolved
11 changes: 2 additions & 9 deletions .travis-install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,14 @@ cd
mkdir -p local

# Install swift
SWIFT_BRANCH=swift-3.1.1-release
SWIFT_VERSION=swift-3.1.1-RELEASE
SWIFT_PLATFORM=ubuntu14.04
SWIFT_URL=https://swift.org/builds/$SWIFT_BRANCH/$(echo "$SWIFT_PLATFORM" | tr -d .)/$SWIFT_VERSION/$SWIFT_VERSION-$SWIFT_PLATFORM.tar.gz

SWIFT_URL=https://swift.org/builds/swift-4.0-branch/ubuntu1404/swift-4.0-DEVELOPMENT-SNAPSHOT-2017-09-01-a/swift-4.0-DEVELOPMENT-SNAPSHOT-2017-09-01-a-ubuntu14.04.tar.gz
echo $SWIFT_URL

curl -fSsL $SWIFT_URL -o swift.tar.gz
tar -xzf swift.tar.gz --strip-components=2 --directory=local

# Install protoc
PROTOC_URL=https://github.com/google/protobuf/releases/download/v3.2.0rc2/protoc-3.2.0rc2-linux-x86_64.zip

PROTOC_URL=https://github.com/google/protobuf/releases/download/v3.4.0/protoc-3.4.0-linux-x86_64.zip
echo $PROTOC_URL

curl -fSsL $PROTOC_URL -o protoc.zip
unzip protoc.zip -d local

Expand Down
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ build:
go get
go install
cd generate-gnostic; go get; go install
cd apps/disco; go get; go install
cd apps/report; go get; go install
cd apps/petstore-builder; go get; go install
cd plugins/gnostic-summary; go get; go install
cd plugins/gnostic-analyze; go get; go install
cd plugins/gnostic-go-generator; go get; go install
rm -f $(GOPATH)/bin/gnostic-go-client $(GOPATH)/bin/gnostic-go-server
ln -s $(GOPATH)/bin/gnostic-go-generator $(GOPATH)/bin/gnostic-go-client
Expand Down
2 changes: 1 addition & 1 deletion extensions/extension.pb.go

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

73 changes: 36 additions & 37 deletions gnostic.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,41 +43,42 @@ import (
"strings"

"github.com/golang/protobuf/proto"
"github.com/googleapis/gnostic/discovery"
"github.com/googleapis/gnostic/OpenAPIv2"
"github.com/googleapis/gnostic/OpenAPIv3"
"github.com/googleapis/gnostic/compiler"
"github.com/googleapis/gnostic/discovery"
"github.com/googleapis/gnostic/jsonwriter"
plugins "github.com/googleapis/gnostic/plugins"
surface "github.com/googleapis/gnostic/surface"
"gopkg.in/yaml.v2"
)

const ( // OpenAPI Version
openAPIvUnknown = 0
openAPIv2 = 2
openAPIv3 = 3
discoveryFormat = 4
const ( // Source Format
SourceFormatUnknown = 0
SourceFormatOpenAPI2 = 2
SourceFormatOpenAPI3 = 3
SourceFormatDiscovery = 4
)

// Determine the version of an OpenAPI description read from JSON or YAML.
func getOpenAPIVersionFromInfo(info interface{}) int {
m, ok := compiler.UnpackMap(info)
if !ok {
return openAPIvUnknown
return SourceFormatUnknown
}
swagger, ok := compiler.MapValueForKey(m, "swagger").(string)
if ok && strings.HasPrefix(swagger, "2.0") {
return openAPIv2
return SourceFormatOpenAPI2
}
openapi, ok := compiler.MapValueForKey(m, "openapi").(string)
if ok && strings.HasPrefix(openapi, "3.0") {
return openAPIv3
return SourceFormatOpenAPI3
}
kind, ok := compiler.MapValueForKey(m, "kind").(string)
if ok && kind == "discovery#restDescription" {
return discoveryFormat
return SourceFormatDiscovery
}
return openAPIvUnknown
return SourceFormatUnknown
}

const (
Expand All @@ -91,7 +92,7 @@ type pluginCall struct {
}

// Invokes a plugin.
func (p *pluginCall) perform(document proto.Message, openAPIVersion int, sourceName string) error {
func (p *pluginCall) perform(document proto.Message, sourceFormat int, sourceName string) error {
if p.Name != "" {
request := &plugins.Request{}

Expand Down Expand Up @@ -141,19 +142,17 @@ func (p *pluginCall) perform(document proto.Message, openAPIVersion int, sourceN

request.OutputPath = outputLocation

wrapper := &plugins.Wrapper{}
wrapper.Name = sourceName
switch openAPIVersion {
case openAPIv2:
wrapper.Version = "v2"
case openAPIv3:
wrapper.Version = "v3"
request.SourceName = sourceName
switch sourceFormat {
case SourceFormatOpenAPI2:
request.Openapi2 = document.(*openapi_v2.Document)
request.Surface, _ = surface.NewModelFromOpenAPI2(request.Openapi2)
case SourceFormatOpenAPI3:
request.Openapi3 = document.(*openapi_v3.Document)
request.Surface, _ = surface.NewModelFromOpenAPI3(request.Openapi3)
default:
wrapper.Version = "unknown"
}
protoBytes, _ := proto.Marshal(document)
wrapper.Value = protoBytes
request.Wrapper = wrapper

requestBytes, _ := proto.Marshal(request)

cmd := exec.Command(executableName, "-plugin")
Expand Down Expand Up @@ -237,7 +236,7 @@ type Gnostic struct {
resolveReferences bool
pluginCalls []*pluginCall
extensionHandlers []compiler.ExtensionHandler
openAPIVersion int
sourceFormat int
}

// Initialize a structure to store global application state.
Expand Down Expand Up @@ -345,18 +344,18 @@ func (g *Gnostic) readOpenAPIText(bytes []byte) (message proto.Message, err erro
return nil, err
}
// Determine the OpenAPI version.
g.openAPIVersion = getOpenAPIVersionFromInfo(info)
if g.openAPIVersion == openAPIvUnknown {
g.sourceFormat = getOpenAPIVersionFromInfo(info)
if g.sourceFormat == SourceFormatUnknown {
return nil, errors.New("unable to identify OpenAPI version")
}
// Compile to the proto model.
if g.openAPIVersion == openAPIv2 {
if g.sourceFormat == SourceFormatOpenAPI2 {
document, err := openapi_v2.NewDocument(info, compiler.NewContextWithExtensions("$root", nil, &g.extensionHandlers))
if err != nil {
return nil, err
}
message = document
} else if g.openAPIVersion == openAPIv3 {
} else if g.sourceFormat == SourceFormatOpenAPI3 {
document, err := openapi_v3.NewDocument(info, compiler.NewContextWithExtensions("$root", nil, &g.extensionHandlers))
if err != nil {
return nil, err
Expand All @@ -378,21 +377,21 @@ func (g *Gnostic) readOpenAPIBinary(data []byte) (message proto.Message, err err
documentV3 := &openapi_v3.Document{}
err = proto.Unmarshal(data, documentV3)
if err == nil && strings.HasPrefix(documentV3.Openapi, "3.0") {
g.openAPIVersion = openAPIv3
g.sourceFormat = SourceFormatOpenAPI3
return documentV3, nil
}
// if that failed, try to read an OpenAPI v2 document
documentV2 := &openapi_v2.Document{}
err = proto.Unmarshal(data, documentV2)
if err == nil && strings.HasPrefix(documentV2.Swagger, "2.0") {
g.openAPIVersion = openAPIv2
g.sourceFormat = SourceFormatOpenAPI2
return documentV2, nil
}
// if that failed, try to read a Discovery Format document
discoveryDocument := &discovery_v1.Document{}
err = proto.Unmarshal(data, discoveryDocument)
if err == nil { // && strings.HasPrefix(documentV2.Swagger, "2.0") {
g.openAPIVersion = discoveryFormat
g.sourceFormat = SourceFormatDiscovery
return discoveryDocument, nil
}
return nil, err
Expand Down Expand Up @@ -421,19 +420,19 @@ func (g *Gnostic) writeJSONYAMLOutput(message proto.Message) {
var rawInfo yaml.MapSlice
var ok bool
var err error
if g.openAPIVersion == openAPIv2 {
if g.sourceFormat == SourceFormatOpenAPI2 {
document := message.(*openapi_v2.Document)
rawInfo, ok = document.ToRawInfo().(yaml.MapSlice)
if !ok {
rawInfo = nil
}
} else if g.openAPIVersion == openAPIv3 {
} else if g.sourceFormat == SourceFormatOpenAPI3 {
document := message.(*openapi_v3.Document)
rawInfo, ok = document.ToRawInfo().(yaml.MapSlice)
if !ok {
rawInfo = nil
}
} else if g.openAPIVersion == discoveryFormat {
} else if g.sourceFormat == SourceFormatDiscovery {
document := message.(*discovery_v1.Document)
rawInfo, ok = document.ToRawInfo().(yaml.MapSlice)
if !ok {
Expand Down Expand Up @@ -472,10 +471,10 @@ func (g *Gnostic) writeJSONYAMLOutput(message proto.Message) {
func (g *Gnostic) performActions(message proto.Message) (err error) {
// Optionally resolve internal references.
if g.resolveReferences {
if g.openAPIVersion == openAPIv2 {
if g.sourceFormat == SourceFormatOpenAPI2 {
document := message.(*openapi_v2.Document)
_, err = document.ResolveReferences(g.sourceName)
} else if g.openAPIVersion == openAPIv3 {
} else if g.sourceFormat == SourceFormatOpenAPI3 {
document := message.(*openapi_v3.Document)
_, err = document.ResolveReferences(g.sourceName)
}
Expand All @@ -497,7 +496,7 @@ func (g *Gnostic) performActions(message proto.Message) (err error) {
}
// Call all specified plugins.
for _, p := range g.pluginCalls {
err := p.perform(message, g.openAPIVersion, g.sourceName)
err := p.perform(message, g.sourceFormat, g.sourceName)
if err != nil {
writeFile(g.errorOutputPath, g.errorBytes(err), g.sourceName, "errors")
defer os.Exit(-1) // run all plugins, even when some have errors
Expand Down
68 changes: 22 additions & 46 deletions plugins/environment.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package openapi_plugin_v1
package gnostic_plugin_v1

import (
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"path"
"log"
"flag"
"io/ioutil"

"github.com/golang/protobuf/proto"

Expand All @@ -17,12 +17,10 @@ import (

// Environment contains the environment of a plugin call.
type Environment struct {
Invocation string // string representation of call
Document interface{} // input document
DocumentName string // name of input document
Response *Response // response message
OutputPath string // output location
RunningAsPlugin bool // true if app is being run as a plugin
Request *Request // plugin request object
Response *Response // response message
Invocation string // string representation of call
RunningAsPlugin bool // true if app is being run as a plugin
}

// NewEnvironment creates a plugin context from arguments and standard input.
Expand All @@ -32,7 +30,7 @@ func NewEnvironment() (env *Environment, err error) {
Response: &Response{},
}

input := flag.String("input", "", "OpenAPI description (in binary protocol buffer form)")
input := flag.String("input", "", "API description (in binary protocol buffer form)")
output := flag.String("output", "-", "Output file or directory")
plugin := flag.Bool("plugin", false, "Run as a gnostic plugin (other flags are ignored).")
flag.Parse()
Expand All @@ -58,7 +56,7 @@ When the -plugin option is specified, these flags are ignored.`)
}

if env.RunningAsPlugin {
// handle invocation as a plugin.
// Handle invocation as a plugin.

// Read the plugin input.
pluginData, err := ioutil.ReadAll(os.Stdin)
Expand All @@ -72,66 +70,44 @@ When the -plugin option is specified, these flags are ignored.`)
err = proto.Unmarshal(pluginData, request)
env.RespondAndExitIfError(err)

env.OutputPath = request.OutputPath

// Collect parameters passed to the plugin.
parameters := request.Parameters
for _, parameter := range parameters {
env.Invocation += " " + parameter.Name + "=" + parameter.Value
}

// Log the invocation.
log.Printf("Running plugin %s(input:%s)", env.Invocation, request.Wrapper.Version)

// Read the document sent by the plugin.
version := request.Wrapper.Version
apiData := request.Wrapper.Value

env.DocumentName = request.Wrapper.Name

switch version {
case "v2":
documentv2 := &openapiv2.Document{}
err = proto.Unmarshal(apiData, documentv2)
env.RespondAndExitIfError(err)
env.Document = documentv2
case "v3":
documentv3 := &openapiv3.Document{}
err = proto.Unmarshal(apiData, documentv3)
env.RespondAndExitIfError(err)
env.Document = documentv3
default:
err = fmt.Errorf("Unsupported OpenAPI version %s", version)
env.RespondAndExitIfError(err)
}
log.Printf("Running plugin %s", env.Invocation)

} else {
// handle invocation from the command line.
env.Request = request

env.OutputPath = *output
} else {
// Handle invocation from the command line.

// Read the input document.
apiData, err := ioutil.ReadFile(*input)
if len(apiData) == 0 {
env.RespondAndExitIfError(fmt.Errorf("no input data"))
}

env.DocumentName = path.Base(*input)
env.Request = &Request{}
env.Request.OutputPath = *output
env.Request.SourceName = path.Base(*input)

// first try to unmarshal OpenAPI v2
// First try to unmarshal OpenAPI v2.
documentv2 := &openapiv2.Document{}
err = proto.Unmarshal(apiData, documentv2)
if err == nil {
env.Document = documentv2
env.Request.Openapi2 = documentv2
} else {
// ignore deserialization errors
}

// then try to unmarshal OpenAPI v3
// Then try to unmarshal OpenAPI v3.
documentv3 := &openapiv3.Document{}
err = proto.Unmarshal(apiData, documentv3)
if err == nil {
env.Document = documentv3
env.Request.Openapi3 = documentv3
} else {
// ignore deserialization errors
}
Expand All @@ -154,7 +130,7 @@ func (env *Environment) RespondAndExit() {
responseBytes, _ := proto.Marshal(env.Response)
os.Stdout.Write(responseBytes)
} else {
err := HandleResponse(env.Response, env.OutputPath)
err := HandleResponse(env.Response, env.Request.OutputPath)
if err != nil {
log.Printf("%s", err.Error())
}
Expand Down
Loading

0 comments on commit ee43cbb

Please sign in to comment.