added vendors

This commit is contained in:
mickymiek
2018-12-19 15:29:25 +01:00
parent 12e6881669
commit 8ee6bc4b91
2952 changed files with 1124359 additions and 1 deletions

View File

@ -0,0 +1,7 @@
# Plugins
This directory contains support code for building Gnostic plugins and associated examples.
Plugins are used to process API descriptions and can perform tasks like documentation and
code generation. Plugins can be written in any language that is supported by the Protocol
Buffer tools.

View File

@ -0,0 +1,215 @@
package gnostic_plugin_v1
import (
"errors"
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"path"
"github.com/golang/protobuf/proto"
"github.com/golang/protobuf/ptypes/any"
openapiv2 "github.com/googleapis/gnostic/OpenAPIv2"
openapiv3 "github.com/googleapis/gnostic/OpenAPIv3"
discovery "github.com/googleapis/gnostic/discovery"
surface "github.com/googleapis/gnostic/surface"
)
// Environment contains the environment of a plugin call.
type Environment struct {
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.
func NewEnvironment() (env *Environment, err error) {
env = &Environment{
Invocation: os.Args[0],
Response: &Response{},
}
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()
env.RunningAsPlugin = *plugin
programName := path.Base(os.Args[0])
if (*input == "") && !*plugin {
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "\n")
fmt.Fprintf(os.Stderr, programName+" is a gnostic plugin.\n")
fmt.Fprintf(os.Stderr, `
When it is run from gnostic, the -plugin option is specified and gnostic
writes a binary request to stdin and waits for a binary response on stdout.
This program can also be run standalone using the other flags listed below.
When the -plugin option is specified, these flags are ignored.`)
fmt.Fprintf(os.Stderr, "\n\nUsage:\n")
flag.PrintDefaults()
}
flag.Usage()
os.Exit(0)
}
if env.RunningAsPlugin {
// Handle invocation as a plugin.
// Read the plugin input.
pluginData, err := ioutil.ReadAll(os.Stdin)
env.RespondAndExitIfError(err)
if len(pluginData) == 0 {
env.RespondAndExitIfError(fmt.Errorf("no input data"))
}
// Deserialize the request from the input.
request := &Request{}
err = proto.Unmarshal(pluginData, request)
env.RespondAndExitIfError(err)
// 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", env.Invocation)
env.Request = request
} 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.Request = &Request{}
env.Request.OutputPath = *output
env.Request.SourceName = path.Base(*input)
// First try to unmarshal OpenAPI v2.
documentv2 := &openapiv2.Document{}
err = proto.Unmarshal(apiData, documentv2)
if err == nil {
env.Request.AddModel("openapi.v2.Document", documentv2)
// include experimental API surface model
surfaceModel, err := surface.NewModelFromOpenAPI2(documentv2)
if err != nil {
env.Request.AddModel("surface.v1.Model", surfaceModel)
}
return env, err
}
// If that failed, ignore deserialization errors and try to unmarshal OpenAPI v3.
documentv3 := &openapiv3.Document{}
err = proto.Unmarshal(apiData, documentv3)
if err == nil {
env.Request.AddModel("openapi.v3.Document", documentv3)
// include experimental API surface model
surfaceModel, err := surface.NewModelFromOpenAPI3(documentv3)
if err != nil {
env.Request.AddModel("surface.v1.Model", surfaceModel)
}
return env, err
}
// If that failed, ignore deserialization errors and try to unmarshal a Discovery document.
discoveryDocument := &discovery.Document{}
err = proto.Unmarshal(apiData, discoveryDocument)
if err == nil {
env.Request.AddModel("discovery.v1.Document", discoveryDocument)
return env, err
}
// If we get here, we don't know what we got
err = errors.New("Unrecognized format for input")
return env, err
}
return env, err
}
// RespondAndExitIfError checks an error and if it is non-nil, records it and serializes and returns the response and then exits.
func (env *Environment) RespondAndExitIfError(err error) {
if err != nil {
env.Response.Errors = append(env.Response.Errors, err.Error())
env.RespondAndExit()
}
}
// RespondAndExit serializes and returns the plugin response and then exits.
func (env *Environment) RespondAndExit() {
if env.RunningAsPlugin {
responseBytes, _ := proto.Marshal(env.Response)
os.Stdout.Write(responseBytes)
} else {
err := HandleResponse(env.Response, env.Request.OutputPath)
if err != nil {
log.Printf("%s", err.Error())
}
}
os.Exit(0)
}
func HandleResponse(response *Response, outputLocation string) error {
if response.Errors != nil {
return fmt.Errorf("Plugin error: %+v", response.Errors)
}
// Write files to the specified directory.
var writer io.Writer
switch {
case outputLocation == "!":
// Write nothing.
case outputLocation == "-":
writer = os.Stdout
for _, file := range response.Files {
writer.Write([]byte("\n\n" + file.Name + " -------------------- \n"))
writer.Write(file.Data)
}
case isFile(outputLocation):
return fmt.Errorf("unable to overwrite %s", outputLocation)
default: // write files into a directory named by outputLocation
if !isDirectory(outputLocation) {
os.Mkdir(outputLocation, 0755)
}
for _, file := range response.Files {
p := outputLocation + "/" + file.Name
dir := path.Dir(p)
os.MkdirAll(dir, 0755)
f, _ := os.Create(p)
defer f.Close()
f.Write(file.Data)
}
}
return nil
}
func (request *Request) AddModel(modelType string, model proto.Message) error {
modelBytes, err := proto.Marshal(model)
request.Models = append(request.Models, &any.Any{TypeUrl: modelType, Value: modelBytes})
return err
}
func isFile(path string) bool {
fileInfo, err := os.Stat(path)
if err != nil {
return false
}
return !fileInfo.IsDir()
}
func isDirectory(path string) bool {
fileInfo, err := os.Stat(path)
if err != nil {
return false
}
return fileInfo.IsDir()
}

View File

@ -0,0 +1,23 @@
# gnostic-analyze
This directory contains a `gnostic` plugin that analyzes an OpenAPI description for factors
that might influence code generation and other API automation.
The plugin can be invoked like this:
gnostic bookstore.json --analyze_out=.
This will write analysis results to a file in the current directory.
Results are written to a file named `summary.json`.
The plugin can be applied to a directory of descriptions using a command
like the following:
find APIs -name "swagger.yaml" -exec gnostic --analyze_out=analysis {} \;
This finds all `swagger.yaml` files in a directory named `APIs` and its subdirectories
and writes corresponding `summary.json` files into a directory named `analysis`.
Results of multiple analysis runs can be gathered together and summarized
using the `summarize` program, which is in the `summarize` subdirectory.
Just run `summarize` in the same location as the `find` command shown above.

View File

@ -0,0 +1,95 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// 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.
// gnostic_analyze is a tool for analyzing OpenAPI descriptions.
//
// It scans an API description and evaluates properties
// that influence the ease and quality of code generation.
// - The number of HTTP operations of each method (GET, POST, etc).
// - The number of HTTP operations with no OperationId value.
// - The parameter types used and their frequencies.
// - The response types used and their frequencies.
// - The types used in definition objects and arrays and their frequencies.
// Results are returned in a JSON structure.
package main
import (
"encoding/json"
"os"
"path"
"strings"
"github.com/googleapis/gnostic/plugins/gnostic-analyze/statistics"
"github.com/golang/protobuf/proto"
openapiv2 "github.com/googleapis/gnostic/OpenAPIv2"
openapiv3 "github.com/googleapis/gnostic/OpenAPIv3"
plugins "github.com/googleapis/gnostic/plugins"
)
// Record an error, then serialize and return a response.
func sendAndExitIfError(err error, response *plugins.Response) {
if err != nil {
response.Errors = append(response.Errors, err.Error())
sendAndExit(response)
}
}
// Serialize and return a response.
func sendAndExit(response *plugins.Response) {
responseBytes, _ := proto.Marshal(response)
os.Stdout.Write(responseBytes)
os.Exit(0)
}
// This is the main function for the plugin.
func main() {
env, err := plugins.NewEnvironment()
env.RespondAndExitIfError(err)
var stats *statistics.DocumentStatistics
for _, model := range env.Request.Models {
switch model.TypeUrl {
case "openapi.v2.Document":
documentv2 := &openapiv2.Document{}
err = proto.Unmarshal(model.Value, documentv2)
if err == nil {
// Analyze the API document.
stats = statistics.NewDocumentStatistics(env.Request.SourceName, documentv2)
}
case "openapi.v3.Document":
documentv3 := &openapiv3.Document{}
err = proto.Unmarshal(model.Value, documentv3)
if err == nil {
// Analyze the API document.
stats = statistics.NewDocumentStatisticsV3(env.Request.SourceName, documentv3)
}
}
}
if stats != nil {
// Return the analysis results with an appropriate filename.
// Results are in files named "summary.json" in the same relative
// locations as the description source files.
file := &plugins.File{}
file.Name = strings.Replace(stats.Name, path.Base(stats.Name), "summary.json", -1)
file.Data, err = json.MarshalIndent(stats, "", " ")
file.Data = append(file.Data, []byte("\n")...)
env.RespondAndExitIfError(err)
env.Response.Files = append(env.Response.Files, file)
}
env.RespondAndExit()
}

View File

@ -0,0 +1,331 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// 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 statistics
import (
"fmt"
"strings"
openapi "github.com/googleapis/gnostic/OpenAPIv2"
)
// DocumentStatistics contains information collected about an API description.
type DocumentStatistics struct {
Name string `json:"name"`
Title string `json:"title"`
Operations map[string]int `json:"operations"`
DefinitionCount int `json:"definitions"`
ParameterTypes map[string]int `json:"parameterTypes"`
ResultTypes map[string]int `json:"resultTypes"`
DefinitionFieldTypes map[string]int `json:"definitionFieldTypes"`
DefinitionArrayTypes map[string]int `json:"definitionArrayTypes"`
DefinitionPrimitiveTypes map[string]int `json:"definitionPrimitiveTypes"`
AnonymousOperations []string `json:"anonymousOperations"`
AnonymousObjects []string `json:"anonymousObjects"`
}
// NewDocumentStatistics builds a new DocumentStatistics object.
func NewDocumentStatistics(source string, document *openapi.Document) *DocumentStatistics {
s := &DocumentStatistics{}
s.Operations = make(map[string]int, 0)
s.ParameterTypes = make(map[string]int, 0)
s.ResultTypes = make(map[string]int, 0)
s.DefinitionFieldTypes = make(map[string]int, 0)
s.DefinitionArrayTypes = make(map[string]int, 0)
s.DefinitionPrimitiveTypes = make(map[string]int, 0)
s.AnonymousOperations = make([]string, 0)
s.AnonymousObjects = make([]string, 0)
s.analyzeDocument(source, document)
return s
}
func (s *DocumentStatistics) addOperation(name string) {
s.Operations[name] = s.Operations[name] + 1
}
func (s *DocumentStatistics) addParameterType(path string, name string) {
if strings.Contains(name, "object") {
s.AnonymousObjects = append(s.AnonymousObjects, path)
}
s.ParameterTypes[name] = s.ParameterTypes[name] + 1
}
func (s *DocumentStatistics) addResultType(path string, name string) {
if strings.Contains(name, "object") {
s.AnonymousObjects = append(s.AnonymousObjects, path)
}
s.ResultTypes[name] = s.ResultTypes[name] + 1
}
func (s *DocumentStatistics) addDefinitionFieldType(path string, name string) {
if strings.Contains(name, "object") {
s.AnonymousObjects = append(s.AnonymousObjects, path)
}
s.DefinitionFieldTypes[name] = s.DefinitionFieldTypes[name] + 1
}
func (s *DocumentStatistics) addDefinitionArrayType(path string, name string) {
if strings.Contains(name, "object") {
s.AnonymousObjects = append(s.AnonymousObjects, path)
}
s.DefinitionArrayTypes[name] = s.DefinitionArrayTypes[name] + 1
}
func (s *DocumentStatistics) addDefinitionPrimitiveType(path string, name string) {
s.DefinitionPrimitiveTypes[name] = s.DefinitionPrimitiveTypes[name] + 1
}
func typeForPrimitivesItems(p *openapi.PrimitivesItems) string {
switch {
case p == nil:
return "object"
case p.Type != "":
return p.Type
case p.Items != nil && p.Items.Type != "":
return p.Items.Type
default:
return "object"
}
}
func (s *DocumentStatistics) analyzeOperation(method string, path string, operation *openapi.Operation) {
s.addOperation(method)
s.addOperation("total")
if operation.OperationId == "" {
s.addOperation("anonymous")
s.AnonymousOperations = append(s.AnonymousOperations, path)
}
for _, parameter := range operation.Parameters {
p := parameter.GetParameter()
if p != nil {
b := p.GetBodyParameter()
if b != nil {
typeName := typeForSchema(b.Schema)
s.addParameterType(path+"/"+b.Name, typeName)
}
n := p.GetNonBodyParameter()
if n != nil {
hp := n.GetHeaderParameterSubSchema()
if hp != nil {
t := hp.Type
if t == "array" {
t += "-of-" + typeForPrimitivesItems(hp.Items)
}
s.addParameterType(path+"/"+hp.Name, t)
}
fp := n.GetFormDataParameterSubSchema()
if fp != nil {
t := fp.Type
if t == "array" {
t += "-of-" + typeForPrimitivesItems(fp.Items)
}
s.addParameterType(path+"/"+fp.Name, t)
}
qp := n.GetQueryParameterSubSchema()
if qp != nil {
t := qp.Type
if t == "array" {
t += "-of-" + typeForPrimitivesItems(qp.Items)
}
s.addParameterType(path+"/"+qp.Name, t)
}
pp := n.GetPathParameterSubSchema()
if pp != nil {
t := pp.Type
if t == "array" {
if t == "array" {
t += "-of-" + typeForPrimitivesItems(pp.Items)
}
}
s.addParameterType(path+"/"+pp.Name, t)
}
}
}
r := parameter.GetJsonReference()
if r != nil {
s.addParameterType(path+"/", "reference")
}
}
for _, pair := range operation.Responses.ResponseCode {
value := pair.Value
response := value.GetResponse()
if response != nil {
responseSchema := response.Schema
responseSchemaSchema := responseSchema.GetSchema()
if responseSchemaSchema != nil {
s.addResultType(path+"/responses/"+pair.Name, typeForSchema(responseSchemaSchema))
}
responseFileSchema := responseSchema.GetFileSchema()
if responseFileSchema != nil {
s.addResultType(path+"/responses/"+pair.Name, typeForFileSchema(responseFileSchema))
}
}
ref := value.GetJsonReference()
if ref != nil {
}
}
}
// Analyze a definition in an OpenAPI description.
// Collect information about the definition type and any subsidiary types,
// such as the types of object fields or array elements.
func (s *DocumentStatistics) analyzeDefinition(path string, definition *openapi.Schema) {
s.DefinitionCount++
typeName := typeNameForSchema(definition)
switch typeName {
case "object":
if definition.Properties != nil {
for _, pair := range definition.Properties.AdditionalProperties {
propertySchema := pair.Value
propertyType := typeForSchema(propertySchema)
s.addDefinitionFieldType(path+"/"+pair.Name, propertyType)
}
}
case "array":
s.addDefinitionArrayType(path+"/", typeForSchema(definition))
default: // string, boolean, integer, number, null...
s.addDefinitionPrimitiveType(path+"/", typeName)
}
}
// Analyze an OpenAPI description.
// Collect information about types used in the API.
// This should be called exactly once per DocumentStatistics object.
func (s *DocumentStatistics) analyzeDocument(source string, document *openapi.Document) {
s.Name = source
s.Title = document.Info.Title
for _, pair := range document.Paths.Path {
path := pair.Value
if path.Get != nil {
s.analyzeOperation("get", "paths"+pair.Name+"/get", path.Get)
}
if path.Post != nil {
s.analyzeOperation("post", "paths"+pair.Name+"/post", path.Post)
}
if path.Put != nil {
s.analyzeOperation("put", "paths"+pair.Name+"/put", path.Put)
}
if path.Delete != nil {
s.analyzeOperation("delete", "paths"+pair.Name+"/delete", path.Delete)
}
}
if document.Definitions != nil {
for _, pair := range document.Definitions.AdditionalProperties {
definition := pair.Value
s.analyzeDefinition("definitions/"+pair.Name, definition)
}
}
}
// helpers
func typeNameForSchema(schema *openapi.Schema) string {
typeName := "object" // default type
if schema.Type != nil && len(schema.Type.Value) > 0 {
typeName = ""
for i, name := range schema.Type.Value {
if i > 0 {
typeName += "|"
}
typeName += name
}
}
return typeName
}
// Return a type name to use for a schema.
func typeForSchema(schema *openapi.Schema) string {
if schema.XRef != "" {
return "reference"
}
if len(schema.Enum) > 0 {
enumType := typeNameForSchema(schema)
return "enum-of-" + enumType
}
typeName := typeNameForSchema(schema)
if typeName == "array" {
if schema.Items != nil {
// items contains an array of schemas
itemType := ""
for i, itemSchema := range schema.Items.Schema {
if i > 0 {
itemType += "|"
}
itemType += typeForSchema(itemSchema)
}
return "array-of-" + itemType
} else if schema.XRef != "" {
return "array-of-reference"
} else {
// we need to do more work to understand this type
return fmt.Sprintf("array-of-[%+v]", schema)
}
} else if typeName == "object" {
// this object might be representable with a map
// but not if it has properties
if (schema.Properties != nil) && (len(schema.Properties.AdditionalProperties) > 0) {
return typeName
}
if schema.AdditionalProperties != nil {
if schema.AdditionalProperties.GetSchema() != nil {
additionalPropertiesSchemaType := typeForSchema(schema.AdditionalProperties.GetSchema())
return "map-of-" + additionalPropertiesSchemaType
}
if schema.AdditionalProperties.GetBoolean() == false {
// no additional properties are allowed, so we're not sure what to do if we get here...
return typeName
}
}
if schema.Items != nil {
itemType := ""
for i, itemSchema := range schema.Items.Schema {
if i > 0 {
itemType += "|"
}
itemType += typeForSchema(itemSchema)
}
return "map-of-" + itemType
}
return "map-of-object"
} else {
return typeName
}
}
func typeForFileSchema(schema *openapi.FileSchema) string {
if schema.Type != "" {
value := schema.Type
switch value {
case "boolean":
return "fileschema-" + value
case "string":
return "fileschema-" + value
case "file":
return "fileschema-" + value
case "number":
return "fileschema-" + value
case "integer":
return "fileschema-" + value
case "object":
return "fileschema-" + value
case "null":
return "fileschema-" + value
}
}
return fmt.Sprintf("FILE SCHEMA %+v", schema)
}

View File

@ -0,0 +1,127 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// 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 statistics
import (
openapi "github.com/googleapis/gnostic/OpenAPIv3"
)
// NewDocumentStatistics builds a new DocumentStatistics object.
func NewDocumentStatisticsV3(source string, document *openapi.Document) *DocumentStatistics {
s := &DocumentStatistics{}
s.Operations = make(map[string]int, 0)
s.ParameterTypes = make(map[string]int, 0)
s.ResultTypes = make(map[string]int, 0)
s.DefinitionFieldTypes = make(map[string]int, 0)
s.DefinitionArrayTypes = make(map[string]int, 0)
s.DefinitionPrimitiveTypes = make(map[string]int, 0)
s.AnonymousOperations = make([]string, 0)
s.AnonymousObjects = make([]string, 0)
// TODO
//s.analyzeDocumentV3(source, document)
return s
}
/*
func (s *DocumentStatistics) analyzeOperationV3(method string, path string, operation *openapi.Operation) {
s.addOperation(method)
s.addOperation("total")
if operation.OperationId == "" {
s.addOperation("anonymous")
s.AnonymousOperations = append(s.AnonymousOperations, path)
}
for _, parametersItem := range operation.Parameters {
p := parametersItem.GetParameter()
if p != nil {
typeName := typeNameForSchemaOrReferenceV3(p.Schema)
s.addParameterType(path+"/"+p.Name, typeName)
}
}
for _, pair := range *(operation.Responses.Responses) {
value := pair.Value
response := value.GetResponse()
if response != nil {
responseSchema := response.Schema
responseSchemaSchema := responseSchema.GetSchema()
if responseSchemaSchema != nil {
s.addResultType(path+"/responses/"+pair.Name, typeForSchema(responseSchemaSchema))
}
responseFileSchema := responseSchema.GetFileSchema()
if responseFileSchema != nil {
s.addResultType(path+"/responses/"+pair.Name, typeForFileSchema(responseFileSchema))
}
}
ref := value.GetJsonReference()
if ref != nil {
}
}
}
// Analyze a definition in an OpenAPI description.
// Collect information about the definition type and any subsidiary types,
// such as the types of object fields or array elements.
func (s *DocumentStatistics) analyzeDefinitionV3(path string, definition *openapi.Schema) {
s.DefinitionCount++
typeName := typeNameForSchemaV3(definition)
switch typeName {
case "object":
if definition.Properties != nil {
for _, pair := range definition.Properties.AdditionalProperties {
propertySchema := pair.Value
propertyType := typeForSchemaV3(propertySchema)
s.addDefinitionFieldType(path+"/"+pair.Name, propertyType)
}
}
case "array":
s.addDefinitionArrayType(path+"/", typeForSchemaV3(definition))
default: // string, boolean, integer, number, null...
s.addDefinitionPrimitiveType(path+"/", typeName)
}
}
// Analyze an OpenAPI description.
// Collect information about types used in the API.
// This should be called exactly once per DocumentStatistics object.
func (s *DocumentStatistics) analyzeDocumentV3(source string, document *openapi.Document) {
s.Name = source
s.Title = document.Info.Title
for _, pair := range document.Paths.Path {
path := pair.Value
if path.Get != nil {
s.analyzeOperation("get", "paths"+pair.Name+"/get", path.Get)
}
if path.Post != nil {
s.analyzeOperation("post", "paths"+pair.Name+"/post", path.Post)
}
if path.Put != nil {
s.analyzeOperation("put", "paths"+pair.Name+"/put", path.Put)
}
if path.Delete != nil {
s.analyzeOperation("delete", "paths"+pair.Name+"/delete", path.Delete)
}
}
if document.Components.Schemas != nil {
for _, pair := range document.Components.Schemas.AdditionalProperties {
definition := pair.Value
if definition.GetSchema() != nil {
s.analyzeDefinition("definitions/"+pair.Name, definition.GetSchema())
}
}
}
}
*/

View File

@ -0,0 +1,156 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// 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.
// summarize is a tool for summarizing the results of gnostic_analyze runs.
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"sort"
"github.com/googleapis/gnostic/plugins/gnostic-analyze/statistics"
)
// Results are collected in this global slice.
var stats []statistics.DocumentStatistics
// walker is called for each summary file found.
func walker(p string, info os.FileInfo, err error) error {
basename := path.Base(p)
if basename != "summary.json" {
return nil
}
data, err := ioutil.ReadFile(p)
if err != nil {
return err
}
var s statistics.DocumentStatistics
err = json.Unmarshal(data, &s)
if err != nil {
return err
}
stats = append(stats, s)
return nil
}
func printFrequencies(m map[string]int) {
for _, pair := range rankByCount(m) {
fmt.Printf("%6d %s\n", pair.Value, pair.Key)
}
}
func rankByCount(frequencies map[string]int) pairList {
pl := make(pairList, len(frequencies))
i := 0
for k, v := range frequencies {
pl[i] = pair{k, v}
i++
}
sort.Sort(sort.Reverse(pl))
return pl
}
type pair struct {
Key string
Value int
}
type pairList []pair
func (p pairList) Len() int { return len(p) }
func (p pairList) Less(i, j int) bool { return p[i].Value < p[j].Value }
func (p pairList) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func main() {
// Collect all statistics in the current directory and its subdirectories.
stats = make([]statistics.DocumentStatistics, 0)
filepath.Walk(".", walker)
// Compute some interesting properties.
apisWithAnonymousOperations := 0
apisWithAnonymousObjects := 0
apisWithAnonymousAnything := 0
opFrequencies := make(map[string]int, 0)
parameterTypeFrequencies := make(map[string]int, 0)
resultTypeFrequencies := make(map[string]int, 0)
definitionFieldTypeFrequencies := make(map[string]int, 0)
definitionArrayTypeFrequencies := make(map[string]int, 0)
definitionPrimitiveTypeFrequencies := make(map[string]int, 0)
for _, api := range stats {
if api.Operations["anonymous"] != 0 {
apisWithAnonymousOperations++
}
if len(api.AnonymousObjects) > 0 {
apisWithAnonymousObjects++
}
if len(api.AnonymousOperations) > 0 {
apisWithAnonymousAnything++
if len(api.AnonymousObjects) > 0 {
fmt.Printf("%s has anonymous operations and objects\n", api.Name)
} else {
fmt.Printf("%s has anonymous operations\n", api.Name)
}
} else {
if len(api.AnonymousObjects) > 0 {
apisWithAnonymousAnything++
fmt.Printf("%s has anonymous objects\n", api.Name)
} else {
fmt.Printf("%s has no anonymous operations or objects\n", api.Name)
}
}
for k, v := range api.Operations {
opFrequencies[k] += v
}
for k, v := range api.ParameterTypes {
parameterTypeFrequencies[k] += v
}
for k, v := range api.ResultTypes {
resultTypeFrequencies[k] += v
}
for k, v := range api.DefinitionFieldTypes {
definitionFieldTypeFrequencies[k] += v
}
for k, v := range api.DefinitionArrayTypes {
definitionArrayTypeFrequencies[k] += v
}
for k, v := range api.DefinitionPrimitiveTypes {
definitionPrimitiveTypeFrequencies[k] += v
}
}
// Report the results.
fmt.Printf("\n")
fmt.Printf("Collected information on %d APIs.\n\n", len(stats))
fmt.Printf("APIs with anonymous operations: %d\n", apisWithAnonymousOperations)
fmt.Printf("APIs with anonymous objects: %d\n", apisWithAnonymousObjects)
fmt.Printf("APIs with anonymous anything: %d\n", apisWithAnonymousAnything)
fmt.Printf("\nOperation frequencies:\n")
printFrequencies(opFrequencies)
fmt.Printf("\nParameter type frequencies:\n")
printFrequencies(parameterTypeFrequencies)
fmt.Printf("\nResult type frequencies:\n")
printFrequencies(resultTypeFrequencies)
fmt.Printf("\nDefinition object field type frequencies:\n")
printFrequencies(definitionFieldTypeFrequencies)
fmt.Printf("\nDefinition array type frequencies:\n")
printFrequencies(definitionArrayTypeFrequencies)
fmt.Printf("\nDefinition primitive type frequencies:\n")
printFrequencies(definitionPrimitiveTypeFrequencies)
}

View File

@ -0,0 +1,9 @@
build:
go get golang.org/x/tools/cmd/goimports
go install github.com/googleapis/gnostic
go install github.com/googleapis/gnostic/plugins/gnostic-go-generator
rm -f $(GOPATH)/bin/gnostic-go-client $(GOPATH)/bin/gnostic-go-server
ln -s $(GOPATH)/bin/gnostic-go-generator $(GOPATH)/bin/gnostic-go-client
ln -s $(GOPATH)/bin/gnostic-go-generator $(GOPATH)/bin/gnostic-go-server

View File

@ -0,0 +1,18 @@
# Go Generator Plugin
This directory contains a `gnostic` plugin that can be used to generate a Go client library and scaffolding for a Go server for an API with an OpenAPI description.
The plugin can be invoked like this:
gnostic bookstore.json --go-generator-out=bookstore
`bookstore` is the name of a directory where the generated code will be written.
`bookstore` will also be the package name used for generated code.
By default, both client and server code will be generated. If the `gnostic-go-generator` binary is also linked from the names `gnostic-go-client` and `gnostic-go-server`, then only client or only server code can be generated as follows:
gnostic bookstore.json --go-client-out=bookstore
gnostic bookstore.json --go-server-out=bookstore
For example usage, see the [examples/v2.0/bookstore](examples/v2.0/bookstore) directory.

View File

@ -0,0 +1,31 @@
# googleauth
This directory contains support code that can be used to get an OAuth2 token for a Google API user.
It is designed to work on computers with attached displays.
Use it to write command-line tools and test programs that call Google APIs.
## Instructions
Import this package and make the following call to request a token.
client, err := googleauth.NewOAuth2Client(scopes)
`scopes` should be a string containing the OAuth scopes needed by the APIs to be called.
For example, the URL Shortener API would require "https://www.googleapis.com/auth/urlshortener".
This call will then open a local browser that will redirect to a Google signin page
with information about the app that is requesting a token.
## Application Credentials
To use this package, you need to download a "client secrets" file and
save it as `client_secrets.json` in the directory where your tool is run.
To get this file, visit the {{ Google Cloud Console }}{{ https://cloud.google.com/console }}
and create a project. Then go to the API Manager to enable the APIs that you want to use
and create OAuth2 credentials. You'll then be able to download these credentials
as JSON. Save this file as `client_secrets.json`
For more information about the `client_secrets.json` file format, please visit:
https://developers.google.com/api-client-library/python/guide/aaa_client_secrets

View File

@ -0,0 +1,220 @@
//
// Copyright 2017 Google Inc. All Rights Reserved.
//
// 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 googleauth
import (
"encoding/json"
"errors"
"flag"
"fmt"
"io/ioutil"
"net"
"net/http"
"os"
"os/exec"
"path/filepath"
"runtime"
"golang.org/x/net/context"
"golang.org/x/oauth2"
)
const missingClientSecretsMessage = `
Please configure OAuth 2.0
To make this sample run, you need to populate the client_secrets.json file
found at:
%v
with information from the {{ Google Cloud Console }}
{{ https://cloud.google.com/console }}
For more information about the client_secrets.json file format, please visit:
https://developers.google.com/api-client-library/python/guide/aaa_client_secrets
`
var (
clientSecretsFile = flag.String("secrets", "client_secrets.json", "Client Secrets configuration")
cacheFile = flag.String("cache", "request.token", "Token cache file")
)
// ClientConfig is a data structure definition for the client_secrets.json file.
// The code unmarshals the JSON configuration file into this structure.
type ClientConfig struct {
ClientID string `json:"client_id"`
ClientSecret string `json:"client_secret"`
RedirectURIs []string `json:"redirect_uris"`
AuthURI string `json:"auth_uri"`
TokenURI string `json:"token_uri"`
}
// Config is a root-level configuration object.
type Config struct {
Installed ClientConfig `json:"installed"`
Web ClientConfig `json:"web"`
}
// openURL opens a browser window to the specified location.
// This code originally appeared at:
// http://stackoverflow.com/questions/10377243/how-can-i-launch-a-process-that-is-not-a-file-in-go
func openURL(url string) error {
var err error
switch runtime.GOOS {
case "linux":
err = exec.Command("xdg-open", url).Start()
case "windows":
err = exec.Command("rundll32", "url.dll,FileProtocolHandler", "http://localhost:4001/").Start()
case "darwin":
err = exec.Command("open", url).Start()
default:
err = fmt.Errorf("Cannot open URL %s on this platform", url)
}
return err
}
// readConfig reads the configuration from clientSecretsFile.
// It returns an oauth configuration object for use with the Google API client.
func readConfig(scopes []string) (*oauth2.Config, error) {
// Read the secrets file
data, err := ioutil.ReadFile(*clientSecretsFile)
if err != nil {
pwd, _ := os.Getwd()
fullPath := filepath.Join(pwd, *clientSecretsFile)
return nil, fmt.Errorf(missingClientSecretsMessage, fullPath)
}
cfg := new(Config)
err = json.Unmarshal(data, &cfg)
if err != nil {
return nil, err
}
var redirectURI string
if len(cfg.Web.RedirectURIs) > 0 {
redirectURI = cfg.Web.RedirectURIs[0]
} else if len(cfg.Installed.RedirectURIs) > 0 {
redirectURI = cfg.Installed.RedirectURIs[0]
} else {
return nil, errors.New("Must specify a redirect URI in config file or when creating OAuth client")
}
return &oauth2.Config{
ClientID: cfg.Installed.ClientID,
ClientSecret: cfg.Installed.ClientSecret,
Scopes: scopes,
Endpoint: oauth2.Endpoint{cfg.Installed.AuthURI, cfg.Installed.TokenURI},
RedirectURL: redirectURI,
}, nil
}
// startWebServer starts a web server that listens on http://localhost:8080.
// The webserver waits for an oauth code in the three-legged auth flow.
func startWebServer() (codeCh chan string, err error) {
listener, err := net.Listen("tcp", "localhost:8080")
if err != nil {
return nil, err
}
codeCh = make(chan string)
go http.Serve(listener, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
code := r.FormValue("code")
codeCh <- code // send code to OAuth flow
listener.Close()
w.Header().Set("Content-Type", "text/plain")
fmt.Fprintf(w, "Received code: %v\r\nYou can now safely close this browser window.", code)
}))
return codeCh, nil
}
// NewOAuth2Client takes the user through the three-legged OAuth flow.
// It opens a browser in the native OS or outputs a URL, then blocks until
// the redirect completes to the /oauth2callback URI.
// It returns an instance of an HTTP client that can be passed to the
// constructor of an OAuth client.
// scopes is a variable number of OAuth scopes
func NewOAuth2Client(scopes ...string) (*http.Client, error) {
var ctx context.Context
tokenSource, err := NewOAuth2TokenSource(scopes...)
if err == nil {
return oauth2.NewClient(ctx, tokenSource), nil
}
return nil, err
}
// NewOAuth2TokenSource takes the user through the three-legged OAuth flow.
// It opens a browser in the native OS or outputs a URL, then blocks until
// the redirect completes to the /oauth2callback URI.
// It returns an instance of an OAuth token source that can be passed to the
// constructor of an OAuth client.
// scopes is a variable number of OAuth scopes
func NewOAuth2TokenSource(scopes ...string) (oauth2.TokenSource, error) {
config, err := readConfig(scopes)
if err != nil {
msg := fmt.Sprintf("Cannot read configuration file: %v", err)
return nil, errors.New(msg)
}
var ctx context.Context
// Try to read the token from the cache file.
// If an error occurs, do the three-legged OAuth flow because
// the token is invalid or doesn't exist.
//token, err := config.TokenCache.Token()
var token *oauth2.Token
data, err := ioutil.ReadFile(*cacheFile)
if err == nil {
err = json.Unmarshal(data, &token)
}
if (err != nil) || !token.Valid() {
// Start web server.
// This is how this program receives the authorization code
// when the browser redirects.
codeCh, err := startWebServer()
if err != nil {
return nil, err
}
// Open url in browser
url := config.AuthCodeURL("")
err = openURL(url)
if err != nil {
fmt.Println("Visit the URL below to get a code.",
" This program will pause until the site is visted.")
} else {
fmt.Println("Your browser has been opened to an authorization URL.",
" This program will resume once authorization has been provided.\n")
}
fmt.Println(url)
// Wait for the web server to get the code.
code := <-codeCh
// This code caches the authorization code on the local
// filesystem, if necessary, as long as the TokenCache
// attribute in the config is set.
token, err = config.Exchange(ctx, code)
if err != nil {
return nil, err
}
data, err := json.Marshal(token)
ioutil.WriteFile(*cacheFile, data, 0644)
}
return oauth2.StaticTokenSource(token), nil
}

View File

@ -0,0 +1,4 @@
all:
gnostic swagger.yaml --go-client-out=apis_guru
go install

View File

@ -0,0 +1,45 @@
// +build ignore
// This file is omitted when getting with `go get github.com/googleapis/gnostic/...`
package main
import (
"fmt"
"sort"
"github.com/googleapis/gnostic/plugins/gnostic-go-generator/examples/v2.0/apis_guru/apis_guru"
)
func main() {
c := apis_guru.NewClient("http://api.apis.guru/v2")
metrics, err := c.GetMetrics()
if err != nil {
panic(err)
}
fmt.Printf("%+v\n", metrics)
apis, err := c.ListAPIs()
if err != nil {
panic(err)
}
keys := make([]string, 0)
for key, _ := range *apis.OK {
keys = append(keys, key)
}
sort.Strings(keys)
for _, key := range keys {
api := (*apis.OK)[key]
versions := make([]string, 0)
for key, _ := range api.Versions {
versions = append(versions, key)
}
sort.Strings(versions)
fmt.Printf("[%s]:%+v\n", key, versions)
}
api := (*apis.OK)["xkcd.com"].Versions["1.0.0"]
fmt.Printf("%+v\n", api.SwaggerUrl)
}

View File

@ -0,0 +1,186 @@
swagger: '2.0'
schemes:
- https
host: api.apis.guru
basePath: /v2/
info:
contact:
email: founders@apis.guru
name: APIs.guru
url: 'http://APIs.guru'
description: |
Wikipedia for Web APIs. Repository of API specs in OpenAPI(fka Swagger) 2.0 format.
**Warning**: If you want to be notified about changes in advance please subscribe to our [Gitter channel](https://gitter.im/APIs-guru/api-models).
Client sample: [[Demo]](https://apis.guru/simple-ui) [[Repo]](https://github.com/APIs-guru/simple-ui)
license:
name: CC0 1.0
url: 'https://github.com/APIs-guru/api-models#licenses'
title: APIs.guru
version: '2.0'
x-logo:
url: 'https://apis.guru/branding/logo_vertical.svg'
externalDocs:
url: 'https://github.com/APIs-guru/api-models/blob/master/API.md'
produces:
- application/json
security: []
paths:
/list.json:
get:
description: |
List all APIs in the directory.
Returns links to OpenAPI specification for each API in the directory.
If API exist in multiply versions `preferred` one is explicitly marked.
Some basic info from OpenAPI spec is cached inside each object.
This allows to generate some simple views without need to fetch OpenAPI spec for each API.
operationId: listAPIs
responses:
'200':
description: OK
schema:
$ref: '#/definitions/APIs'
summary: List all APIs
/metrics.json:
get:
description: |
Some basic metrics for the entire directory.
Just stunning numbers to put on a front page and are intended purely for WoW effect :)
operationId: getMetrics
responses:
'200':
description: OK
schema:
$ref: '#/definitions/Metrics'
summary: Get basic metrics
definitions:
API:
additionalProperties: false
description: Meta information about API
properties:
added:
description: Timestamp when the API was first added to the directory
format: date-time
type: string
preferred:
description: Recommended version
type: string
versions:
additionalProperties:
$ref: '#/definitions/ApiVersion'
description: List of supported versions of the API
minProperties: 1
type: object
required:
- added
- preferred
- versions
type: object
APIs:
additionalProperties:
$ref: '#/definitions/API'
description: |
List of API details.
It is a JSON object with API IDs(`<provider>[:<service>]`) as keys.
example:
'googleapis.com:drive':
added: '2015-02-22T20:00:45.000Z'
preferred: v3
versions:
v2:
added: '2015-02-22T20:00:45.000Z'
info:
title: Drive
version: v2
x-apiClientRegistration:
url: 'https://console.developers.google.com'
x-logo:
url: 'https://api.apis.guru/v2/cache/logo/https_www.gstatic.com_images_icons_material_product_2x_drive_32dp.png'
x-origin:
format: google
url: 'https://www.googleapis.com/discovery/v1/apis/drive/v2/rest'
version: v1
x-preferred: false
x-providerName: googleapis.com
x-serviceName: drive
swaggerUrl: 'https://api.apis.guru/v2/specs/googleapis.com/drive/v2/swagger.json'
swaggerYamlUrl: 'https://api.apis.guru/v2/specs/googleapis.com/drive/v2/swagger.yaml'
updated: '2016-06-17T00:21:44.000Z'
v3:
added: '2015-12-12T00:25:13.000Z'
info:
title: Drive
version: v3
x-apiClientRegistration:
url: 'https://console.developers.google.com'
x-logo:
url: 'https://api.apis.guru/v2/cache/logo/https_www.gstatic.com_images_icons_material_product_2x_drive_32dp.png'
x-origin:
format: google
url: 'https://www.googleapis.com/discovery/v1/apis/drive/v3/rest'
version: v1
x-preferred: true
x-providerName: googleapis.com
x-serviceName: drive
swaggerUrl: 'https://api.apis.guru/v2/specs/googleapis.com/drive/v3/swagger.json'
swaggerYamlUrl: 'https://api.apis.guru/v2/specs/googleapis.com/drive/v3/swagger.yaml'
updated: '2016-06-17T00:21:44.000Z'
minProperties: 1
type: object
ApiVersion:
additionalProperties: false
properties:
added:
description: Timestamp when the version was added
format: date-time
type: string
info:
description: Copy of `info` section from Swagger spec
minProperties: 1
type: object
swaggerUrl:
description: URL to Swagger spec in JSON format
format: url
type: string
swaggerYamlUrl:
description: URL to Swagger spec in YAML format
format: url
type: string
updated:
description: Timestamp when the version was updated
format: date-time
type: string
required:
- added
- updated
- swaggerUrl
- swaggerYamlUrl
- info
type: object
Metrics:
additionalProperties: false
description: List of basic metrics
example:
numAPIs: 238
numEndpoints: 6448
numSpecs: 302
properties:
numAPIs:
description: Number of APIs
minimum: 1
type: integer
numEndpoints:
description: Total number of endpoints inside all specifications
minimum: 1
type: integer
numSpecs:
description: Number of API specifications including different versions of the same API
minimum: 1
type: integer
required:
- numSpecs
- numAPIs
- numEndpoints
type: object

View File

@ -0,0 +1,20 @@
build:
go get golang.org/x/tools/cmd/goimports
go install github.com/googleapis/gnostic
go install github.com/googleapis/gnostic/plugins/gnostic-go-generator
rm -f $(GOPATH)/bin/gnostic-go-client $(GOPATH)/bin/gnostic-go-server
ln -s $(GOPATH)/bin/gnostic-go-generator $(GOPATH)/bin/gnostic-go-client
ln -s $(GOPATH)/bin/gnostic-go-generator $(GOPATH)/bin/gnostic-go-server
all: build
gnostic bookstore.json --go-generator-out=bookstore
clean:
rm -rf bookstore bookstore.text service/service
test: all
killall service; true # ignore errors due to no matching processes
cd service; go get .; go build; ./service &
go test
killall service

View File

@ -0,0 +1,23 @@
# Bookstore Example
This directory contains an OpenAPI description of a simple bookstore API.
Use this example to try the `gnostic-go-generator` plugin, which implements
`gnostic-go-client` and `gnostic-go-server` for generating API client and
server code, respectively.
Run "make all" to build and install `gnostic` and the Go plugins.
It will generate both client and server code. The API client and
server code will be in the `bookstore` package.
The `service` directory contains additional code that completes the server.
To build and run the service, `cd service` and do the following:
go get .
go build
./service &
To test the service with the generated client, go back up to the top-level
directory and run `go test`. The test in `bookstore_test.go` uses client
code generated in `bookstore` to verify the service.

View File

@ -0,0 +1,357 @@
{
"swagger": "2.0",
"info": {
"description": "A simple Bookstore API example.",
"title": "Bookstore",
"version": "1.0.0"
},
"host": "generated-bookstore.appspot.com",
"basePath": "/",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"schemes": [
"https"
],
"paths": {
"/shelves": {
"get": {
"description": "Return all shelves in the bookstore.",
"operationId": "listShelves",
"produces": [
"application/json"
],
"responses": {
"200": {
"description": "List of shelves in the bookstore.",
"schema": {
"$ref": "#/definitions/listShelvesResponse"
}
}
},
"security": [
]
},
"post": {
"description": "Create a new shelf in the bookstore.",
"operationId": "createShelf",
"parameters": [
{
"description": "A shelf resource to create.",
"in": "body",
"name": "shelf",
"required": true,
"schema": {
"$ref": "#/definitions/shelf"
}
}
],
"produces": [
"application/json"
],
"responses": {
"200": {
"description": "A newly created shelf resource.",
"schema": {
"$ref": "#/definitions/shelf"
}
}
}
},
"delete": {
"description": "Delete all shelves.",
"operationId": "deleteShelves",
"responses": {
"default": {
"description": "An empty response body."
}
}
}
},
"/shelves/{shelf}": {
"get": {
"description": "Get a single shelf resource with the given ID.",
"operationId": "getShelf",
"parameters": [
{
"description": "ID of the shelf to get.",
"format": "int64",
"in": "path",
"name": "shelf",
"required": true,
"type": "integer"
}
],
"produces": [
"application/json"
],
"responses": {
"200": {
"description": "A shelf resource.",
"schema": {
"$ref": "#/definitions/shelf"
}
},
"default": {
"description": "unexpected error",
"schema": {
"$ref": "#/definitions/error"
}
}
}
},
"delete": {
"description": "Delete a single shelf with the given ID.",
"operationId": "deleteShelf",
"parameters": [
{
"description": "ID of the shelf to delete.",
"format": "int64",
"in": "path",
"name": "shelf",
"required": true,
"type": "integer"
}
],
"responses": {
"default": {
"description": "An empty response body."
}
}
}
},
"/shelves/{shelf}/books": {
"get": {
"description": "Return all books in a shelf with the given ID.",
"operationId": "listBooks",
"parameters": [
{
"description": "ID of the shelf whose books should be returned.",
"format": "int64",
"in": "path",
"name": "shelf",
"required": true,
"type": "integer"
}
],
"produces": [
"application/json"
],
"responses": {
"200": {
"description": "List of books on the specified shelf.",
"schema": {
"$ref": "#/definitions/listBooksResponse"
}
},
"default": {
"description": "unexpected error",
"schema": {
"$ref": "#/definitions/error"
}
}
}
},
"post": {
"description": "Create a new book on the shelf.",
"operationId": "createBook",
"parameters": [
{
"description": "ID of the shelf where the book should be created.",
"format": "int64",
"in": "path",
"name": "shelf",
"required": true,
"type": "integer"
},
{
"description": "Book to create.",
"in": "body",
"name": "book",
"required": true,
"schema": {
"$ref": "#/definitions/book"
}
}
],
"produces": [
"application/json"
],
"responses": {
"200": {
"description": "A newly created book resource.",
"schema": {
"$ref": "#/definitions/book"
}
},
"default": {
"description": "unexpected error",
"schema": {
"$ref": "#/definitions/error"
}
}
}
}
},
"/shelves/{shelf}/books/{book}": {
"get": {
"description": "Get a single book with a given ID from a shelf.",
"operationId": "getBook",
"parameters": [
{
"description": "ID of the shelf from which to get the book.",
"format": "int64",
"in": "path",
"name": "shelf",
"required": true,
"type": "integer"
},
{
"description": "ID of the book to get from the shelf.",
"format": "int64",
"in": "path",
"name": "book",
"required": true,
"type": "integer"
}
],
"produces": [
"application/json"
],
"responses": {
"200": {
"description": "A book resource.",
"schema": {
"$ref": "#/definitions/book"
}
},
"default": {
"description": "unexpected error",
"schema": {
"$ref": "#/definitions/error"
}
}
}
},
"delete": {
"description": "Delete a single book with a given ID from a shelf.",
"operationId": "deleteBook",
"parameters": [
{
"description": "ID of the shelf from which to delete the book.",
"format": "int64",
"in": "path",
"name": "shelf",
"required": true,
"type": "integer"
},
{
"description": "ID of the book to delete from the shelf.",
"format": "int64",
"in": "path",
"name": "book",
"required": true,
"type": "integer"
}
],
"responses": {
"default": {
"description": "An empty response body."
}
}
}
}
},
"definitions": {
"book": {
"properties": {
"author": {
"type": "string"
},
"name": {
"type": "string"
},
"title": {
"type": "string"
}
},
"required": [
"name",
"author",
"title"
]
},
"listBooksResponse": {
"properties": {
"books": {
"items": {
"$ref": "#/definitions/book"
},
"type": "array"
}
},
"required": [
"books"
],
"type": "object"
},
"listShelvesResponse": {
"properties": {
"shelves": {
"items": {
"$ref": "#/definitions/shelf"
},
"type": "array"
}
},
"type": "object"
},
"shelf": {
"properties": {
"name": {
"type": "string"
},
"theme": {
"type": "string"
}
},
"required": [
"name",
"theme"
]
},
"error": {
"required": [
"code",
"message"
],
"properties": {
"code": {
"type": "integer",
"format": "int32"
},
"message": {
"type": "string"
}
}
}
},
"security": [
{
"api_key": [
]
}
],
"securityDefinitions": {
"api_key": {
"in": "query",
"name": "key",
"type": "apiKey"
}
}
}

View File

@ -0,0 +1,19 @@
/*
Copyright 2017 Google Inc. All Rights Reserved.
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 bookstore exists to allow this repo to work with recursive go get.
// It will be filled in with auto generated code.
package bookstore

View File

@ -0,0 +1,239 @@
/*
Copyright 2017 Google Inc. All Rights Reserved.
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 test
import (
"fmt"
"net/http"
"strings"
"testing"
"github.com/googleapis/gnostic/plugins/gnostic-go-generator/examples/v2.0/bookstore/bookstore"
)
const service = "http://localhost:8080"
//const service = "http://generated-bookstore.appspot.com"
func TestBookstore(t *testing.T) {
// create a client
b := bookstore.NewClient(service, nil)
// reset the service by deleting all shelves
{
err := b.DeleteShelves()
if err != nil {
t.Log("delete shelves failed")
t.Fail()
}
}
// verify that the service has no shelves
{
response, err := b.ListShelves()
if err != nil {
t.Log("list shelves failed")
t.Fail()
}
if (response == nil) || (response.OK == nil) || (response.OK.Shelves != nil) {
t.Log(fmt.Sprintf("list shelves failed %+v", response.OK))
t.Log(fmt.Sprintf("list shelves failed len=%d", len(response.OK.Shelves)))
t.Fail()
}
}
// attempting to get a shelf should return an error
{
_, err := b.GetShelf(1)
if err == nil {
t.Log("get shelf failed to return an error")
t.Fail()
}
}
// attempting to get a book should return an error
{
_, err := b.GetBook(1, 2)
if err == nil {
t.Log("get book failed to return an error")
t.Fail()
}
}
// add a shelf
{
var shelf bookstore.Shelf
shelf.Theme = "mysteries"
response, err := b.CreateShelf(shelf)
if err != nil {
t.Log("create shelf mysteries failed")
t.Fail()
}
if (response.OK.Name != "shelves/1") ||
(response.OK.Theme != "mysteries") {
t.Log("create shelf mysteries failed")
t.Fail()
}
}
// add another shelf
{
var shelf bookstore.Shelf
shelf.Theme = "comedies"
response, err := b.CreateShelf(shelf)
if err != nil {
t.Log("create shelf comedies failed")
t.Fail()
}
if (response.OK.Name != "shelves/2") ||
(response.OK.Theme != "comedies") {
t.Log("create shelf comedies failed")
t.Fail()
}
}
// get the first shelf that was added
{
response, err := b.GetShelf(1)
if err != nil {
t.Log("get shelf mysteries failed")
t.Fail()
}
if (response.OK.Name != "shelves/1") ||
(response.OK.Theme != "mysteries") {
t.Log("get shelf mysteries failed")
t.Fail()
}
}
// list shelves and verify that there are 2
{
response, err := b.ListShelves()
if err != nil {
t.Log("list shelves failed")
t.Fail()
}
if len(response.OK.Shelves) != 2 {
t.Log("list shelves failed")
t.Fail()
}
}
// delete a shelf
{
err := b.DeleteShelf(2)
if err != nil {
t.Log("delete shelf failed")
t.Fail()
}
}
// list shelves and verify that there is only 1
{
response, err := b.ListShelves()
if err != nil {
t.Log("list shelves failed")
t.Fail()
}
if len(response.OK.Shelves) != 1 {
t.Log("list shelves failed")
t.Fail()
}
}
// list books on a shelf, verify that there are none
{
response, err := b.ListBooks(1)
if err != nil {
t.Log("list books failed")
t.Fail()
}
if len(response.OK.Books) != 0 {
t.Log("list books failed")
t.Fail()
}
}
// create a book
{
var book bookstore.Book
book.Author = "Agatha Christie"
book.Title = "And Then There Were None"
_, err := b.CreateBook(1, book)
if err != nil {
t.Log("create book failed")
t.Fail()
}
}
// create another book
{
var book bookstore.Book
book.Author = "Agatha Christie"
book.Title = "Murder on the Orient Express"
_, err := b.CreateBook(1, book)
if err != nil {
t.Log("create book failed")
t.Fail()
}
}
// get the first book that was added
{
_, err := b.GetBook(1, 1)
if err != nil {
t.Log("get book failed")
t.Fail()
}
}
// list the books on a shelf and verify that there are 2
{
response, err := b.ListBooks(1)
if err != nil {
t.Log("list books failed")
t.Fail()
}
if len(response.OK.Books) != 2 {
t.Log("list books failed")
t.Fail()
}
}
// delete a book
{
err := b.DeleteBook(1, 2)
if err != nil {
t.Log("delete book failed")
t.Fail()
}
}
// list the books on a shelf and verify that is only 1
{
response, err := b.ListBooks(1)
if err != nil {
t.Log("list books failed")
t.Fail()
}
if len(response.OK.Books) != 1 {
t.Log("list books failed")
t.Fail()
}
}
// verify the handling of a badly-formed request
{
req, err := http.NewRequest("POST", service+"/shelves", strings.NewReader(""))
if err != nil {
t.Log("bad request failed")
return
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return
}
// we expect a 400 (Bad Request) code
if resp.StatusCode != 400 {
t.Log("bad request failed")
t.Fail()
}
return
}
}

View File

@ -0,0 +1,9 @@
application: bookstore
version: 1
runtime: go
api_version: go1
handlers:
- url: /.*
script: _go_app
- url: /
static_dir: static

View File

@ -0,0 +1,27 @@
/*
Copyright 2017 Google Inc. All Rights Reserved.
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 main
import (
"github.com/googleapis/gnostic/plugins/gnostic-go-generator/examples/v2.0/bookstore/bookstore"
)
// init() is called when the package is loaded
// this allows this app to be trivially deployed to Google App Engine, which does not call main()
func init() {
bookstore.Initialize(NewService())
}

View File

@ -0,0 +1,34 @@
// +build !appengine
// This file is omitted when the app is built for Google App Engine
/*
Copyright 2017 Google Inc. All Rights Reserved.
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 main
import (
"log"
"github.com/googleapis/gnostic/plugins/gnostic-go-generator/examples/v2.0/bookstore/bookstore"
)
func main() {
err := bookstore.ServeHTTP(":8080")
if err != nil {
log.Printf("%v", err)
}
}

View File

@ -0,0 +1,195 @@
/*
Copyright 2017 Google Inc. All Rights Reserved.
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 main
import (
"errors"
"fmt"
"net/http"
"sync"
"github.com/googleapis/gnostic/plugins/gnostic-go-generator/examples/v2.0/bookstore/bookstore"
)
//
// The Service type implements a bookstore service.
// All objects are managed in an in-memory non-persistent store.
//
type Service struct {
// shelves are stored in a map keyed by shelf id
// books are stored in a two level map, keyed first by shelf id and then by book id
Shelves map[int64]*bookstore.Shelf
Books map[int64]map[int64]*bookstore.Book
LastShelfID int64 // the id of the last shelf that was added
LastBookID int64 // the id of the last book that was added
Mutex sync.Mutex // global mutex to synchronize service access
}
func NewService() *Service {
return &Service{
Shelves: make(map[int64]*bookstore.Shelf),
Books: make(map[int64]map[int64]*bookstore.Book),
}
}
func (service *Service) ListShelves(responses *bookstore.ListShelvesResponses) (err error) {
service.Mutex.Lock()
defer service.Mutex.Unlock()
// copy shelf ids from Shelves map keys
shelves := make([]bookstore.Shelf, 0, len(service.Shelves))
for _, shelf := range service.Shelves {
shelves = append(shelves, *shelf)
}
response := &bookstore.ListShelvesResponse{}
response.Shelves = shelves
(*responses).OK = response
return err
}
func (service *Service) CreateShelf(parameters *bookstore.CreateShelfParameters, responses *bookstore.CreateShelfResponses) (err error) {
service.Mutex.Lock()
defer service.Mutex.Unlock()
// assign an id and name to a shelf and add it to the Shelves map.
shelf := parameters.Shelf
service.LastShelfID++
sid := service.LastShelfID
shelf.Name = fmt.Sprintf("shelves/%d", sid)
service.Shelves[sid] = &shelf
(*responses).OK = &shelf
return err
}
func (service *Service) DeleteShelves() (err error) {
service.Mutex.Lock()
defer service.Mutex.Unlock()
// delete everything by reinitializing the Shelves and Books maps.
service.Shelves = make(map[int64]*bookstore.Shelf)
service.Books = make(map[int64]map[int64]*bookstore.Book)
service.LastShelfID = 0
service.LastBookID = 0
return nil
}
func (service *Service) GetShelf(parameters *bookstore.GetShelfParameters, responses *bookstore.GetShelfResponses) (err error) {
service.Mutex.Lock()
defer service.Mutex.Unlock()
// look up a shelf from the Shelves map.
shelf, err := service.getShelf(parameters.Shelf)
if err != nil {
(*responses).Default = &bookstore.Error{Code: int32(http.StatusNotFound), Message: err.Error()}
return nil
} else {
(*responses).OK = shelf
return nil
}
}
func (service *Service) DeleteShelf(parameters *bookstore.DeleteShelfParameters) (err error) {
service.Mutex.Lock()
defer service.Mutex.Unlock()
// delete a shelf by removing the shelf from the Shelves map and the associated books from the Books map.
delete(service.Shelves, parameters.Shelf)
delete(service.Books, parameters.Shelf)
return nil
}
func (service *Service) ListBooks(parameters *bookstore.ListBooksParameters, responses *bookstore.ListBooksResponses) (err error) {
service.Mutex.Lock()
defer service.Mutex.Unlock()
// list the books in a shelf
_, err = service.getShelf(parameters.Shelf)
if err != nil {
(*responses).Default = &bookstore.Error{Code: int32(http.StatusNotFound), Message: err.Error()}
return nil
}
shelfBooks := service.Books[parameters.Shelf]
books := make([]bookstore.Book, 0, len(shelfBooks))
for _, book := range shelfBooks {
books = append(books, *book)
}
response := &bookstore.ListBooksResponse{}
response.Books = books
(*responses).OK = response
return nil
}
func (service *Service) CreateBook(parameters *bookstore.CreateBookParameters, responses *bookstore.CreateBookResponses) (err error) {
service.Mutex.Lock()
defer service.Mutex.Unlock()
// return "not found" if the shelf doesn't exist
shelf, err := service.getShelf(parameters.Shelf)
if err != nil {
(*responses).Default = &bookstore.Error{Code: int32(http.StatusNotFound), Message: err.Error()}
return nil
}
// assign an id and name to a book and add it to the Books map.
service.LastBookID++
bid := service.LastBookID
book := parameters.Book
book.Name = fmt.Sprintf("%s/books/%d", shelf.Name, bid)
if service.Books[parameters.Shelf] == nil {
service.Books[parameters.Shelf] = make(map[int64]*bookstore.Book)
}
service.Books[parameters.Shelf][bid] = &book
(*responses).OK = &book
return err
}
func (service *Service) GetBook(parameters *bookstore.GetBookParameters, responses *bookstore.GetBookResponses) (err error) {
service.Mutex.Lock()
defer service.Mutex.Unlock()
// get a book from the Books map
book, err := service.getBook(parameters.Shelf, parameters.Book)
if err != nil {
(*responses).Default = &bookstore.Error{Code: int32(http.StatusNotFound), Message: err.Error()}
} else {
(*responses).OK = book
}
return nil
}
func (service *Service) DeleteBook(parameters *bookstore.DeleteBookParameters) (err error) {
service.Mutex.Lock()
defer service.Mutex.Unlock()
// delete a book by removing the book from the Books map.
delete(service.Books[parameters.Shelf], parameters.Book)
return nil
}
// internal helpers
func (service *Service) getShelf(sid int64) (shelf *bookstore.Shelf, err error) {
shelf, ok := service.Shelves[sid]
if !ok {
return nil, errors.New(fmt.Sprintf("Couldn't find shelf %d", sid))
} else {
return shelf, nil
}
}
func (service *Service) getBook(sid int64, bid int64) (book *bookstore.Book, err error) {
_, err = service.getShelf(sid)
if err != nil {
return nil, err
}
book, ok := service.Books[sid][bid]
if !ok {
return nil, errors.New(fmt.Sprintf("Couldn't find book %d on shelf %d", bid, sid))
} else {
return book, nil
}
}

View File

@ -0,0 +1,20 @@
build:
go get golang.org/x/tools/cmd/goimports
go install github.com/googleapis/gnostic
go install github.com/googleapis/gnostic/plugins/gnostic-go-generator
rm -f $(GOPATH)/bin/gnostic-go-client $(GOPATH)/bin/gnostic-go-server
ln -s $(GOPATH)/bin/gnostic-go-generator $(GOPATH)/bin/gnostic-go-client
ln -s $(GOPATH)/bin/gnostic-go-generator $(GOPATH)/bin/gnostic-go-server
all: build
gnostic sample.yaml --go-generator-out=sample
clean:
rm -rf sample service/service
test: all
killall service; true # ignore errors due to no matching processes
cd service; go get .; go build; ./service &
go test
killall service

View File

@ -0,0 +1,24 @@
# API Sample
This directory contains an OpenAPI description of a sample API
that exercises various OpenAPI features.
Use this example to try the `gnostic-go-generator` plugin, which implements
`gnostic-go-client` and `gnostic-go-server` for generating API client and
server code, respectively.
Run "make all" to build and install `gnostic` and the Go plugins.
It will generate both client and server code. The API client and
server code will be in the `sample` package.
The `service` directory contains additional code that completes the server.
To build and run the service, `cd service` and do the following:
go get .
go build
./service &
To test the service with the generated client, go back up to the top-level
directory and run `go test`. The test in `sample_test.go` uses client
code generated in `sample` to verify the service.

View File

@ -0,0 +1,67 @@
swagger: '2.0'
schemes:
- https
host: sample.io
basePath: /
info:
title: sample.io
version: '1.0'
consumes:
- application/json
produces:
- application/json;charset=UTF-8
securityDefinitions:
api_key:
in: query
name: key
type: apiKey
paths:
/sample/{id}:
get:
operationId: "GetSample"
parameters:
- description: identifier
in: path
name: id
required: true
type: string
responses:
'200':
description: sample response
schema:
$ref: '#/definitions/Sample'
'401':
description: User doesn't have a valid session.
schema:
$ref: '#/definitions/APIError'
'404':
description: Unable to find supplied extractor ID.
schema:
$ref: '#/definitions/APIError'
security:
- api_key: []
summary: Get a sample response
tags:
- sample
- demo
definitions:
APIError:
properties:
code:
description: Internal error code
format: int
type: integer
message:
description: A message containing a brief description of the error
type: string
type: object
Sample:
properties:
id:
type: string
thing:
type: object
count:
format: int32
type: integer
type: object

View File

@ -0,0 +1,19 @@
/*
Copyright 2017 Google Inc. All Rights Reserved.
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 sample exists to allow this repo to work with recursive go get.
// It will be filled in with auto generated code.
package sample

View File

@ -0,0 +1,68 @@
/*
Copyright 2018 Google Inc. All Rights Reserved.
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 test
import (
"fmt"
"net/http"
"strings"
"testing"
"github.com/googleapis/gnostic/plugins/gnostic-go-generator/examples/v2.0/sample/sample"
)
const service = "http://localhost:8080"
func TestSample(t *testing.T) {
// create a client
s := sample.NewClient(service, nil)
// verify a sample request
{
message := "hello world"
response, err := s.GetSample(message)
if err != nil {
t.Log("get sample failed")
t.Fail()
}
if response.OK.Id != message || response.OK.Count != int32(len(message)) {
t.Log(fmt.Sprintf("get sample received %+v", response.OK))
t.Fail()
}
if (response == nil) || (response.OK == nil) {
t.Log(fmt.Sprintf("get sample failed %+v", response.OK))
t.Fail()
}
}
// verify the handling of an invalid request
{
req, err := http.NewRequest("GET", service+"/unsupported", strings.NewReader(""))
if err != nil {
t.Log("bad request failed")
return
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return
}
// we expect a 404 (Not Found) code
if resp.StatusCode != 404 {
t.Log("bad request failed")
t.Fail()
}
return
}
}

View File

@ -0,0 +1,9 @@
application: sample
version: 1
runtime: go
api_version: go1
handlers:
- url: /.*
script: _go_app
- url: /
static_dir: static

View File

@ -0,0 +1,27 @@
/*
Copyright 2018 Google Inc. All Rights Reserved.
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 main
import (
"github.com/googleapis/gnostic/plugins/gnostic-go-generator/examples/v2.0/sample/sample"
)
// init() is called when the package is loaded
// this allows this app to be trivially deployed to Google App Engine, which does not call main()
func init() {
sample.Initialize(NewService())
}

View File

@ -0,0 +1,34 @@
// +build !appengine
// This file is omitted when the app is built for Google App Engine
/*
Copyright 2018 Google Inc. All Rights Reserved.
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 main
import (
"log"
"github.com/googleapis/gnostic/plugins/gnostic-go-generator/examples/v2.0/sample/sample"
)
func main() {
err := sample.ServeHTTP(":8080")
if err != nil {
log.Printf("%v", err)
}
}

View File

@ -0,0 +1,38 @@
/*
Copyright 2018 Google Inc. All Rights Reserved.
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 main
import (
"github.com/googleapis/gnostic/plugins/gnostic-go-generator/examples/v2.0/sample/sample"
)
//
// The Service type implements a sample service.
//
type Service struct{}
func NewService() *Service {
return &Service{}
}
func (service *Service) GetSample(parameters *sample.GetSampleParameters, responses *sample.GetSampleResponses) (err error) {
(*responses).OK = &sample.Sample{
Id: parameters.Id,
Thing: map[string]interface{}{"thing": 123},
Count: int32(len(parameters.Id))}
return err
}

View File

@ -0,0 +1,3 @@
all:
gnostic swagger.json --go-client-out=xkcd
go install

View File

@ -0,0 +1,23 @@
package main
import (
"fmt"
"github.com/googleapis/gnostic/plugins/gnostic-go-generator/examples/v2.0/xkcd/xkcd"
)
func main() {
c := xkcd.NewClient("http://xkcd.com")
comic, err := c.Get_info_0_json()
if err != nil {
panic(err)
}
fmt.Printf("%+v\n", comic)
comic, err = c.Get_comicId_info_0_json(1800)
if err != nil {
panic(err)
}
fmt.Printf("%+v\n", comic)
}

View File

@ -0,0 +1,111 @@
{
"swagger": "2.0",
"schemes": [
"http"
],
"host": "xkcd.com",
"basePath": "/",
"info": {
"description": "Webcomic of romance, sarcasm, math, and language.",
"title": "XKCD",
"version": "1.0.0",
"x-apisguru-categories": [
"media"
],
"x-logo": {
"url": "https://api.apis.guru/v2/cache/logo/http_imgs.xkcd.com_static_terrible_small_logo.png"
},
"x-origin": {
"format": "swagger",
"url": "https://raw.githubusercontent.com/APIs-guru/unofficial_openapi_specs/master/xkcd.com/1.0.0/swagger.yaml",
"version": "2.0"
},
"x-preferred": true,
"x-providerName": "xkcd.com",
"x-tags": [
"humor",
"comics"
],
"x-unofficialSpec": true
},
"externalDocs": {
"url": "https://xkcd.com/json.html"
},
"securityDefinitions": {},
"paths": {
"/info.0.json": {
"get": {
"description": "Fetch current comic and metadata.\n",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/comic"
}
}
}
}
},
"/{comicId}/info.0.json": {
"get": {
"description": "Fetch comics and metadata by comic id.\n",
"parameters": [
{
"in": "path",
"name": "comicId",
"required": true,
"type": "number"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/comic"
}
}
}
}
}
},
"definitions": {
"comic": {
"properties": {
"alt": {
"type": "string"
},
"day": {
"type": "string"
},
"img": {
"type": "string"
},
"link": {
"type": "string"
},
"month": {
"type": "string"
},
"news": {
"type": "string"
},
"num": {
"type": "number"
},
"safe_title": {
"type": "string"
},
"title": {
"type": "string"
},
"transcript": {
"type": "string"
},
"year": {
"type": "string"
}
},
"type": "object"
}
}
}

View File

@ -0,0 +1,19 @@
/*
Copyright 2017 Google Inc. All Rights Reserved.
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 xkcd exists to allow this repo to work with recursive go get.
// It will be filled in with auto generated code.
package xkcd

View File

@ -0,0 +1,20 @@
build:
go get golang.org/x/tools/cmd/goimports
go install github.com/googleapis/gnostic
go install github.com/googleapis/gnostic/plugins/gnostic-go-generator
rm -f $(GOPATH)/bin/gnostic-go-client $(GOPATH)/bin/gnostic-go-server
ln -s $(GOPATH)/bin/gnostic-go-generator $(GOPATH)/bin/gnostic-go-client
ln -s $(GOPATH)/bin/gnostic-go-generator $(GOPATH)/bin/gnostic-go-server
all: build
gnostic bookstore.json --go-generator-out=bookstore
clean:
rm -rf bookstore bookstore.text service/service
test: all
killall service; true # ignore errors due to no matching processes
cd service; go get .; go build; ./service &
go test
killall service

View File

@ -0,0 +1,23 @@
# Bookstore Example
This directory contains an OpenAPI description of a simple bookstore API.
Use this example to try the `gnostic-go-generator` plugin, which implements
`gnostic-go-client` and `gnostic-go-server` for generating API client and
server code, respectively.
Run "make all" to build and install `gnostic` and the Go plugins.
It will generate both client and server code. The API client and
server code will be in the `bookstore` package.
The `service` directory contains additional code that completes the server.
To build and run the service, `cd service` and do the following:
go get .
go build
./service &
To test the service with the generated client, go back up to the top-level
directory and run `go test`. The test in `bookstore_test.go` uses client
code generated in `bookstore` to verify the service.

View File

@ -0,0 +1,392 @@
{
"openapi": "3.0.0",
"servers": [
{
"url": "https://generated-bookstore.appspot.com/"
}
],
"info": {
"description": "A simple Bookstore API example.",
"title": "Bookstore",
"version": "1.0.0"
},
"paths": {
"/shelves": {
"get": {
"description": "Return all shelves in the bookstore.",
"operationId": "listShelves",
"responses": {
"200": {
"description": "List of shelves in the bookstore.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/listShelvesResponse"
}
}
}
}
},
"security": []
},
"post": {
"description": "Create a new shelf in the bookstore.",
"operationId": "createShelf",
"responses": {
"200": {
"description": "A newly created shelf resource.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/shelf"
}
}
}
}
},
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/shelf"
}
}
},
"description": "A shelf resource to create.",
"required": true
}
},
"delete": {
"description": "Delete all shelves.",
"operationId": "deleteShelves",
"responses": {
"default": {
"description": "An empty response body."
}
}
}
},
"/shelves/{shelf}": {
"get": {
"description": "Get a single shelf resource with the given ID.",
"operationId": "getShelf",
"parameters": [
{
"description": "ID of the shelf to get.",
"in": "path",
"name": "shelf",
"required": true,
"schema": {
"type": "integer",
"format": "int64"
}
}
],
"responses": {
"200": {
"description": "A shelf resource.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/shelf"
}
}
}
},
"default": {
"description": "unexpected error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/error"
}
}
}
}
}
},
"delete": {
"description": "Delete a single shelf with the given ID.",
"operationId": "deleteShelf",
"parameters": [
{
"description": "ID of the shelf to delete.",
"in": "path",
"name": "shelf",
"required": true,
"schema": {
"type": "integer",
"format": "int64"
}
}
],
"responses": {
"default": {
"description": "An empty response body."
}
}
}
},
"/shelves/{shelf}/books": {
"get": {
"description": "Return all books in a shelf with the given ID.",
"operationId": "listBooks",
"parameters": [
{
"description": "ID of the shelf whose books should be returned.",
"in": "path",
"name": "shelf",
"required": true,
"schema": {
"type": "integer",
"format": "int64"
}
}
],
"responses": {
"200": {
"description": "List of books on the specified shelf.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/listBooksResponse"
}
}
}
},
"default": {
"description": "unexpected error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/error"
}
}
}
}
}
},
"post": {
"description": "Create a new book on the shelf.",
"operationId": "createBook",
"parameters": [
{
"description": "ID of the shelf where the book should be created.",
"in": "path",
"name": "shelf",
"required": true,
"schema": {
"type": "integer",
"format": "int64"
}
}
],
"responses": {
"200": {
"description": "A newly created book resource.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/book"
}
}
}
},
"default": {
"description": "unexpected error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/error"
}
}
}
}
},
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/book"
}
}
},
"description": "Book to create.",
"required": true
}
}
},
"/shelves/{shelf}/books/{book}": {
"get": {
"description": "Get a single book with a given ID from a shelf.",
"operationId": "getBook",
"parameters": [
{
"description": "ID of the shelf from which to get the book.",
"in": "path",
"name": "shelf",
"required": true,
"schema": {
"type": "integer",
"format": "int64"
}
},
{
"description": "ID of the book to get from the shelf.",
"in": "path",
"name": "book",
"required": true,
"schema": {
"type": "integer",
"format": "int64"
}
}
],
"responses": {
"200": {
"description": "A book resource.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/book"
}
}
}
},
"default": {
"description": "unexpected error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/error"
}
}
}
}
}
},
"delete": {
"description": "Delete a single book with a given ID from a shelf.",
"operationId": "deleteBook",
"parameters": [
{
"description": "ID of the shelf from which to delete the book.",
"in": "path",
"name": "shelf",
"required": true,
"schema": {
"type": "integer",
"format": "int64"
}
},
{
"description": "ID of the book to delete from the shelf.",
"in": "path",
"name": "book",
"required": true,
"schema": {
"type": "integer",
"format": "int64"
}
}
],
"responses": {
"default": {
"description": "An empty response body."
}
}
}
}
},
"security": [
{
"api_key": []
}
],
"components": {
"schemas": {
"book": {
"properties": {
"author": {
"type": "string"
},
"name": {
"type": "string"
},
"title": {
"type": "string"
}
},
"required": [
"name",
"author",
"title"
],
"type": "object"
},
"listBooksResponse": {
"properties": {
"books": {
"items": {
"$ref": "#/components/schemas/book"
},
"type": "array"
}
},
"required": [
"books"
],
"type": "object"
},
"listShelvesResponse": {
"properties": {
"shelves": {
"items": {
"$ref": "#/components/schemas/shelf"
},
"type": "array"
}
},
"type": "object"
},
"shelf": {
"properties": {
"name": {
"type": "string"
},
"theme": {
"type": "string"
}
},
"required": [
"name",
"theme"
],
"type": "object"
},
"error": {
"required": [
"code",
"message"
],
"properties": {
"code": {
"type": "integer",
"format": "int32"
},
"message": {
"type": "string"
}
},
"type": "object"
}
},
"securitySchemes": {
"api_key": {
"in": "query",
"name": "key",
"type": "apiKey"
}
}
}
}

View File

@ -0,0 +1,19 @@
/*
Copyright 2017 Google Inc. All Rights Reserved.
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 bookstore exists to allow this repo to work with recursive go get.
// It will be filled in with auto generated code.
package bookstore

View File

@ -0,0 +1,239 @@
/*
Copyright 2017 Google Inc. All Rights Reserved.
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 test
import (
"fmt"
"net/http"
"strings"
"testing"
"github.com/googleapis/gnostic/plugins/gnostic-go-generator/examples/v3.0/bookstore/bookstore"
)
const service = "http://localhost:8080"
//const service = "http://generated-bookstore.appspot.com"
func TestBookstore(t *testing.T) {
// create a client
b := bookstore.NewClient(service, nil)
// reset the service by deleting all shelves
{
err := b.DeleteShelves()
if err != nil {
t.Log("delete shelves failed")
t.Fail()
}
}
// verify that the service has no shelves
{
response, err := b.ListShelves()
if err != nil {
t.Log("list shelves failed")
t.Fail()
}
if (response == nil) || (response.OK == nil) || (response.OK.Shelves != nil) {
t.Log(fmt.Sprintf("list shelves failed %+v", response.OK))
t.Log(fmt.Sprintf("list shelves failed len=%d", len(response.OK.Shelves)))
t.Fail()
}
}
// attempting to get a shelf should return an error
{
response, err := b.GetShelf(1)
if err == nil {
t.Logf("get shelf failed to return an error (%+v)", response.OK)
t.Fail()
}
}
// attempting to get a book should return an error
{
response, err := b.GetBook(1, 2)
if err == nil {
t.Logf("get book failed to return an error (%+v)", response.OK)
t.Fail()
}
}
// add a shelf
{
var shelf bookstore.Shelf
shelf.Theme = "mysteries"
response, err := b.CreateShelf(shelf)
if err != nil {
t.Log("create shelf mysteries failed")
t.Fail()
}
if (response.OK.Name != "shelves/1") ||
(response.OK.Theme != "mysteries") {
t.Log("create shelf mysteries failed")
t.Fail()
}
}
// add another shelf
{
var shelf bookstore.Shelf
shelf.Theme = "comedies"
response, err := b.CreateShelf(shelf)
if err != nil {
t.Log("create shelf comedies failed")
t.Fail()
}
if (response.OK.Name != "shelves/2") ||
(response.OK.Theme != "comedies") {
t.Log("create shelf comedies failed")
t.Fail()
}
}
// get the first shelf that was added
{
response, err := b.GetShelf(1)
if err != nil {
t.Log("get shelf mysteries failed")
t.Fail()
}
if (response.OK.Name != "shelves/1") ||
(response.OK.Theme != "mysteries") {
t.Log("get shelf mysteries failed")
t.Fail()
}
}
// list shelves and verify that there are 2
{
response, err := b.ListShelves()
if err != nil {
t.Log("list shelves failed")
t.Fail()
}
if len(response.OK.Shelves) != 2 {
t.Log("list shelves failed")
t.Fail()
}
}
// delete a shelf
{
err := b.DeleteShelf(2)
if err != nil {
t.Log("delete shelf failed")
t.Fail()
}
}
// list shelves and verify that there is only 1
{
response, err := b.ListShelves()
if err != nil {
t.Log("list shelves failed")
t.Fail()
}
if len(response.OK.Shelves) != 1 {
t.Log("list shelves failed")
t.Fail()
}
}
// list books on a shelf, verify that there are none
{
response, err := b.ListBooks(1)
if err != nil {
t.Log("list books failed")
t.Fail()
}
if len(response.OK.Books) != 0 {
t.Log("list books failed")
t.Fail()
}
}
// create a book
{
var book bookstore.Book
book.Author = "Agatha Christie"
book.Title = "And Then There Were None"
_, err := b.CreateBook(1, book)
if err != nil {
t.Log("create book failed")
t.Fail()
}
}
// create another book
{
var book bookstore.Book
book.Author = "Agatha Christie"
book.Title = "Murder on the Orient Express"
_, err := b.CreateBook(1, book)
if err != nil {
t.Log("create book failed")
t.Fail()
}
}
// get the first book that was added
{
_, err := b.GetBook(1, 1)
if err != nil {
t.Log("get book failed")
t.Fail()
}
}
// list the books on a shelf and verify that there are 2
{
response, err := b.ListBooks(1)
if err != nil {
t.Log("list books failed")
t.Fail()
}
if len(response.OK.Books) != 2 {
t.Log("list books failed")
t.Fail()
}
}
// delete a book
{
err := b.DeleteBook(1, 2)
if err != nil {
t.Log("delete book failed")
t.Fail()
}
}
// list the books on a shelf and verify that is only 1
{
response, err := b.ListBooks(1)
if err != nil {
t.Log("list books failed")
t.Fail()
}
if len(response.OK.Books) != 1 {
t.Log("list books failed")
t.Fail()
}
}
// verify the handling of a badly-formed request
{
req, err := http.NewRequest("POST", service+"/shelves", strings.NewReader(""))
if err != nil {
t.Log("bad request failed")
return
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return
}
// we expect a 400 (Bad Request) code
if resp.StatusCode != 400 {
t.Log("bad request failed")
t.Fail()
}
return
}
}

View File

@ -0,0 +1,9 @@
application: bookstore
version: 1
runtime: go
api_version: go1
handlers:
- url: /.*
script: _go_app
- url: /
static_dir: static

View File

@ -0,0 +1,27 @@
/*
Copyright 2017 Google Inc. All Rights Reserved.
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 main
import (
"github.com/googleapis/gnostic/plugins/gnostic-go-generator/examples/v3.0/bookstore/bookstore"
)
// init() is called when the package is loaded
// this allows this app to be trivially deployed to Google App Engine, which does not call main()
func init() {
bookstore.Initialize(NewService())
}

View File

@ -0,0 +1,34 @@
// +build !appengine
// This file is omitted when the app is built for Google App Engine
/*
Copyright 2017 Google Inc. All Rights Reserved.
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 main
import (
"log"
"github.com/googleapis/gnostic/plugins/gnostic-go-generator/examples/v3.0/bookstore/bookstore"
)
func main() {
err := bookstore.ServeHTTP(":8080")
if err != nil {
log.Printf("%v", err)
}
}

View File

@ -0,0 +1,195 @@
/*
Copyright 2017 Google Inc. All Rights Reserved.
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 main
import (
"errors"
"fmt"
"net/http"
"sync"
"github.com/googleapis/gnostic/plugins/gnostic-go-generator/examples/v3.0/bookstore/bookstore"
)
//
// The Service type implements a bookstore service.
// All objects are managed in an in-memory non-persistent store.
//
type Service struct {
// shelves are stored in a map keyed by shelf id
// books are stored in a two level map, keyed first by shelf id and then by book id
Shelves map[int64]*bookstore.Shelf
Books map[int64]map[int64]*bookstore.Book
LastShelfID int64 // the id of the last shelf that was added
LastBookID int64 // the id of the last book that was added
Mutex sync.Mutex // global mutex to synchronize service access
}
func NewService() *Service {
return &Service{
Shelves: make(map[int64]*bookstore.Shelf),
Books: make(map[int64]map[int64]*bookstore.Book),
}
}
func (service *Service) ListShelves(responses *bookstore.ListShelvesResponses) (err error) {
service.Mutex.Lock()
defer service.Mutex.Unlock()
// copy shelf ids from Shelves map keys
shelves := make([]bookstore.Shelf, 0, len(service.Shelves))
for _, shelf := range service.Shelves {
shelves = append(shelves, *shelf)
}
response := &bookstore.ListShelvesResponse{}
response.Shelves = shelves
(*responses).OK = response
return err
}
func (service *Service) CreateShelf(parameters *bookstore.CreateShelfParameters, responses *bookstore.CreateShelfResponses) (err error) {
service.Mutex.Lock()
defer service.Mutex.Unlock()
// assign an id and name to a shelf and add it to the Shelves map.
shelf := parameters.Shelf
service.LastShelfID++
sid := service.LastShelfID
shelf.Name = fmt.Sprintf("shelves/%d", sid)
service.Shelves[sid] = &shelf
(*responses).OK = &shelf
return err
}
func (service *Service) DeleteShelves() (err error) {
service.Mutex.Lock()
defer service.Mutex.Unlock()
// delete everything by reinitializing the Shelves and Books maps.
service.Shelves = make(map[int64]*bookstore.Shelf)
service.Books = make(map[int64]map[int64]*bookstore.Book)
service.LastShelfID = 0
service.LastBookID = 0
return nil
}
func (service *Service) GetShelf(parameters *bookstore.GetShelfParameters, responses *bookstore.GetShelfResponses) (err error) {
service.Mutex.Lock()
defer service.Mutex.Unlock()
// look up a shelf from the Shelves map.
shelf, err := service.getShelf(parameters.Shelf)
if err != nil {
(*responses).Default = &bookstore.Error{Code: int32(http.StatusNotFound), Message: err.Error()}
return nil
} else {
(*responses).OK = shelf
return nil
}
}
func (service *Service) DeleteShelf(parameters *bookstore.DeleteShelfParameters) (err error) {
service.Mutex.Lock()
defer service.Mutex.Unlock()
// delete a shelf by removing the shelf from the Shelves map and the associated books from the Books map.
delete(service.Shelves, parameters.Shelf)
delete(service.Books, parameters.Shelf)
return nil
}
func (service *Service) ListBooks(parameters *bookstore.ListBooksParameters, responses *bookstore.ListBooksResponses) (err error) {
service.Mutex.Lock()
defer service.Mutex.Unlock()
// list the books in a shelf
_, err = service.getShelf(parameters.Shelf)
if err != nil {
(*responses).Default = &bookstore.Error{Code: int32(http.StatusNotFound), Message: err.Error()}
return nil
}
shelfBooks := service.Books[parameters.Shelf]
books := make([]bookstore.Book, 0, len(shelfBooks))
for _, book := range shelfBooks {
books = append(books, *book)
}
response := &bookstore.ListBooksResponse{}
response.Books = books
(*responses).OK = response
return nil
}
func (service *Service) CreateBook(parameters *bookstore.CreateBookParameters, responses *bookstore.CreateBookResponses) (err error) {
service.Mutex.Lock()
defer service.Mutex.Unlock()
// return "not found" if the shelf doesn't exist
shelf, err := service.getShelf(parameters.Shelf)
if err != nil {
(*responses).Default = &bookstore.Error{Code: int32(http.StatusNotFound), Message: err.Error()}
return nil
}
// assign an id and name to a book and add it to the Books map.
service.LastBookID++
bid := service.LastBookID
book := parameters.Book
book.Name = fmt.Sprintf("%s/books/%d", shelf.Name, bid)
if service.Books[parameters.Shelf] == nil {
service.Books[parameters.Shelf] = make(map[int64]*bookstore.Book)
}
service.Books[parameters.Shelf][bid] = &book
(*responses).OK = &book
return err
}
func (service *Service) GetBook(parameters *bookstore.GetBookParameters, responses *bookstore.GetBookResponses) (err error) {
service.Mutex.Lock()
defer service.Mutex.Unlock()
// get a book from the Books map
book, err := service.getBook(parameters.Shelf, parameters.Book)
if err != nil {
(*responses).Default = &bookstore.Error{Code: int32(http.StatusNotFound), Message: err.Error()}
} else {
(*responses).OK = book
}
return nil
}
func (service *Service) DeleteBook(parameters *bookstore.DeleteBookParameters) (err error) {
service.Mutex.Lock()
defer service.Mutex.Unlock()
// delete a book by removing the book from the Books map.
delete(service.Books[parameters.Shelf], parameters.Book)
return nil
}
// internal helpers
func (service *Service) getShelf(sid int64) (shelf *bookstore.Shelf, err error) {
shelf, ok := service.Shelves[sid]
if !ok {
return nil, errors.New(fmt.Sprintf("Couldn't find shelf %d", sid))
} else {
return shelf, nil
}
}
func (service *Service) getBook(sid int64, bid int64) (book *bookstore.Book, err error) {
_, err = service.getShelf(sid)
if err != nil {
return nil, err
}
book, ok := service.Books[sid][bid]
if !ok {
return nil, errors.New(fmt.Sprintf("Couldn't find book %d on shelf %d", bid, sid))
} else {
return book, nil
}
}

View File

@ -0,0 +1,26 @@
# urlshortener sample client
## Steps to run:
1. Generate the OpenAPI 3.0 description using `disco` (in the `gnostic/apps` directory).
disco get urlshortener --openapi3
2. (optional) View the JSON OpenAPI 3.0 description.
gnostic openapi3-urlshortener-v1.pb --json-out=-
3. Generate the urlshortener client.
gnostic openapi3-urlshortener-v1.pb --go-client-out=urlshortener
4. Build the client.
go install
5. Download `client_secrets.json` from the Google Cloud Developer Console.
6. Run the client
urlshortener

View File

@ -0,0 +1,62 @@
package main
import (
"fmt"
"log"
"github.com/docopt/docopt-go"
"github.com/googleapis/gnostic/plugins/gnostic-go-generator/examples/googleauth"
"github.com/googleapis/gnostic/plugins/gnostic-go-generator/examples/v3.0/urlshortener/urlshortener"
)
func main() {
usage := `
Usage:
urlshortener get <url>
urlshortener list
urlshortener insert <url>
`
arguments, err := docopt.Parse(usage, nil, false, "URL Shortener 1.0", false)
if err != nil {
log.Fatalf("%+v", err)
}
path := "https://www.googleapis.com/urlshortener/v1" // this should be generated
client, err := googleauth.NewOAuth2Client("https://www.googleapis.com/auth/urlshortener")
if err != nil {
log.Fatalf("Error building OAuth client: %v", err)
}
c := urlshortener.NewClient(path, client)
// get
if arguments["get"].(bool) {
response, err := c.Urlshortener_Url_Get("FULL", arguments["<url>"].(string))
if err != nil {
log.Fatalf("%+v", err)
}
fmt.Println(response.Default.LongUrl)
}
// list
if arguments["list"].(bool) {
response, err := c.Urlshortener_Url_List("", "")
if err != nil {
log.Fatalf("%+v", err)
}
for _, item := range response.Default.Items {
fmt.Printf("%-40s %s\n", item.Id, item.LongUrl)
}
}
// insert
if arguments["insert"].(bool) {
var url urlshortener.Url
url.LongUrl = arguments["<url>"].(string)
response, err := c.Urlshortener_Url_Insert(url)
if err != nil {
log.Fatalf("%+v", err)
}
fmt.Printf("%+v\n", response.Default.Id)
}
}

View File

@ -0,0 +1,19 @@
/*
Copyright 2017 Google Inc. All Rights Reserved.
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 urlshortener exists to allow this repo to work with recursive go get.
// It will be filled in with auto generated code.
package urlshortener

View File

@ -0,0 +1,50 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// 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 main
import (
"io/ioutil"
"log"
"os"
"os/exec"
"strings"
)
// Run goimports to format and update imports statements in generated code.
func goimports(filename string, inputBytes []byte) (outputBytes []byte, err error) {
if false {
return inputBytes, nil
}
cmd := exec.Command(os.Getenv("GOPATH") + "/bin/goimports")
input, _ := cmd.StdinPipe()
output, _ := cmd.StdoutPipe()
cmderr, _ := cmd.StderrPipe()
err = cmd.Start()
if err != nil {
return
}
input.Write(inputBytes)
input.Close()
outputBytes, _ = ioutil.ReadAll(output)
errors, _ := ioutil.ReadAll(cmderr)
if len(errors) > 0 {
errors := strings.Replace(string(errors), "<standard input>", filename, -1)
log.Printf("Syntax errors in generated code:\n%s", errors)
return inputBytes, nil
}
return
}

View File

@ -0,0 +1,123 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// 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 main
import (
surface "github.com/googleapis/gnostic/surface"
"strings"
"unicode"
)
type GoLanguageModel struct{}
func NewGoLanguageModel() *GoLanguageModel {
return &GoLanguageModel{}
}
// Prepare sets language-specific properties for all types and methods.
func (language *GoLanguageModel) Prepare(model *surface.Model) {
for _, t := range model.Types {
// determine the type used for Go language implementation of the type
t.TypeName = strings.Title(filteredTypeName(t.Name))
for _, f := range t.Fields {
f.FieldName = goFieldName(f.Name)
f.ParameterName = goParameterName(f.Name)
switch f.Type {
case "number":
f.NativeType = "int"
case "integer":
switch f.Format {
case "int32":
f.NativeType = "int32"
case "int64":
f.NativeType = "int64"
default:
f.NativeType = "int64"
}
case "object":
f.NativeType = "interface{}"
case "string":
f.NativeType = "string"
default:
f.NativeType = strings.Title(f.Type)
}
}
}
for _, m := range model.Methods {
m.HandlerName = "Handle" + m.Name
m.ProcessorName = m.Name
m.ClientName = m.Name
}
}
func goParameterName(name string) string {
// lowercase first letter
a := []rune(name)
a[0] = unicode.ToLower(a[0])
name = string(a)
// replace dots with underscores
name = strings.Replace(name, ".", "_", -1)
// replaces dashes with underscores
name = strings.Replace(name, "-", "_", -1)
// avoid reserved words
if name == "type" {
return "myType"
}
return name
}
func goFieldName(name string) string {
name = strings.Replace(name, ".", "_", -1)
name = strings.Replace(name, "-", "_", -1)
name = snakeCaseToCamelCaseWithCapitalizedFirstLetter(name)
// avoid integers
if name == "200" {
return "OK"
} else if unicode.IsDigit(rune(name[0])) {
return "Code" + name
}
return name
}
func snakeCaseToCamelCaseWithCapitalizedFirstLetter(snakeCase string) (camelCase string) {
isToUpper := false
for _, runeValue := range snakeCase {
if isToUpper {
camelCase += strings.ToUpper(string(runeValue))
isToUpper = false
} else {
if runeValue == '_' {
isToUpper = true
} else {
camelCase += string(runeValue)
}
}
}
camelCase = strings.Title(camelCase)
return
}
func filteredTypeName(typeName string) (name string) {
// first take the last path segment
parts := strings.Split(typeName, "/")
name = parts[len(parts)-1]
// then take the last part of a dotted name
parts = strings.Split(name, ".")
name = parts[len(parts)-1]
return name
}

View File

@ -0,0 +1,29 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// 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 main
import "bytes"
type LineWriter struct {
bytes.Buffer
}
func NewLineWriter() *LineWriter {
return &LineWriter{}
}
func (w *LineWriter) WriteLine(line string) {
w.WriteString(line + "\n")
}

View File

@ -0,0 +1,76 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// 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.
// gnostic_go_generator is a sample Gnostic plugin that generates Go
// code that supports an API.
package main
import (
"encoding/json"
"errors"
"strings"
"github.com/golang/protobuf/proto"
plugins "github.com/googleapis/gnostic/plugins"
surface "github.com/googleapis/gnostic/surface"
)
// This is the main function for the code generation plugin.
func main() {
env, err := plugins.NewEnvironment()
env.RespondAndExitIfError(err)
packageName := env.Request.OutputPath
// Use the name used to run the plugin to decide which files to generate.
var files []string
switch {
case strings.Contains(env.Invocation, "gnostic-go-client"):
files = []string{"client.go", "types.go", "constants.go"}
case strings.Contains(env.Invocation, "gnostic-go-server"):
files = []string{"server.go", "provider.go", "types.go", "constants.go"}
default:
files = []string{"client.go", "server.go", "provider.go", "types.go", "constants.go"}
}
for _, model := range env.Request.Models {
switch model.TypeUrl {
case "surface.v1.Model":
surfaceModel := &surface.Model{}
err = proto.Unmarshal(model.Value, surfaceModel)
if err == nil {
// Customize the code surface model for Go
NewGoLanguageModel().Prepare(surfaceModel)
modelJSON, _ := json.MarshalIndent(surfaceModel, "", " ")
modelFile := &plugins.File{Name: "model.json", Data: modelJSON}
env.Response.Files = append(env.Response.Files, modelFile)
// Create the renderer.
renderer, err := NewServiceRenderer(surfaceModel)
renderer.Package = packageName
env.RespondAndExitIfError(err)
// Run the renderer to generate files and add them to the response object.
err = renderer.Render(env.Response, files)
env.RespondAndExitIfError(err)
// Return with success.
env.RespondAndExit()
}
}
}
err = errors.New("No generated code surface model is available.")
env.RespondAndExitIfError(err)
}

View File

@ -0,0 +1,178 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// 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 main
import (
"strings"
surface "github.com/googleapis/gnostic/surface"
)
// ParameterList returns a string representation of a method's parameters
func ParameterList(parametersType *surface.Type) string {
result := ""
if parametersType != nil {
for _, field := range parametersType.Fields {
result += field.ParameterName + " " + field.NativeType + "," + "\n"
}
}
return result
}
func (renderer *Renderer) RenderClient() ([]byte, error) {
f := NewLineWriter()
f.WriteLine("// GENERATED FILE: DO NOT EDIT!")
f.WriteLine(``)
f.WriteLine("package " + renderer.Package)
// imports will be automatically added by goimports
f.WriteLine(`// Client represents an API client.`)
f.WriteLine(`type Client struct {`)
f.WriteLine(` service string`)
f.WriteLine(` APIKey string`)
f.WriteLine(` client *http.Client`)
f.WriteLine(`}`)
f.WriteLine(`// NewClient creates an API client.`)
f.WriteLine(`func NewClient(service string, c *http.Client) *Client {`)
f.WriteLine(` client := &Client{}`)
f.WriteLine(` client.service = service`)
f.WriteLine(` if c != nil {`)
f.WriteLine(` client.client = c`)
f.WriteLine(` } else {`)
f.WriteLine(` client.client = http.DefaultClient`)
f.WriteLine(` }`)
f.WriteLine(` return client`)
f.WriteLine(`}`)
for _, method := range renderer.Model.Methods {
parametersType := renderer.Model.TypeWithTypeName(method.ParametersTypeName)
responsesType := renderer.Model.TypeWithTypeName(method.ResponsesTypeName)
f.WriteLine(commentForText(method.Description))
f.WriteLine(`func (client *Client) ` + method.ClientName + `(`)
f.WriteLine(ParameterList(parametersType) + `) (`)
if method.ResponsesTypeName == "" {
f.WriteLine(`err error,`)
} else {
f.WriteLine(`response *` + method.ResponsesTypeName + `,`)
f.WriteLine(`err error,`)
}
f.WriteLine(` ) {`)
path := method.Path
path = strings.Replace(path, "{+", "{", -1)
f.WriteLine(`path := client.service + "` + path + `"`)
if parametersType != nil {
if parametersType.HasFieldWithPosition(surface.Position_PATH) {
for _, field := range parametersType.Fields {
if field.Position == surface.Position_PATH {
f.WriteLine(`path = strings.Replace(path, "{` + field.Name + `}", fmt.Sprintf("%v", ` +
field.ParameterName + `), 1)`)
}
}
}
if parametersType.HasFieldWithPosition(surface.Position_QUERY) {
f.WriteLine(`v := url.Values{}`)
for _, field := range parametersType.Fields {
if field.Position == surface.Position_QUERY {
if field.NativeType == "string" {
f.WriteLine(`if (` + field.ParameterName + ` != "") {`)
f.WriteLine(` v.Set("` + field.Name + `", ` + field.ParameterName + `)`)
f.WriteLine(`}`)
}
}
}
f.WriteLine(`if client.APIKey != "" {`)
f.WriteLine(` v.Set("key", client.APIKey)`)
f.WriteLine(`}`)
f.WriteLine(`if len(v) > 0 {`)
f.WriteLine(` path = path + "?" + v.Encode()`)
f.WriteLine(`}`)
}
}
if method.Method == "POST" {
f.WriteLine(`body := new(bytes.Buffer)`)
if parametersType != nil {
f.WriteLine(`json.NewEncoder(body).Encode(` + parametersType.FieldWithPosition(surface.Position_BODY).Name + `)`)
}
f.WriteLine(`req, err := http.NewRequest("` + method.Method + `", path, body)`)
f.WriteLine(`reqHeaders := make(http.Header)`)
f.WriteLine(`reqHeaders.Set("Content-Type", "application/json")`)
f.WriteLine(`req.Header = reqHeaders`)
} else {
f.WriteLine(`req, err := http.NewRequest("` + method.Method + `", path, nil)`)
}
f.WriteLine(`if err != nil {return}`)
f.WriteLine(`resp, err := client.client.Do(req)`)
f.WriteLine(`if err != nil {return}`)
f.WriteLine(`defer resp.Body.Close()`)
f.WriteLine(`if resp.StatusCode != 200 {`)
if responsesType != nil {
f.WriteLine(` return nil, errors.New(resp.Status)`)
} else {
f.WriteLine(` return errors.New(resp.Status)`)
}
f.WriteLine(`}`)
if responsesType != nil {
f.WriteLine(`response = &` + responsesType.Name + `{}`)
f.WriteLine(`switch {`)
// first handle everything that isn't "default"
for _, responseField := range responsesType.Fields {
if responseField.Name != "default" {
f.WriteLine(`case resp.StatusCode == ` + responseField.Name + `:`)
f.WriteLine(` body, err := ioutil.ReadAll(resp.Body)`)
f.WriteLine(` if err != nil {return nil, err}`)
f.WriteLine(` result := &` + responseField.NativeType + `{}`)
f.WriteLine(` err = json.Unmarshal(body, result)`)
f.WriteLine(` if err != nil {return nil, err}`)
f.WriteLine(` response.` + responseField.FieldName + ` = result`)
}
}
// then handle "default"
hasDefault := false
for _, responseField := range responsesType.Fields {
if responseField.Name == "default" {
hasDefault = true
f.WriteLine(`default:`)
f.WriteLine(` defer resp.Body.Close()`)
f.WriteLine(` body, err := ioutil.ReadAll(resp.Body)`)
f.WriteLine(` if err != nil {return nil, err}`)
f.WriteLine(` result := &` + responseField.NativeType + `{}`)
f.WriteLine(` err = json.Unmarshal(body, result)`)
f.WriteLine(` if err != nil {return nil, err}`)
f.WriteLine(` response.` + responseField.FieldName + ` = result`)
}
}
if !hasDefault {
f.WriteLine(`default:`)
f.WriteLine(` break`)
}
f.WriteLine(`}`) // close switch statement
}
f.WriteLine("return")
f.WriteLine("}")
}
return f.Bytes(), nil
}

View File

@ -0,0 +1,30 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// 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 main
func (renderer *Renderer) RenderConstants() ([]byte, error) {
f := NewLineWriter()
f.WriteLine("// GENERATED FILE: DO NOT EDIT!")
f.WriteLine(``)
f.WriteLine("package " + renderer.Package)
f.WriteLine(``)
f.WriteLine(`// ServicePath is the base URL of the service.`)
f.WriteLine(`const ServicePath = "` + `"`)
f.WriteLine(``)
f.WriteLine(`// OAuthScopes lists the OAuth scopes required by the service.`)
f.WriteLine(`const OAuthScopes = "` + `"`)
return f.Bytes(), nil
}

View File

@ -0,0 +1,64 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// 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 main
import (
"strings"
)
func (renderer *Renderer) RenderProvider() ([]byte, error) {
f := NewLineWriter()
f.WriteLine("// GENERATED FILE: DO NOT EDIT!\n")
f.WriteLine("package " + renderer.Package)
f.WriteLine(``)
f.WriteLine(`// To create a server, first write a class that implements this interface.`)
f.WriteLine(`// Then pass an instance of it to Initialize().`)
f.WriteLine(`type Provider interface {`)
for _, method := range renderer.Model.Methods {
parametersType := renderer.Model.TypeWithTypeName(method.ParametersTypeName)
responsesType := renderer.Model.TypeWithTypeName(method.ResponsesTypeName)
f.WriteLine(``)
f.WriteLine(commentForText(method.Description))
if parametersType != nil {
if responsesType != nil {
f.WriteLine(method.ProcessorName +
`(parameters *` + parametersType.Name +
`, responses *` + responsesType.Name + `) (err error)`)
} else {
f.WriteLine(method.ProcessorName + `(parameters *` + parametersType.Name + `) (err error)`)
}
} else {
if responsesType != nil {
f.WriteLine(method.ProcessorName + `(responses *` + responsesType.Name + `) (err error)`)
} else {
f.WriteLine(method.ProcessorName + `() (err error)`)
}
}
}
f.WriteLine(`}`)
return f.Bytes(), nil
}
func commentForText(text string) string {
result := ""
lines := strings.Split(text, "\n")
for i, line := range lines {
if i > 0 {
result += "\n"
}
result += "// " + line
}
return result
}

View File

@ -0,0 +1,168 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// 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 main
import (
"fmt"
surface "github.com/googleapis/gnostic/surface"
)
func (renderer *Renderer) RenderServer() ([]byte, error) {
f := NewLineWriter()
f.WriteLine("// GENERATED FILE: DO NOT EDIT!")
f.WriteLine(``)
f.WriteLine("package " + renderer.Package)
f.WriteLine(``)
imports := []string{
"github.com/gorilla/mux",
"net/http",
}
f.WriteLine(``)
f.WriteLine(`import (`)
for _, imp := range imports {
f.WriteLine(`"` + imp + `"`)
}
f.WriteLine(`)`)
f.WriteLine(`func intValue(s string) (v int64) {`)
f.WriteLine(` v, _ = strconv.ParseInt(s, 10, 64)`)
f.WriteLine(` return v`)
f.WriteLine(`}`)
f.WriteLine(``)
f.WriteLine(`// This package-global variable holds the user-written Provider for API services.`)
f.WriteLine(`// See the Provider interface for details.`)
f.WriteLine(`var provider Provider`)
f.WriteLine(``)
f.WriteLine(`// These handlers serve API methods.`)
f.WriteLine(``)
for _, method := range renderer.Model.Methods {
parametersType := renderer.Model.TypeWithTypeName(method.ParametersTypeName)
responsesType := renderer.Model.TypeWithTypeName(method.ResponsesTypeName)
f.WriteLine(`// Handler`)
f.WriteLine(commentForText(method.Description))
f.WriteLine(`func ` + method.HandlerName + `(w http.ResponseWriter, r *http.Request) {`)
f.WriteLine(` var err error`)
if parametersType != nil {
f.WriteLine(`// instantiate the parameters structure`)
f.WriteLine(`parameters := &` + parametersType.Name + `{}`)
if method.Method == "POST" {
f.WriteLine(`// deserialize request from post data`)
f.WriteLine(`decoder := json.NewDecoder(r.Body)`)
f.WriteLine(`err = decoder.Decode(&parameters.` +
parametersType.FieldWithPosition(surface.Position_BODY).FieldName + `)`)
f.WriteLine(`if err != nil {`)
f.WriteLine(` w.WriteHeader(http.StatusBadRequest)`)
f.WriteLine(` w.Write([]byte(err.Error() + "\n"))`)
f.WriteLine(` return`)
f.WriteLine(`}`)
}
f.WriteLine(`// get request fields in path and query parameters`)
if parametersType.HasFieldWithPosition(surface.Position_PATH) {
f.WriteLine(`vars := mux.Vars(r)`)
}
if parametersType.HasFieldWithPosition(surface.Position_FORMDATA) {
f.WriteLine(`r.ParseForm()`)
}
for _, field := range parametersType.Fields {
if field.Position == surface.Position_PATH {
if field.Type == "string" {
f.WriteLine(fmt.Sprintf("// %+v", field))
f.WriteLine(`if value, ok := vars["` + field.Name + `"]; ok {`)
f.WriteLine(` parameters.` + field.FieldName + ` = value`)
f.WriteLine(`}`)
} else {
f.WriteLine(`if value, ok := vars["` + field.Name + `"]; ok {`)
f.WriteLine(` parameters.` + field.FieldName + ` = intValue(value)`)
f.WriteLine(`}`)
}
} else if field.Position == surface.Position_FORMDATA {
f.WriteLine(`if len(r.Form["` + field.Name + `"]) > 0 {`)
f.WriteLine(` parameters.` + field.FieldName + ` = intValue(r.Form["` + field.Name + `"][0])`)
f.WriteLine(`}`)
}
}
}
if responsesType != nil {
f.WriteLine(`// instantiate the responses structure`)
f.WriteLine(`responses := &` + method.ResponsesTypeName + `{}`)
}
f.WriteLine(`// call the service provider`)
callLine := `err = provider.` + method.ProcessorName
if parametersType != nil {
if responsesType != nil {
callLine += `(parameters, responses)`
} else {
callLine += `(parameters)`
}
} else {
if responsesType != nil {
callLine += `(responses)`
} else {
callLine += `()`
}
}
f.WriteLine(callLine)
f.WriteLine(`if err == nil {`)
if responsesType != nil {
if responsesType.HasFieldWithName("OK") {
f.WriteLine(`if responses.OK != nil {`)
f.WriteLine(` // write the normal response`)
f.WriteLine(` encoder := json.NewEncoder(w)`)
f.WriteLine(` encoder.Encode(responses.OK)`)
f.WriteLine(` return`)
f.WriteLine(`}`)
}
if responsesType.HasFieldWithName("Default") {
f.WriteLine(`if responses.Default != nil {`)
f.WriteLine(` // write the error response`)
if responsesType.FieldWithName("Default").ServiceType(renderer.Model).FieldWithName("Code") != nil {
f.WriteLine(` w.WriteHeader(int(responses.Default.Code))`)
}
f.WriteLine(` encoder := json.NewEncoder(w)`)
f.WriteLine(` encoder.Encode(responses.Default)`)
f.WriteLine(` return`)
f.WriteLine(`}`)
}
}
f.WriteLine(`} else {`)
f.WriteLine(` w.WriteHeader(http.StatusInternalServerError)`)
f.WriteLine(` w.Write([]byte(err.Error() + "\n"))`)
f.WriteLine(` return`)
f.WriteLine(`}`)
f.WriteLine(`}`)
f.WriteLine(``)
}
f.WriteLine(`// Initialize the API service.`)
f.WriteLine(`func Initialize(p Provider) {`)
f.WriteLine(` provider = p`)
f.WriteLine(` var router = mux.NewRouter()`)
for _, method := range renderer.Model.Methods {
f.WriteLine(`router.HandleFunc("` + method.Path + `", ` + method.HandlerName + `).Methods("` + method.Method + `")`)
}
f.WriteLine(` http.Handle("/", router)`)
f.WriteLine(`}`)
f.WriteLine(``)
f.WriteLine(`// Provide the API service over HTTP.`)
f.WriteLine(`func ServeHTTP(address string) error {`)
f.WriteLine(` if provider == nil {`)
f.WriteLine(` return errors.New("Use ` + renderer.Package + `.Initialize() to set a service provider.")`)
f.WriteLine(` }`)
f.WriteLine(` return http.ListenAndServe(address, nil)`)
f.WriteLine(`}`)
return f.Bytes(), nil
}

View File

@ -0,0 +1,57 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// 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 main
import (
surface "github.com/googleapis/gnostic/surface"
)
func (renderer *Renderer) RenderTypes() ([]byte, error) {
f := NewLineWriter()
f.WriteLine(`// GENERATED FILE: DO NOT EDIT!`)
f.WriteLine(``)
f.WriteLine(`package ` + renderer.Package)
f.WriteLine(`// Types used by the API.`)
for _, modelType := range renderer.Model.Types {
f.WriteLine(`// ` + modelType.Description)
if modelType.Kind == surface.TypeKind_STRUCT {
f.WriteLine(`type ` + modelType.TypeName + ` struct {`)
for _, field := range modelType.Fields {
prefix := ""
if field.Kind == surface.FieldKind_REFERENCE {
prefix = "*"
} else if field.Kind == surface.FieldKind_ARRAY {
prefix = "[]"
} else if field.Kind == surface.FieldKind_MAP {
prefix = "map[string]"
}
f.WriteLine(field.FieldName + ` ` + prefix + field.NativeType + jsonTag(field))
}
f.WriteLine(`}`)
} else if modelType.Kind == surface.TypeKind_OBJECT {
f.WriteLine(`type ` + modelType.TypeName + ` map[string]` + modelType.ContentType)
} else {
f.WriteLine(`type ` + modelType.TypeName + ` interface {}`)
}
}
return f.Bytes(), nil
}
func jsonTag(field *surface.Field) string {
if field.Serialize {
return " `json:" + `"` + field.Name + `,omitempty"` + "`"
}
return ""
}

View File

@ -0,0 +1,67 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// 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 main
import (
"fmt"
_ "os"
"path/filepath"
plugins "github.com/googleapis/gnostic/plugins"
surface "github.com/googleapis/gnostic/surface"
)
// Renderer generates code for a surface.Model.
type Renderer struct {
Model *surface.Model
Package string // package name
}
// NewServiceRenderer creates a renderer.
func NewServiceRenderer(model *surface.Model) (renderer *Renderer, err error) {
renderer = &Renderer{}
renderer.Model = model
return renderer, nil
}
// Generate runs the renderer to generate the named files.
func (renderer *Renderer) Render(response *plugins.Response, files []string) (err error) {
for _, filename := range files {
file := &plugins.File{Name: filename}
switch filename {
case "client.go":
file.Data, err = renderer.RenderClient()
case "types.go":
file.Data, err = renderer.RenderTypes()
case "provider.go":
file.Data, err = renderer.RenderProvider()
case "server.go":
file.Data, err = renderer.RenderServer()
case "constants.go":
file.Data, err = renderer.RenderConstants()
default:
file.Data = nil
}
if err != nil {
response.Errors = append(response.Errors, fmt.Sprintf("ERROR %v", err))
}
// run generated Go files through goimports
if filepath.Ext(file.Name) == ".go" {
file.Data, err = goimports(file.Name, file.Data)
}
response.Files = append(response.Files, file)
}
return
}

View File

@ -0,0 +1,8 @@
# gnostic-summary
This directory contains a `gnostic` plugin that summarizes the contents of an OpenAPI description.
gnostic bookstore.json --summary-out=-
Here the `-` in the output path indicates that results are to be written to stdout.
A `.` will write a summary file into the current directory.

View File

@ -0,0 +1,122 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// 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.
// gnostic_go_generator is a sample Gnostic plugin that generates Go
// code that supports an API.
package main
import (
"log"
"github.com/golang/protobuf/proto"
openapiv2 "github.com/googleapis/gnostic/OpenAPIv2"
openapiv3 "github.com/googleapis/gnostic/OpenAPIv3"
plugins "github.com/googleapis/gnostic/plugins"
"github.com/googleapis/gnostic/printer"
)
// generate a simple report of an OpenAPI document's contents
func printDocumentV2(code *printer.Code, document *openapiv2.Document) {
code.Print("Swagger: %+v", document.Swagger)
code.Print("Host: %+v", document.Host)
code.Print("BasePath: %+v", document.BasePath)
if document.Info != nil {
code.Print("Info:")
code.Indent()
if document.Info.Title != "" {
code.Print("Title: %s", document.Info.Title)
}
if document.Info.Description != "" {
code.Print("Description: %s", document.Info.Description)
}
if document.Info.Version != "" {
code.Print("Version: %s", document.Info.Version)
}
code.Outdent()
}
code.Print("Paths:")
code.Indent()
for _, pair := range document.Paths.Path {
v := pair.Value
if v.Get != nil {
code.Print("GET %+v", pair.Name)
}
if v.Post != nil {
code.Print("POST %+v", pair.Name)
}
}
code.Outdent()
}
// generate a simple report of an OpenAPI document's contents
func printDocumentV3(code *printer.Code, document *openapiv3.Document) {
code.Print("OpenAPI: %+v", document.Openapi)
code.Print("Servers: %+v", document.Servers)
if document.Info != nil {
code.Print("Info:")
code.Indent()
if document.Info.Title != "" {
code.Print("Title: %s", document.Info.Title)
}
if document.Info.Description != "" {
code.Print("Description: %s", document.Info.Description)
}
if document.Info.Version != "" {
code.Print("Version: %s", document.Info.Version)
}
code.Outdent()
}
code.Print("Paths:")
code.Indent()
for _, pair := range document.Paths.Path {
v := pair.Value
if v.Get != nil {
code.Print("GET %+v", pair.Name)
}
if v.Post != nil {
code.Print("POST %+v", pair.Name)
}
}
code.Outdent()
}
// This is the main function for the plugin.
func main() {
env, err := plugins.NewEnvironment()
env.RespondAndExitIfError(err)
code := &printer.Code{}
for _, model := range env.Request.Models {
log.Printf("model %s", model.TypeUrl)
switch model.TypeUrl {
case "openapi.v2.Document":
documentv2 := &openapiv2.Document{}
err = proto.Unmarshal(model.Value, documentv2)
if err == nil {
printDocumentV2(code, documentv2)
}
case "openapi.v3.Document":
documentv3 := &openapiv3.Document{}
err = proto.Unmarshal(model.Value, documentv3)
if err == nil {
printDocumentV3(code, documentv3)
}
}
}
file := &plugins.File{
Name: "summary.txt",
Data: []byte(code.String()),
}
env.Response.Files = append(env.Response.Files, file)
env.RespondAndExit()
}

View File

@ -0,0 +1,13 @@
BINDIR=.build/debug
all:
swift build
install: all
cp $(BINDIR)/gnostic-swift-generator $(GOPATH)/bin/gnostic-swift-generator
cp $(BINDIR)/gnostic-swift-generator $(GOPATH)/bin/gnostic-swift-client
cp $(BINDIR)/gnostic-swift-generator $(GOPATH)/bin/gnostic-swift-server
clean:
rm -rf .build Packages

View File

@ -0,0 +1,26 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// 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.
import PackageDescription
let package = Package(
name: "SwiftOpenAPIPlugin",
targets: [
Target(name: "gnostic-swift-generator", dependencies: ["Gnostic"]),
Target(name: "Gnostic")
],
dependencies: [
.Package(url: "https://github.com/apple/swift-protobuf.git", Version(0,9,904)),
]
)

View File

@ -0,0 +1,15 @@
# OpenAPI Swift Generator Plugin
This directory contains a `gnostic` plugin that can be used to generate a Swift client library and scaffolding for a Swift server for an API with an OpenAPI description.
The plugin can be invoked like this:
gnostic bookstore.json --swift-generator-out=Bookstore
Where `Bookstore` is the name of a directory where the generated code will be written.
Both client and server code will be generated.
For example usage, see the [examples/bookstore](examples/bookstore) directory.
HTTP services are provided by the Kitura library.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,441 @@
// DO NOT EDIT.
//
// Generated by the Swift generator plugin for the protocol buffer compiler.
// Source: github.com/googleapis/gnostic/plugins/plugin.proto
//
// For information on using the generated types, please see the documenation:
// https://github.com/apple/swift-protobuf/
// Copyright 2017 Google Inc. All Rights Reserved.
//
// 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.
// gnostic can be extended with plugins.
// A plugin is uset a program that reads a Request from stdin
// and writes a Response to stdout.
//
// A plugin executable needs only to be placed somewhere in the path. The
// plugin should be named "gnostic_$NAME", and will then be used when the
// flag "--${NAME}_out" is passed to gnostic.
import Foundation
import SwiftProtobuf
// If the compiler emits an error on this type, it is because this file
// was generated by a version of the `protoc` Swift plug-in that is
// incompatible with the version of SwiftProtobuf to which you are linking.
// Please ensure that your are building against the same version of the API
// that was used to generate this file.
fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {}
typealias Version = _2
}
/// The version number of gnostic.
public struct Gnostic_Plugin_V1_Version: SwiftProtobuf.Message {
public static let protoMessageName: String = _protobuf_package + ".Version"
public var major: Int32 = 0
public var minor: Int32 = 0
public var patch: Int32 = 0
/// A suffix for alpha, beta or rc release, e.g., "alpha-1", "rc2". It should
/// be empty for mainline stable releases.
public var suffix: String = String()
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
/// Used by the decoding initializers in the SwiftProtobuf library, not generally
/// used directly. `init(serializedData:)`, `init(jsonUTF8Data:)`, and other decoding
/// initializers are defined in the SwiftProtobuf library. See the Message and
/// Message+*Additions` files.
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularInt32Field(value: &self.major)
case 2: try decoder.decodeSingularInt32Field(value: &self.minor)
case 3: try decoder.decodeSingularInt32Field(value: &self.patch)
case 4: try decoder.decodeSingularStringField(value: &self.suffix)
default: break
}
}
}
/// Used by the encoding methods of the SwiftProtobuf library, not generally
/// used directly. `Message.serializedData()`, `Message.jsonUTF8Data()`, and
/// other serializer methods are defined in the SwiftProtobuf library. See the
/// `Message` and `Message+*Additions` files.
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if self.major != 0 {
try visitor.visitSingularInt32Field(value: self.major, fieldNumber: 1)
}
if self.minor != 0 {
try visitor.visitSingularInt32Field(value: self.minor, fieldNumber: 2)
}
if self.patch != 0 {
try visitor.visitSingularInt32Field(value: self.patch, fieldNumber: 3)
}
if !self.suffix.isEmpty {
try visitor.visitSingularStringField(value: self.suffix, fieldNumber: 4)
}
try unknownFields.traverse(visitor: &visitor)
}
}
/// A parameter passed to the plugin from (or through) gnostic.
public struct Gnostic_Plugin_V1_Parameter: SwiftProtobuf.Message {
public static let protoMessageName: String = _protobuf_package + ".Parameter"
/// The name of the parameter as specified in the option string
public var name: String = String()
/// The parameter value as specified in the option string
public var value: String = String()
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
/// Used by the decoding initializers in the SwiftProtobuf library, not generally
/// used directly. `init(serializedData:)`, `init(jsonUTF8Data:)`, and other decoding
/// initializers are defined in the SwiftProtobuf library. See the Message and
/// Message+*Additions` files.
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularStringField(value: &self.name)
case 2: try decoder.decodeSingularStringField(value: &self.value)
default: break
}
}
}
/// Used by the encoding methods of the SwiftProtobuf library, not generally
/// used directly. `Message.serializedData()`, `Message.jsonUTF8Data()`, and
/// other serializer methods are defined in the SwiftProtobuf library. See the
/// `Message` and `Message+*Additions` files.
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.name.isEmpty {
try visitor.visitSingularStringField(value: self.name, fieldNumber: 1)
}
if !self.value.isEmpty {
try visitor.visitSingularStringField(value: self.value, fieldNumber: 2)
}
try unknownFields.traverse(visitor: &visitor)
}
}
/// An encoded Request is written to the plugin's stdin.
public struct Gnostic_Plugin_V1_Request: SwiftProtobuf.Message {
public static let protoMessageName: String = _protobuf_package + ".Request"
/// filename or URL of the original source document
public var sourceName: String {
get {return _storage._sourceName}
set {_uniqueStorage()._sourceName = newValue}
}
/// Output path specified in the plugin invocation.
public var outputPath: String {
get {return _storage._outputPath}
set {_uniqueStorage()._outputPath = newValue}
}
/// Plugin parameters parsed from the invocation string.
public var parameters: [Gnostic_Plugin_V1_Parameter] {
get {return _storage._parameters}
set {_uniqueStorage()._parameters = newValue}
}
/// The version number of gnostic.
public var compilerVersion: Gnostic_Plugin_V1_Version {
get {return _storage._compilerVersion ?? Gnostic_Plugin_V1_Version()}
set {_uniqueStorage()._compilerVersion = newValue}
}
/// Returns true if `compilerVersion` has been explicitly set.
public var hasCompilerVersion: Bool {return _storage._compilerVersion != nil}
/// Clears the value of `compilerVersion`. Subsequent reads from it will return its default value.
public mutating func clearCompilerVersion() {_storage._compilerVersion = nil}
/// API models
public var models: [SwiftProtobuf.Google_Protobuf_Any] {
get {return _storage._models}
set {_uniqueStorage()._models = newValue}
}
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
/// Used by the decoding initializers in the SwiftProtobuf library, not generally
/// used directly. `init(serializedData:)`, `init(jsonUTF8Data:)`, and other decoding
/// initializers are defined in the SwiftProtobuf library. See the Message and
/// Message+*Additions` files.
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
_ = _uniqueStorage()
try withExtendedLifetime(_storage) { (_storage: _StorageClass) in
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularStringField(value: &_storage._sourceName)
case 2: try decoder.decodeSingularStringField(value: &_storage._outputPath)
case 3: try decoder.decodeRepeatedMessageField(value: &_storage._parameters)
case 4: try decoder.decodeSingularMessageField(value: &_storage._compilerVersion)
case 5: try decoder.decodeRepeatedMessageField(value: &_storage._models)
default: break
}
}
}
}
/// Used by the encoding methods of the SwiftProtobuf library, not generally
/// used directly. `Message.serializedData()`, `Message.jsonUTF8Data()`, and
/// other serializer methods are defined in the SwiftProtobuf library. See the
/// `Message` and `Message+*Additions` files.
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
try withExtendedLifetime(_storage) { (_storage: _StorageClass) in
if !_storage._sourceName.isEmpty {
try visitor.visitSingularStringField(value: _storage._sourceName, fieldNumber: 1)
}
if !_storage._outputPath.isEmpty {
try visitor.visitSingularStringField(value: _storage._outputPath, fieldNumber: 2)
}
if !_storage._parameters.isEmpty {
try visitor.visitRepeatedMessageField(value: _storage._parameters, fieldNumber: 3)
}
if let v = _storage._compilerVersion {
try visitor.visitSingularMessageField(value: v, fieldNumber: 4)
}
if !_storage._models.isEmpty {
try visitor.visitRepeatedMessageField(value: _storage._models, fieldNumber: 5)
}
}
try unknownFields.traverse(visitor: &visitor)
}
fileprivate var _storage = _StorageClass.defaultInstance
}
/// The plugin writes an encoded Response to stdout.
public struct Gnostic_Plugin_V1_Response: SwiftProtobuf.Message {
public static let protoMessageName: String = _protobuf_package + ".Response"
/// Error message. If non-empty, the plugin failed.
/// The plugin process should exit with status code zero
/// even if it reports an error in this way.
///
/// This should be used to indicate errors which prevent the plugin from
/// operating as intended. Errors which indicate a problem in gnostic
/// 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.
public var errors: [String] = []
/// file output, each file will be written by gnostic to an appropriate location.
public var files: [Gnostic_Plugin_V1_File] = []
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
/// Used by the decoding initializers in the SwiftProtobuf library, not generally
/// used directly. `init(serializedData:)`, `init(jsonUTF8Data:)`, and other decoding
/// initializers are defined in the SwiftProtobuf library. See the Message and
/// Message+*Additions` files.
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeRepeatedStringField(value: &self.errors)
case 2: try decoder.decodeRepeatedMessageField(value: &self.files)
default: break
}
}
}
/// Used by the encoding methods of the SwiftProtobuf library, not generally
/// used directly. `Message.serializedData()`, `Message.jsonUTF8Data()`, and
/// other serializer methods are defined in the SwiftProtobuf library. See the
/// `Message` and `Message+*Additions` files.
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.errors.isEmpty {
try visitor.visitRepeatedStringField(value: self.errors, fieldNumber: 1)
}
if !self.files.isEmpty {
try visitor.visitRepeatedMessageField(value: self.files, fieldNumber: 2)
}
try unknownFields.traverse(visitor: &visitor)
}
}
/// File describes a file generated by a plugin.
public struct Gnostic_Plugin_V1_File: SwiftProtobuf.Message {
public static let protoMessageName: String = _protobuf_package + ".File"
/// name of the file
public var name: String = String()
/// data to be written to the file
public var data: Data = SwiftProtobuf.Internal.emptyData
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
/// Used by the decoding initializers in the SwiftProtobuf library, not generally
/// used directly. `init(serializedData:)`, `init(jsonUTF8Data:)`, and other decoding
/// initializers are defined in the SwiftProtobuf library. See the Message and
/// Message+*Additions` files.
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularStringField(value: &self.name)
case 2: try decoder.decodeSingularBytesField(value: &self.data)
default: break
}
}
}
/// Used by the encoding methods of the SwiftProtobuf library, not generally
/// used directly. `Message.serializedData()`, `Message.jsonUTF8Data()`, and
/// other serializer methods are defined in the SwiftProtobuf library. See the
/// `Message` and `Message+*Additions` files.
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.name.isEmpty {
try visitor.visitSingularStringField(value: self.name, fieldNumber: 1)
}
if !self.data.isEmpty {
try visitor.visitSingularBytesField(value: self.data, fieldNumber: 2)
}
try unknownFields.traverse(visitor: &visitor)
}
}
// MARK: - Code below here is support for the SwiftProtobuf runtime.
fileprivate let _protobuf_package = "gnostic.plugin.v1"
extension Gnostic_Plugin_V1_Version: SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "major"),
2: .same(proto: "minor"),
3: .same(proto: "patch"),
4: .same(proto: "suffix"),
]
public func _protobuf_generated_isEqualTo(other: Gnostic_Plugin_V1_Version) -> Bool {
if self.major != other.major {return false}
if self.minor != other.minor {return false}
if self.patch != other.patch {return false}
if self.suffix != other.suffix {return false}
if unknownFields != other.unknownFields {return false}
return true
}
}
extension Gnostic_Plugin_V1_Parameter: SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "name"),
2: .same(proto: "value"),
]
public func _protobuf_generated_isEqualTo(other: Gnostic_Plugin_V1_Parameter) -> Bool {
if self.name != other.name {return false}
if self.value != other.value {return false}
if unknownFields != other.unknownFields {return false}
return true
}
}
extension Gnostic_Plugin_V1_Request: SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .standard(proto: "source_name"),
2: .standard(proto: "output_path"),
3: .same(proto: "parameters"),
4: .standard(proto: "compiler_version"),
5: .same(proto: "models"),
]
fileprivate class _StorageClass {
var _sourceName: String = String()
var _outputPath: String = String()
var _parameters: [Gnostic_Plugin_V1_Parameter] = []
var _compilerVersion: Gnostic_Plugin_V1_Version? = nil
var _models: [SwiftProtobuf.Google_Protobuf_Any] = []
static let defaultInstance = _StorageClass()
private init() {}
init(copying source: _StorageClass) {
_sourceName = source._sourceName
_outputPath = source._outputPath
_parameters = source._parameters
_compilerVersion = source._compilerVersion
_models = source._models
}
}
fileprivate mutating func _uniqueStorage() -> _StorageClass {
if !isKnownUniquelyReferenced(&_storage) {
_storage = _StorageClass(copying: _storage)
}
return _storage
}
public func _protobuf_generated_isEqualTo(other: Gnostic_Plugin_V1_Request) -> Bool {
if _storage !== other._storage {
let storagesAreEqual: Bool = withExtendedLifetime((_storage, other._storage)) { (_storage, other_storage) in
if _storage._sourceName != other_storage._sourceName {return false}
if _storage._outputPath != other_storage._outputPath {return false}
if _storage._parameters != other_storage._parameters {return false}
if _storage._compilerVersion != other_storage._compilerVersion {return false}
if _storage._models != other_storage._models {return false}
return true
}
if !storagesAreEqual {return false}
}
if unknownFields != other.unknownFields {return false}
return true
}
}
extension Gnostic_Plugin_V1_Response: SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "errors"),
2: .same(proto: "files"),
]
public func _protobuf_generated_isEqualTo(other: Gnostic_Plugin_V1_Response) -> Bool {
if self.errors != other.errors {return false}
if self.files != other.files {return false}
if unknownFields != other.unknownFields {return false}
return true
}
}
extension Gnostic_Plugin_V1_File: SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "name"),
2: .same(proto: "data"),
]
public func _protobuf_generated_isEqualTo(other: Gnostic_Plugin_V1_File) -> Bool {
if self.name != other.name {return false}
if self.data != other.data {return false}
if unknownFields != other.unknownFields {return false}
return true
}
}

View File

@ -0,0 +1,579 @@
// DO NOT EDIT.
//
// Generated by the Swift generator plugin for the protocol buffer compiler.
// Source: github.com/googleapis/gnostic/surface/surface.proto
//
// For information on using the generated types, please see the documenation:
// https://github.com/apple/swift-protobuf/
// Copyright 2017 Google Inc. All Rights Reserved.
//
// 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.
// Model an API surface for code generation.
import Foundation
import SwiftProtobuf
// If the compiler emits an error on this type, it is because this file
// was generated by a version of the `protoc` Swift plug-in that is
// incompatible with the version of SwiftProtobuf to which you are linking.
// Please ensure that your are building against the same version of the API
// that was used to generate this file.
fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {}
typealias Version = _2
}
public enum Surface_V1_FieldKind: SwiftProtobuf.Enum {
public typealias RawValue = Int
case scalar // = 0
case map // = 1
case array // = 2
case reference // = 3
case UNRECOGNIZED(Int)
public init() {
self = .scalar
}
public init?(rawValue: Int) {
switch rawValue {
case 0: self = .scalar
case 1: self = .map
case 2: self = .array
case 3: self = .reference
default: self = .UNRECOGNIZED(rawValue)
}
}
public var rawValue: Int {
switch self {
case .scalar: return 0
case .map: return 1
case .array: return 2
case .reference: return 3
case .UNRECOGNIZED(let i): return i
}
}
}
public enum Surface_V1_TypeKind: SwiftProtobuf.Enum {
public typealias RawValue = Int
/// implement with named fields
case `struct` // = 0
/// implement with a map
case object // = 1
case UNRECOGNIZED(Int)
public init() {
self = .struct
}
public init?(rawValue: Int) {
switch rawValue {
case 0: self = .struct
case 1: self = .object
default: self = .UNRECOGNIZED(rawValue)
}
}
public var rawValue: Int {
switch self {
case .struct: return 0
case .object: return 1
case .UNRECOGNIZED(let i): return i
}
}
}
public enum Surface_V1_Position: SwiftProtobuf.Enum {
public typealias RawValue = Int
case body // = 0
case header // = 1
case formdata // = 2
case query // = 3
case path // = 4
case UNRECOGNIZED(Int)
public init() {
self = .body
}
public init?(rawValue: Int) {
switch rawValue {
case 0: self = .body
case 1: self = .header
case 2: self = .formdata
case 3: self = .query
case 4: self = .path
default: self = .UNRECOGNIZED(rawValue)
}
}
public var rawValue: Int {
switch self {
case .body: return 0
case .header: return 1
case .formdata: return 2
case .query: return 3
case .path: return 4
case .UNRECOGNIZED(let i): return i
}
}
}
/// Field is a field in a definition and can be associated with
/// a position in a request structure.
public struct Surface_V1_Field: SwiftProtobuf.Message {
public static let protoMessageName: String = _protobuf_package + ".Field"
/// the name as specified in the API description
public var name: String = String()
/// the specified content type of the field
public var type: String = String()
/// what kind of thing is this field? scalar, reference, array, map of strings to the specified type
public var kind: Surface_V1_FieldKind = .scalar
/// the specified format of the field
public var format: String = String()
/// "body", "header", "formdata", "query", or "path"
public var position: Surface_V1_Position = .body
/// the programming-language native type of the field
public var nativeType: String = String()
/// the name to use for a data structure field
public var fieldName: String = String()
/// the name to use for a function parameter
public var parameterName: String = String()
/// true if this field should be serialized (to JSON, etc)
public var serialize: Bool = false
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
/// Used by the decoding initializers in the SwiftProtobuf library, not generally
/// used directly. `init(serializedData:)`, `init(jsonUTF8Data:)`, and other decoding
/// initializers are defined in the SwiftProtobuf library. See the Message and
/// Message+*Additions` files.
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularStringField(value: &self.name)
case 2: try decoder.decodeSingularStringField(value: &self.type)
case 3: try decoder.decodeSingularEnumField(value: &self.kind)
case 4: try decoder.decodeSingularStringField(value: &self.format)
case 5: try decoder.decodeSingularEnumField(value: &self.position)
case 6: try decoder.decodeSingularStringField(value: &self.nativeType)
case 7: try decoder.decodeSingularStringField(value: &self.fieldName)
case 8: try decoder.decodeSingularStringField(value: &self.parameterName)
case 9: try decoder.decodeSingularBoolField(value: &self.serialize)
default: break
}
}
}
/// Used by the encoding methods of the SwiftProtobuf library, not generally
/// used directly. `Message.serializedData()`, `Message.jsonUTF8Data()`, and
/// other serializer methods are defined in the SwiftProtobuf library. See the
/// `Message` and `Message+*Additions` files.
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.name.isEmpty {
try visitor.visitSingularStringField(value: self.name, fieldNumber: 1)
}
if !self.type.isEmpty {
try visitor.visitSingularStringField(value: self.type, fieldNumber: 2)
}
if self.kind != .scalar {
try visitor.visitSingularEnumField(value: self.kind, fieldNumber: 3)
}
if !self.format.isEmpty {
try visitor.visitSingularStringField(value: self.format, fieldNumber: 4)
}
if self.position != .body {
try visitor.visitSingularEnumField(value: self.position, fieldNumber: 5)
}
if !self.nativeType.isEmpty {
try visitor.visitSingularStringField(value: self.nativeType, fieldNumber: 6)
}
if !self.fieldName.isEmpty {
try visitor.visitSingularStringField(value: self.fieldName, fieldNumber: 7)
}
if !self.parameterName.isEmpty {
try visitor.visitSingularStringField(value: self.parameterName, fieldNumber: 8)
}
if self.serialize != false {
try visitor.visitSingularBoolField(value: self.serialize, fieldNumber: 9)
}
try unknownFields.traverse(visitor: &visitor)
}
}
/// Type typically corresponds to a definition, parameter, or response
/// in an API and is represented by a type in generated code.
public struct Surface_V1_Type: SwiftProtobuf.Message {
public static let protoMessageName: String = _protobuf_package + ".Type"
/// the name to use for the type
public var name: String = String()
/// a meta-description of the type (struct, map, etc)
public var kind: Surface_V1_TypeKind = .struct
/// a comment describing the type
public var description_p: String = String()
/// if the type is a map, this is its content type
public var contentType: String = String()
/// the fields of the type
public var fields: [Surface_V1_Field] = []
/// language-specific type name
public var typeName: String = String()
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
/// Used by the decoding initializers in the SwiftProtobuf library, not generally
/// used directly. `init(serializedData:)`, `init(jsonUTF8Data:)`, and other decoding
/// initializers are defined in the SwiftProtobuf library. See the Message and
/// Message+*Additions` files.
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularStringField(value: &self.name)
case 2: try decoder.decodeSingularEnumField(value: &self.kind)
case 3: try decoder.decodeSingularStringField(value: &self.description_p)
case 4: try decoder.decodeSingularStringField(value: &self.contentType)
case 5: try decoder.decodeRepeatedMessageField(value: &self.fields)
case 6: try decoder.decodeSingularStringField(value: &self.typeName)
default: break
}
}
}
/// Used by the encoding methods of the SwiftProtobuf library, not generally
/// used directly. `Message.serializedData()`, `Message.jsonUTF8Data()`, and
/// other serializer methods are defined in the SwiftProtobuf library. See the
/// `Message` and `Message+*Additions` files.
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.name.isEmpty {
try visitor.visitSingularStringField(value: self.name, fieldNumber: 1)
}
if self.kind != .struct {
try visitor.visitSingularEnumField(value: self.kind, fieldNumber: 2)
}
if !self.description_p.isEmpty {
try visitor.visitSingularStringField(value: self.description_p, fieldNumber: 3)
}
if !self.contentType.isEmpty {
try visitor.visitSingularStringField(value: self.contentType, fieldNumber: 4)
}
if !self.fields.isEmpty {
try visitor.visitRepeatedMessageField(value: self.fields, fieldNumber: 5)
}
if !self.typeName.isEmpty {
try visitor.visitSingularStringField(value: self.typeName, fieldNumber: 6)
}
try unknownFields.traverse(visitor: &visitor)
}
}
/// Method is an operation of an API and typically has associated client and server code.
public struct Surface_V1_Method: SwiftProtobuf.Message {
public static let protoMessageName: String = _protobuf_package + ".Method"
/// Operation ID
public var operation: String = String()
/// HTTP path
public var path: String = String()
/// HTTP method name
public var method: String = String()
/// description of method
public var description_p: String = String()
/// Operation name, possibly generated from method and path
public var name: String = String()
/// name of the generated handler
public var handlerName: String = String()
/// name of the processing function in the service interface
public var processorName: String = String()
/// name of client
public var clientName: String = String()
/// parameters (input), with fields corresponding to input parameters
public var parametersTypeName: String = String()
/// responses (output), with fields corresponding to possible response values
public var responsesTypeName: String = String()
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
/// Used by the decoding initializers in the SwiftProtobuf library, not generally
/// used directly. `init(serializedData:)`, `init(jsonUTF8Data:)`, and other decoding
/// initializers are defined in the SwiftProtobuf library. See the Message and
/// Message+*Additions` files.
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularStringField(value: &self.operation)
case 2: try decoder.decodeSingularStringField(value: &self.path)
case 3: try decoder.decodeSingularStringField(value: &self.method)
case 4: try decoder.decodeSingularStringField(value: &self.description_p)
case 5: try decoder.decodeSingularStringField(value: &self.name)
case 6: try decoder.decodeSingularStringField(value: &self.handlerName)
case 7: try decoder.decodeSingularStringField(value: &self.processorName)
case 8: try decoder.decodeSingularStringField(value: &self.clientName)
case 9: try decoder.decodeSingularStringField(value: &self.parametersTypeName)
case 10: try decoder.decodeSingularStringField(value: &self.responsesTypeName)
default: break
}
}
}
/// Used by the encoding methods of the SwiftProtobuf library, not generally
/// used directly. `Message.serializedData()`, `Message.jsonUTF8Data()`, and
/// other serializer methods are defined in the SwiftProtobuf library. See the
/// `Message` and `Message+*Additions` files.
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.operation.isEmpty {
try visitor.visitSingularStringField(value: self.operation, fieldNumber: 1)
}
if !self.path.isEmpty {
try visitor.visitSingularStringField(value: self.path, fieldNumber: 2)
}
if !self.method.isEmpty {
try visitor.visitSingularStringField(value: self.method, fieldNumber: 3)
}
if !self.description_p.isEmpty {
try visitor.visitSingularStringField(value: self.description_p, fieldNumber: 4)
}
if !self.name.isEmpty {
try visitor.visitSingularStringField(value: self.name, fieldNumber: 5)
}
if !self.handlerName.isEmpty {
try visitor.visitSingularStringField(value: self.handlerName, fieldNumber: 6)
}
if !self.processorName.isEmpty {
try visitor.visitSingularStringField(value: self.processorName, fieldNumber: 7)
}
if !self.clientName.isEmpty {
try visitor.visitSingularStringField(value: self.clientName, fieldNumber: 8)
}
if !self.parametersTypeName.isEmpty {
try visitor.visitSingularStringField(value: self.parametersTypeName, fieldNumber: 9)
}
if !self.responsesTypeName.isEmpty {
try visitor.visitSingularStringField(value: self.responsesTypeName, fieldNumber: 10)
}
try unknownFields.traverse(visitor: &visitor)
}
}
/// Model represents an API for code generation.
public struct Surface_V1_Model: SwiftProtobuf.Message {
public static let protoMessageName: String = _protobuf_package + ".Model"
/// a free-form title for the API
public var name: String = String()
/// the types used by the API
public var types: [Surface_V1_Type] = []
/// the methods (functions) of the API
public var methods: [Surface_V1_Method] = []
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
/// Used by the decoding initializers in the SwiftProtobuf library, not generally
/// used directly. `init(serializedData:)`, `init(jsonUTF8Data:)`, and other decoding
/// initializers are defined in the SwiftProtobuf library. See the Message and
/// Message+*Additions` files.
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularStringField(value: &self.name)
case 2: try decoder.decodeRepeatedMessageField(value: &self.types)
case 3: try decoder.decodeRepeatedMessageField(value: &self.methods)
default: break
}
}
}
/// Used by the encoding methods of the SwiftProtobuf library, not generally
/// used directly. `Message.serializedData()`, `Message.jsonUTF8Data()`, and
/// other serializer methods are defined in the SwiftProtobuf library. See the
/// `Message` and `Message+*Additions` files.
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.name.isEmpty {
try visitor.visitSingularStringField(value: self.name, fieldNumber: 1)
}
if !self.types.isEmpty {
try visitor.visitRepeatedMessageField(value: self.types, fieldNumber: 2)
}
if !self.methods.isEmpty {
try visitor.visitRepeatedMessageField(value: self.methods, fieldNumber: 3)
}
try unknownFields.traverse(visitor: &visitor)
}
}
// MARK: - Code below here is support for the SwiftProtobuf runtime.
fileprivate let _protobuf_package = "surface.v1"
extension Surface_V1_FieldKind: SwiftProtobuf._ProtoNameProviding {
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
0: .same(proto: "SCALAR"),
1: .same(proto: "MAP"),
2: .same(proto: "ARRAY"),
3: .same(proto: "REFERENCE"),
]
}
extension Surface_V1_TypeKind: SwiftProtobuf._ProtoNameProviding {
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
0: .same(proto: "STRUCT"),
1: .same(proto: "OBJECT"),
]
}
extension Surface_V1_Position: SwiftProtobuf._ProtoNameProviding {
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
0: .same(proto: "BODY"),
1: .same(proto: "HEADER"),
2: .same(proto: "FORMDATA"),
3: .same(proto: "QUERY"),
4: .same(proto: "PATH"),
]
}
extension Surface_V1_Field: SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "name"),
2: .same(proto: "type"),
3: .same(proto: "kind"),
4: .same(proto: "format"),
5: .same(proto: "position"),
6: .same(proto: "nativeType"),
7: .same(proto: "fieldName"),
8: .same(proto: "parameterName"),
9: .same(proto: "serialize"),
]
public func _protobuf_generated_isEqualTo(other: Surface_V1_Field) -> Bool {
if self.name != other.name {return false}
if self.type != other.type {return false}
if self.kind != other.kind {return false}
if self.format != other.format {return false}
if self.position != other.position {return false}
if self.nativeType != other.nativeType {return false}
if self.fieldName != other.fieldName {return false}
if self.parameterName != other.parameterName {return false}
if self.serialize != other.serialize {return false}
if unknownFields != other.unknownFields {return false}
return true
}
}
extension Surface_V1_Type: SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "name"),
2: .same(proto: "kind"),
3: .same(proto: "description"),
4: .same(proto: "contentType"),
5: .same(proto: "fields"),
6: .same(proto: "typeName"),
]
public func _protobuf_generated_isEqualTo(other: Surface_V1_Type) -> Bool {
if self.name != other.name {return false}
if self.kind != other.kind {return false}
if self.description_p != other.description_p {return false}
if self.contentType != other.contentType {return false}
if self.fields != other.fields {return false}
if self.typeName != other.typeName {return false}
if unknownFields != other.unknownFields {return false}
return true
}
}
extension Surface_V1_Method: SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "operation"),
2: .same(proto: "path"),
3: .same(proto: "method"),
4: .same(proto: "description"),
5: .same(proto: "name"),
6: .same(proto: "handlerName"),
7: .same(proto: "processorName"),
8: .same(proto: "clientName"),
9: .same(proto: "parametersTypeName"),
10: .same(proto: "responsesTypeName"),
]
public func _protobuf_generated_isEqualTo(other: Surface_V1_Method) -> Bool {
if self.operation != other.operation {return false}
if self.path != other.path {return false}
if self.method != other.method {return false}
if self.description_p != other.description_p {return false}
if self.name != other.name {return false}
if self.handlerName != other.handlerName {return false}
if self.processorName != other.processorName {return false}
if self.clientName != other.clientName {return false}
if self.parametersTypeName != other.parametersTypeName {return false}
if self.responsesTypeName != other.responsesTypeName {return false}
if unknownFields != other.unknownFields {return false}
return true
}
}
extension Surface_V1_Model: SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "name"),
2: .same(proto: "types"),
3: .same(proto: "methods"),
]
public func _protobuf_generated_isEqualTo(other: Surface_V1_Model) -> Bool {
if self.name != other.name {return false}
if self.types != other.types {return false}
if self.methods != other.methods {return false}
if unknownFields != other.unknownFields {return false}
return true
}
}

View File

@ -0,0 +1,184 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// 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.
import Foundation
import Gnostic
extension ServiceRenderer {
func renderClient() -> String {
var code = CodePrinter()
code.print(header)
code.print()
code.print("// Client code")
code.print()
code.print("import Foundation")
code.print("import Dispatch")
code.print()
code.print("""
enum ClientError: Swift.Error {
case errorWithCode(Int)
}
""")
code.print()
code.print("public class Client {")
code.indent()
code.print("var service : String")
code.print()
code.print("""
public init(service: String) {
self.service = service
}
""")
for serviceMethod in self.methods {
code.print()
code.print("// " + serviceMethod.description + " Asynchronous.")
code.print("public func " + serviceMethod.name + "(" + asyncClientParametersDeclaration(serviceMethod) + ") throws {")
code.indent()
code.print("var path = self.service")
code.print("path = path + \"" + serviceMethod.path + "\"")
for serviceTypeField in parametersTypeFields(serviceMethod) {
if serviceTypeField.position == "path" {
code.print("path = path.replacingOccurrences(of:\"{" +
serviceTypeField.name +
"}\", with:\"\\(" +
serviceTypeField.name +
")\")")
}
}
code.print("guard let url = URL(string:path) else {")
code.indent()
code.print("throw ClientError.errorWithCode(0)")
code.outdent()
code.print("}")
code.print("var request = URLRequest(url:url)")
code.print("request.httpMethod = \"" + serviceMethod.method + "\"")
for serviceTypeField in parametersTypeFields(serviceMethod) {
if serviceTypeField.position == "body" {
code.print("let jsonObject = " + serviceTypeField.name + ".jsonObject()")
code.print("request.httpBody = try JSONSerialization.data(withJSONObject:jsonObject)")
}
}
if hasResponses(serviceMethod) {
code.print("fetch(request) {(data, response, error) in")
code.indent()
code.print("if error != nil {")
code.indent()
code.print("callback(nil, ClientError.errorWithCode(0))")
code.print("return")
code.outdent()
code.print("}")
code.print("guard let httpResponse = response else {")
code.indent()
code.print("callback(nil, ClientError.errorWithCode(0))")
code.print("return")
code.outdent()
code.print("}")
code.print("if httpResponse.statusCode == 200 {")
code.indent()
code.print("if let data = data {")
code.indent()
code.print("let jsonObject = try! JSONSerialization.jsonObject(with:data)")
code.print("if let value = " + serviceMethod.resultTypeName! + "(jsonObject:jsonObject) {")
code.indent()
code.print("callback(value, nil)")
code.print("return")
code.outdent()
code.print("}")
code.outdent()
code.print("}")
code.print("callback(nil, nil)")
code.outdent()
code.print("} else {")
code.indent()
code.print(" callback(nil, ClientError.errorWithCode(httpResponse.statusCode))")
code.outdent()
code.print("}")
code.outdent()
code.print("}")
} else {
code.print("fetch(request) {(data, response, error) in")
code.print("if error != nil {")
code.indent()
code.print("callback(ClientError.errorWithCode(0))")
code.print("return")
code.outdent()
code.print("}")
code.print("guard let httpResponse = response else {")
code.indent()
code.print("callback(ClientError.errorWithCode(0))")
code.print("return")
code.outdent()
code.print("}")
code.print("if httpResponse.statusCode == 200 {")
code.indent()
code.print("callback(nil)")
code.print("} else {")
code.indent()
code.print("callback(ClientError.errorWithCode(httpResponse.statusCode))")
code.outdent()
code.print("}")
code.outdent()
code.print("}")
}
code.outdent()
code.print("}")
code.print()
code.print("// " + serviceMethod.description + " Synchronous.")
code.print("public func " + serviceMethod.name + "(" + syncClientParametersDeclaration(serviceMethod) + ") throws " + syncClientReturnDeclaration(serviceMethod) + " {")
code.indent()
code.print("let sem = DispatchSemaphore(value: 0)")
if hasResponses(serviceMethod) {
code.print("var response : " + serviceMethod.resultTypeName! + "?")
}
code.print("var error : Swift.Error?")
if hasResponses(serviceMethod) {
code.print("try " + serviceMethod.name + "(" + parameterFieldNames(serviceMethod) + ") {r, e in")
code.indent()
code.print("response = r")
} else {
code.print("try " + serviceMethod.name + "(" + parameterFieldNames(serviceMethod) + ") {e in")
code.indent()
}
code.print("error = e")
code.print("sem.signal()")
code.outdent()
code.print("}")
code.print("sem.wait()")
code.print("if let actualError = error {")
code.indent()
code.print("throw actualError")
code.outdent()
code.print("}")
if hasResponses(serviceMethod) {
code.print("if let actualResponse = response {")
code.indent()
code.print("return actualResponse")
code.outdent()
code.print("} else {")
code.indent()
code.print("throw ClientError.errorWithCode(0)")
code.outdent()
code.print("}")
}
code.outdent()
code.print("}")
code.print()
}
code.outdent()
code.print("}")
return code.content
}
}

View File

@ -0,0 +1,157 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// 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.
import Foundation
import Gnostic
extension ServiceRenderer {
func renderFetch() -> String {
var code = CodePrinter()
code.print(header)
code.print("""
import Foundation
import Dispatch
import KituraNet
// fetch makes a synchronous request using KituraNet's ClientRequest class
// https://github.com/IBM-Swift/Kitura-net/blob/master/Sources/KituraNet/ClientRequest.swift
public func fetch(_ urlRequest: URLRequest) -> (Data?, HTTPURLResponse?, Error?) {
var data: Data?
var urlResponse: HTTPURLResponse?
let error: Error? = nil // make this mutable when we start using it
let sem = DispatchSemaphore(value: 0)
guard let method = urlRequest.httpMethod else {
return (data, urlResponse, error)
}
guard let url = urlRequest.url else {
return (data, urlResponse, error)
}
guard let scheme = url.scheme else {
return (data, urlResponse, error)
}
guard let host = url.host else {
return (data, urlResponse, error)
}
guard let port = url.port else {
return (data, urlResponse, error)
}
let options : [ClientRequest.Options] = [
.method(method),
.schema(scheme),
.hostname(host),
.port(Int16(port)),
.path(url.path),
// headers, etc
]
let request = HTTP.request(options) { (response) in
guard let response = response else {
sem.signal()
return
}
var responseData = Data()
do {
let code = response.httpStatusCode
try response.readAllData(into: &responseData)
data = responseData
urlResponse = HTTPURLResponse(url:url,
statusCode:code.rawValue,
httpVersion:"HTTP/1.1",
headerFields:[:])
sem.signal()
return
} catch {
sem.signal()
return
}
}
if let requestData = urlRequest.httpBody {
request.write(from:requestData)
}
request.end() // send the request
// now wait on the semaphore for a response
let result = sem.wait(timeout: DispatchTime.distantFuture)
switch result {
case .success:
return (data, urlResponse, error)
default: // includes .timeout
return (data, urlResponse, error)
}
}
// fetch makes an asynchronous request using KituraNet's ClientRequest class
// https://github.com/IBM-Swift/Kitura-net/blob/master/Sources/KituraNet/ClientRequest.swift
public func fetch(_ urlRequest: URLRequest, callback:@escaping (Data?, HTTPURLResponse?, Error?) -> ()) {
var data: Data?
var urlResponse: HTTPURLResponse?
let error: Error? = nil // make this mutable when we start using it
guard let method = urlRequest.httpMethod else {
callback (data, urlResponse, error)
return
}
guard let url = urlRequest.url else {
callback (data, urlResponse, error)
return
}
guard let scheme = url.scheme else {
callback (data, urlResponse, error)
return
}
guard let host = url.host else {
callback (data, urlResponse, error)
return
}
guard let port = url.port else {
callback (data, urlResponse, error)
return
}
let options : [ClientRequest.Options] = [
.method(method),
.schema(scheme),
.hostname(host),
.port(Int16(port)),
.path(url.path),
// headers, etc
]
let request = HTTP.request(options) { (response) in
guard let response = response else {
callback (data, urlResponse, nil)
return
}
var responseData = Data()
do {
let code = response.httpStatusCode
try response.readAllData(into: &responseData)
data = responseData
urlResponse = HTTPURLResponse(url:url,
statusCode:code.rawValue,
httpVersion:"HTTP/1.1",
headerFields:[:])
callback (data, urlResponse, nil)
return
} catch {
callback (data, urlResponse, nil)
return
}
}
if let requestData = urlRequest.httpBody {
request.write(from:requestData)
}
request.end() // send the request
}
""")
return code.content
}
}

View File

@ -0,0 +1,166 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// 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.
import Foundation
import Gnostic
extension ServiceRenderer {
func renderServer() -> String {
var code = CodePrinter()
code.print(header)
code.print()
code.print("// Service code")
code.print("import Kitura")
code.print("import KituraNet")
code.print("import Foundation")
code.print("// A server requires an instance of an implementation of this protocol.")
code.print("public protocol Service {")
code.indent()
for serviceMethod in self.methods {
code.print("// " + serviceMethod.description)
code.print("func " + serviceMethod.name + " (" +
protocolParametersDeclaration(serviceMethod) + ") throws " +
protocolReturnDeclaration(serviceMethod))
}
code.outdent()
code.print("}")
code.print("func intValue(_ s:String?) -> Int64 {")
code.indent()
code.print("guard let s = s else {")
code.indent()
code.print("return 0")
code.outdent()
code.print("}")
code.print("guard let value = Int64(s) else {")
code.indent()
code.print("return 0")
code.outdent()
code.print("}")
code.print("return value")
code.outdent()
code.print("}")
code.print("public func server(service : Service) -> Router {")
code.indent()
code.print("// Create a new router")
code.print("let router = Router()")
for serviceMethod in self.methods {
code.print("// " + serviceMethod.description)
code.print("router." + lowercase(serviceMethod.method) + "(\"" + kituraPath(serviceMethod) + "\") { req, res, next in")
code.indent()
if hasParameters(serviceMethod) {
code.print("// instantiate the parameters structure")
code.print("let parameters = " + serviceMethod.parametersTypeName! + "()")
for serviceTypeField in parametersTypeFields(serviceMethod) {
if serviceTypeField.position == "path" {
code.print("parameters." + serviceTypeField.name +
" = intValue(req.parameters[\"" +
serviceTypeField.name + "\"])")
}
}
if serviceMethod.method == "POST" {
code.print("// deserialize request from post data")
code.print("let bodyString = try req.readString() ?? \"\"")
code.print("guard let bodyData = bodyString.data(using:.utf8) else {")
code.indent()
code.print("try res.send(status:.badRequest).end()")
code.print("return")
code.outdent()
code.print("}")
code.print("var jsonObject : Any? = nil")
code.print("do {")
code.indent()
code.print("jsonObject = try JSONSerialization.jsonObject(with:bodyData)")
code.outdent()
code.print("} catch {")
code.indent()
code.print("try res.send(status:.badRequest).end()")
code.print("return")
code.outdent()
code.print("}")
code.print("guard let bodyObject = " + serviceMethod.resultTypeName! + "(jsonObject:jsonObject) else {")
code.print("try res.send(status:.badRequest).end()")
code.indent()
code.print("return")
code.outdent()
code.print("}")
code.print("parameters." + bodyParameterFieldName(serviceMethod) + " = bodyObject")
}
}
if hasParameters(serviceMethod) {
if hasResponses(serviceMethod) {
code.print("let responses = try service." + serviceMethod.name + "(parameters)")
} else {
code.print("try service." + serviceMethod.name + "(parameters)")
}
} else {
if hasResponses(serviceMethod) {
code.print("let responses = try service." + serviceMethod.name + "()")
} else {
code.print("try service." + serviceMethod.name + "()")
}
}
if hasResponses(serviceMethod) {
if responsesHasFieldNamedOK(serviceMethod) {
code.print("if let ok = responses.ok {")
code.indent()
code.print("let jsonObject = ok.jsonObject()")
code.print("let responseData = try JSONSerialization.data(withJSONObject:jsonObject)")
code.print("try res.send(data:responseData).end()")
code.print("return")
code.outdent()
code.print("}")
}
if responsesHasFieldNamedError(serviceMethod) {
code.print("if let errorResponse = responses.error {")
code.indent()
code.print("guard let statusCode = HTTPStatusCode(rawValue:Int(errorResponse.code)) else {")
code.indent()
code.print("try res.send(status:.unknown).end()")
code.print("return")
code.outdent()
code.print("}")
code.print("try res.send(status:statusCode).end()")
code.print("return")
code.outdent()
code.print("}")
}
code.print("try res.send(status:.internalServerError).end()")
} else {
code.print("try res.send(status:.OK).end()")
}
code.outdent()
code.print("}")
}
code.print("return router")
code.outdent()
code.print("}")
code.print("public func initialize(service: Service, port:Int) {")
code.indent()
code.print("// Create a new router")
code.print("let router = server(service:service)")
code.print("// Add an HTTP server and connect it to the router")
code.print("Kitura.addHTTPServer(onPort:port, with: router)")
code.outdent()
code.print("}")
code.print("public func run() {")
code.indent()
code.print("// Start the Kitura runloop (this call never returns)")
code.print("Kitura.run()")
code.outdent()
code.print("}")
return code.content
}
}

View File

@ -0,0 +1,112 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// 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.
import Foundation
import Gnostic
extension ServiceRenderer {
func renderTypes() -> String {
var code = CodePrinter()
code.print(header)
code.print()
code.print("// Common type declarations")
for serviceType in self.types {
code.print()
code.print("public class " + serviceType.name + " : CustomStringConvertible {")
code.indent()
for serviceTypeField in serviceType.fields {
code.print("public var " + serviceTypeField.name + " : " + serviceTypeField.typeName + " = " + serviceTypeField.initialValue)
}
code.print()
code.print("public init() {}")
code.print()
if serviceType.isInterfaceType {
code.print("public init?(jsonObject: Any?) {")
code.indent()
code.print("if let jsonDictionary = jsonObject as? [String:Any] {")
code.indent()
for serviceTypeField in serviceType.fields {
code.print("if let value : Any = jsonDictionary[\"" + serviceTypeField.jsonName + "\"] {")
code.indent()
if serviceTypeField.isArrayType {
code.print("var outArray : [" + serviceTypeField.elementType + "] = []")
code.print("let array = value as! [Any]")
code.print("for arrayValue in array {")
code.indent()
code.print("if let element = " + serviceTypeField.elementType + "(jsonObject:arrayValue) {")
code.indent()
code.print("outArray.append(element)")
code.outdent()
code.print("}")
code.outdent()
code.print("}")
code.print("self." + serviceTypeField.name + " = outArray")
} else if serviceTypeField.isCastableType {
code.print("self." + serviceTypeField.name + " = value as! " + serviceTypeField.typeName)
} else if serviceTypeField.isConvertibleType {
code.print("self." + serviceTypeField.name + " = " + serviceTypeField.typeName + "(value)")
}
code.outdent()
code.print("}")
}
code.outdent()
code.print("} else {")
code.indent()
code.print("return nil")
code.outdent()
code.print("}")
code.outdent()
code.print("}")
code.print()
code.print("public func jsonObject() -> Any {")
code.indent()
code.print("var result : [String:Any] = [:]")
for serviceTypeField in serviceType.fields {
if serviceTypeField.isArrayType {
code.print("var outArray : [Any] = []")
code.print("for arrayValue in self." + serviceTypeField.name + " {")
code.indent()
code.print("outArray.append(arrayValue.jsonObject())")
code.outdent()
code.print("}")
code.print("result[\"" + serviceTypeField.jsonName + "\"] = outArray")
}
if serviceTypeField.isCastableType {
code.print("result[\"" + serviceTypeField.jsonName + "\"] = self." + serviceTypeField.name)
}
if serviceTypeField.isConvertibleType {
code.print("result[\"" + serviceTypeField.jsonName + "\"] = self." + serviceTypeField.name + ".jsonObject()")
}
}
code.print("return result")
code.outdent()
code.print("}")
code.print()
}
code.print("public var description : String {")
code.indent()
code.print("return \"[" + serviceType.name + "\" + ")
for serviceTypeField in serviceType.fields {
code.print(" \" " + serviceTypeField.name + ": \" + String(describing:self." + serviceTypeField.name + ") + ")
}
code.print("\"]\"")
code.outdent()
code.print("}")
code.outdent()
code.print("}")
}
return code.content
}
}

View File

@ -0,0 +1,336 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// 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.
import Foundation
import Gnostic
extension String {
func capitalizingFirstLetter() -> String {
let first = String(characters.prefix(1)).capitalized
let other = String(characters.dropFirst())
return first + other
}
mutating func capitalizeFirstLetter() {
self = self.capitalizingFirstLetter()
}
}
class ServiceType {
var name : String = ""
var fields : [ServiceTypeField] = []
var isInterfaceType : Bool = false
}
class ServiceTypeField {
var name : String = ""
var typeName : String = ""
var isArrayType : Bool = false
var isCastableType : Bool = false
var isConvertibleType : Bool = false
var elementType : String = ""
var jsonName : String = ""
var position: String = "" // "body", "header", "formdata", "query", or "path"
var initialValue : String = ""
func setTypeForName(_ name : String, _ format : String) {
switch name {
case "integer":
if format == "int32" {
self.typeName = "Int32"
} else if format == "int64" {
self.typeName = "Int64"
} else {
self.typeName = "Int"
}
self.initialValue = "0"
self.isCastableType = true
default:
self.typeName = name.capitalizingFirstLetter()
self.initialValue = self.typeName + "()"
self.isConvertibleType = true
}
}
func setTypeForSchema(_ schema : Openapi_V2_Schema, optional : Bool = false) {
let ref = schema.ref
if ref != "" {
self.typeName = typeForRef(ref)
self.isConvertibleType = true
self.initialValue = self.typeName + "()"
}
if schema.hasType {
let types = schema.type.value
let format = schema.format
if types.count == 1 && types[0] == "string" {
self.typeName = "String"
self.isCastableType = true
self.initialValue = "\"\""
}
if types.count == 1 && types[0] == "integer" && format == "int32" {
self.typeName = "Int32"
self.isCastableType = true
self.initialValue = "0"
}
if types.count == 1 && types[0] == "array" && schema.hasItems {
// we have an array.., but of what?
let items = schema.items.schema
if items.count == 1 && items[0].ref != "" {
self.isArrayType = true
self.elementType = typeForRef(items[0].ref)
self.typeName = "[" + self.elementType + "]"
self.initialValue = "[]"
}
}
}
// this function is incomplete... so return a string representing anything that we don't handle
if self.typeName == "" {
self.typeName = "\(schema)"
}
if optional {
self.typeName += "?"
self.initialValue = "nil"
}
}
}
class ServiceMethod {
var name : String = ""
var path : String = ""
var method : String = ""
var description : String = ""
var handlerName : String = ""
var processorName : String = ""
var clientName : String = ""
var resultTypeName : String?
var parametersTypeName : String?
var responsesTypeName : String?
var parametersType : ServiceType?
var responsesType : ServiceType?
}
func propertyNameForResponseCode(_ code:String) -> String {
switch code {
case "200":
return "ok"
case "default":
return "error"
default:
return code
}
}
func typeForRef(_ ref : String) -> String {
let parts = ref.components(separatedBy:"/")
return parts.last!.capitalizingFirstLetter()
}
class ServiceRenderer {
internal var name : String = ""
internal var package: String = ""
internal var types : [ServiceType] = []
internal var methods : [ServiceMethod] = []
internal var surface : Surface_V1_Model
public init(surface : Surface_V1_Model, document : Openapi_V2_Document) {
self.surface = surface
loadService(document:document)
}
private func loadServiceTypeFromParameters(_ name:String,
_ parameters:[Openapi_V2_ParametersItem])
-> ServiceType? {
let t = ServiceType()
t.name = name.capitalizingFirstLetter() + "Parameters"
for parametersItem in parameters {
let f = ServiceTypeField()
f.typeName = "\(parametersItem)"
switch parametersItem.oneof! {
case .parameter(let parameter):
switch parameter.oneof! {
case .bodyParameter(let bodyParameter):
f.name = bodyParameter.name
if bodyParameter.hasSchema {
f.setTypeForSchema(bodyParameter.schema)
f.position = "body"
}
case .nonBodyParameter(let nonBodyParameter):
switch (nonBodyParameter.oneof!) {
case .headerParameterSubSchema(let headerParameter):
f.name = headerParameter.name
f.position = "header"
case .formDataParameterSubSchema(let formDataParameter):
f.name = formDataParameter.name
f.position = "formdata"
case .queryParameterSubSchema(let queryParameter):
f.name = queryParameter.name
f.position = "query"
case .pathParameterSubSchema(let pathParameter):
f.name = pathParameter.name
f.jsonName = pathParameter.name
f.position = "path"
f.setTypeForName(pathParameter.type, pathParameter.format)
}
}
case .jsonReference: // (let reference):
Log("?")
}
t.fields.append(f)
}
if t.fields.count > 0 {
self.types.append(t)
return t
} else {
return nil
}
}
private func loadServiceTypeFromResponses(_ m:ServiceMethod,
_ name:String,
_ responses:Openapi_V2_Responses)
-> ServiceType? {
let t = ServiceType()
t.name = name.capitalizingFirstLetter() + "Responses"
for responseCode in responses.responseCode {
let f = ServiceTypeField()
f.name = propertyNameForResponseCode(responseCode.name)
f.jsonName = ""
if let responseCodeValueOneOf = responseCode.value.oneof {
switch responseCodeValueOneOf {
case .response(let response):
let schema = response.schema
if let schemaOneOf = schema.oneof {
switch schemaOneOf {
case .schema(let schema):
f.setTypeForSchema(schema, optional:true)
t.fields.append(f)
if f.name == "ok" {
m.resultTypeName = f.typeName.replacingOccurrences(of:"?", with:"")
}
default:
break
}
}
default:
break
}
}
}
if t.fields.count > 0 {
self.types.append(t)
return t
} else {
return nil
}
}
private func loadOperation(_ operation : Openapi_V2_Operation,
method : String,
path : String) {
let m = ServiceMethod()
m.name = operation.operationID
m.path = path
m.method = method
m.description = operation.description_p
m.handlerName = "handle" + m.name
m.processorName = "" + m.name
m.clientName = m.name
m.parametersType = loadServiceTypeFromParameters(m.name, operation.parameters)
if m.parametersType != nil {
m.parametersTypeName = m.parametersType!.name
}
m.responsesType = loadServiceTypeFromResponses(m, m.name, operation.responses)
if m.responsesType != nil {
m.responsesTypeName = m.responsesType!.name
}
self.methods.append(m)
}
private func loadService(document : Openapi_V2_Document) {
// collect service type descriptions
for pair in document.definitions.additionalProperties {
let t = ServiceType()
t.isInterfaceType = true
let schema = pair.value
for pair2 in schema.properties.additionalProperties {
let f = ServiceTypeField()
f.name = pair2.name
f.setTypeForSchema(pair2.value)
f.jsonName = pair2.name
t.fields.append(f)
}
t.name = pair.name.capitalizingFirstLetter()
self.types.append(t)
}
// collect service method descriptions
for pair in document.paths.path {
let v = pair.value
if v.hasGet {
loadOperation(v.get, method:"GET", path:pair.name)
}
if v.hasPost {
loadOperation(v.post, method:"POST", path:pair.name)
}
if v.hasPut {
loadOperation(v.put, method:"PUT", path:pair.name)
}
if v.hasDelete {
loadOperation(v.delete, method:"DELETE", path:pair.name)
}
}
}
public func generate(filenames : [String], response : inout Gnostic_Plugin_V1_Response) throws {
for filename in filenames {
var data : Data?
switch filename {
case "types.swift":
data = renderTypes().data(using:.utf8)
case "server.swift":
data = renderServer().data(using:.utf8)
case "client.swift":
data = renderClient().data(using:.utf8)
case "fetch.swift":
data = renderFetch().data(using:.utf8)
default:
print("error: unable to render \(filename)")
}
if let data = data {
var clientfile = Gnostic_Plugin_V1_File()
clientfile.name = filename
clientfile.data = data
response.files.append(clientfile)
}
}
}
}
let header = """
// Copyright 2017 Google Inc. All Rights Reserved.
//
// 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.
"""

View File

@ -0,0 +1,166 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// 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.
func hasParameters(_ value : Any?) -> Bool {
let method : ServiceMethod = value as! ServiceMethod
return method.parametersType != nil
}
func hasResponses(_ value : Any?) -> Bool {
let method : ServiceMethod = value as! ServiceMethod
return method.responsesType != nil
}
func syncClientParametersDeclaration(_ value: Any?) -> String {
let method : ServiceMethod = value as! ServiceMethod
var result = ""
if let parametersType = method.parametersType {
for field in parametersType.fields {
if result != "" {
result += ", "
}
result += field.name + " : " + field.typeName
}
}
return result
}
func syncClientReturnDeclaration(_ value : Any?) -> String {
let method : ServiceMethod = value as! ServiceMethod
var result = ""
if let resultTypeName = method.resultTypeName {
result = " -> " + resultTypeName
}
return result
}
func asyncClientParametersDeclaration(_ value : Any?) -> String {
let method : ServiceMethod = value as! ServiceMethod
var result = ""
if let parametersType = method.parametersType {
for field in parametersType.fields {
if result != "" {
result += ", "
}
result += field.name + " : " + field.typeName
}
}
// add callback
if result != "" {
result += ", "
}
if let resultTypeName = method.resultTypeName {
result += "callback : @escaping (" + resultTypeName + "?, Swift.Error?)->()"
} else {
result += "callback : @escaping (Swift.Error?)->()"
}
return result
}
func protocolParametersDeclaration(_ value: Any?) -> String {
let method : ServiceMethod = value as! ServiceMethod
var result = ""
if let parametersTypeName = method.parametersTypeName {
result = "_ parameters : " + parametersTypeName
}
return result
}
func protocolReturnDeclaration(_ value: Any?) -> String {
let method : ServiceMethod = value as! ServiceMethod
var result = ""
if let responsesTypeName = method.responsesTypeName {
result = "-> " + responsesTypeName
}
return result
}
func parameterFieldNames(_ value: Any?) -> String {
let method : ServiceMethod = value as! ServiceMethod
var result = ""
if let parametersType = method.parametersType {
for field in parametersType.fields {
if result != "" {
result += ", "
}
result += field.name + ":" + field.name
}
}
return result
}
func parametersTypeFields(_ value: Any?) -> [ServiceTypeField] {
let method : ServiceMethod = value as! ServiceMethod
if let parametersType = method.parametersType {
return parametersType.fields
} else {
return []
}
}
func kituraPath(_ value: Any?) -> String {
let method : ServiceMethod = value as! ServiceMethod
var path = method.path
if let parametersType = method.parametersType {
for field in parametersType.fields {
if field.position == "path" {
let original = "{" + field.jsonName + "}"
let replacement = ":" + field.jsonName
path = path.replacingOccurrences(of:original, with:replacement)
}
}
}
return path
}
func bodyParameterFieldName(_ value: Any?) -> String {
let method : ServiceMethod = value as! ServiceMethod
if let parametersType = method.parametersType {
for field in parametersType.fields {
if field.position == "body" {
return field.name
}
}
}
return ""
}
func responsesHasFieldNamedOK(_ value: Any?) -> Bool {
let method : ServiceMethod = value as! ServiceMethod
if let responsesType = method.responsesType {
for field in responsesType.fields {
if field.name == "ok" {
return true
}
}
}
return false
}
func responsesHasFieldNamedError(_ value: Any?) -> Bool {
let method : ServiceMethod = value as! ServiceMethod
if let responsesType = method.responsesType {
for field in responsesType.fields {
if field.name == "error" {
return true
}
}
}
return false
}
func lowercase(_ s : String) -> String {
return s.lowercased()
}

View File

@ -0,0 +1,106 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// 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.
import Foundation
// The I/O code below is derived from Apple's swift-protobuf project.
// https://github.com/apple/swift-protobuf
// BEGIN swift-protobuf derivation
#if os(Linux)
import Glibc
#else
import Darwin.C
#endif
enum PluginError: Error {
/// Raised for any errors reading the input
case readFailure
}
// Alias clib's write() so Stdout.write(bytes:) can call it.
private let _write = write
class Stdin {
static func readall() throws -> Data {
let fd: Int32 = 0
let buffSize = 32
var buff = [UInt8]()
while true {
var fragment = [UInt8](repeating: 0, count: buffSize)
let count = read(fd, &fragment, buffSize)
if count < 0 {
throw PluginError.readFailure
}
if count < buffSize {
buff += fragment[0..<count]
return Data(bytes: buff)
}
buff += fragment
}
}
}
class Stdout {
static func write(bytes: Data) {
bytes.withUnsafeBytes { (p: UnsafePointer<UInt8>) -> () in
_ = _write(1, p, bytes.count)
}
}
}
struct CodePrinter {
private(set) var content = ""
private var currentIndentDepth = 0
private var currentIndent = ""
private var atLineStart = true
mutating func print() {
print("")
}
mutating func print(_ text: String...) {
for t in text {
for c in t.characters {
if c == "\n" {
content.append(c)
atLineStart = true
} else {
if atLineStart {
content.append(currentIndent)
atLineStart = false
}
content.append(c)
}
}
}
content.append("\n")
atLineStart = true
}
mutating private func resetIndent() {
currentIndent = (0..<currentIndentDepth).map { Int -> String in return " " } .joined(separator:"")
}
mutating func indent() {
currentIndentDepth += 1
resetIndent()
}
mutating func outdent() {
currentIndentDepth -= 1
resetIndent()
}
}
// END swift-protobuf derivation

View File

@ -0,0 +1,66 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// 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.
import Foundation
import Gnostic
func Log(_ message : String) {
FileHandle.standardError.write((message + "\n").data(using:.utf8)!)
}
func main() throws {
// read the code generation request
let rawRequest = try Stdin.readall()
let request = try Gnostic_Plugin_V1_Request(serializedData:rawRequest)
var response = Gnostic_Plugin_V1_Response()
var openapiv2 : Openapi_V2_Document?
var surface : Surface_V1_Model?
for model in request.models {
if model.typeURL == "openapi.v2.Document" {
openapiv2 = try Openapi_V2_Document(serializedData: model.value)
} else if model.typeURL == "surface.v1.Model" {
surface = try Surface_V1_Model(serializedData: model.value)
}
}
if let openapiv2 = openapiv2,
let surface = surface {
// build the service renderer
let renderer = ServiceRenderer(surface:surface, document:openapiv2)
// generate the desired files
var filenames : [String]
switch CommandLine.arguments[0] {
case "openapi_swift_client":
filenames = ["client.swift", "types.swift", "fetch.swift"]
case "openapi_swift_server":
filenames = ["server.swift", "types.swift"]
default:
filenames = ["client.swift", "server.swift", "types.swift", "fetch.swift"]
}
try renderer.generate(filenames:filenames, response:&response)
}
// return the results
let serializedResponse = try response.serializedData()
Stdout.write(bytes: serializedResponse)
}
try main()

View File

@ -0,0 +1,52 @@
#!/bin/sh
#
# Copyright 2017 Google Inc. All Rights Reserved.
#
# 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.
# Use this script to run protoc and swift-proto to generate
# support code for gnostic protos.
GNOSTIC=$GOPATH/src/github.com/googleapis/gnostic
PROTOS=(
plugins/plugin.proto
OpenAPIv2/OpenAPIv2.proto
OpenAPIv3/OpenAPIv3.proto
surface/surface.proto
discovery/discovery.proto
)
# remove old compiled pb files
rm -rf Sources/Gnostic/*.pb.swift
# remove any prior compilations
rm -rf Sources/Gnostic/github.com
# compile protos
for proto in "${PROTOS[@]}"
do
echo "COMPILING $proto"
protoc $GNOSTIC/$proto \
--swift_opt=Visibility=Public \
--swift_out=Sources/Gnostic \
--proto_path=$GOPATH/src
# relocate compiled protos
find Sources/Gnostic/github.com -name "*.pb.swift" -exec mv {} Sources/Gnostic \;
# remove scaffolding of compilation
rm -rf Sources/Gnostic/github.com
done

View File

@ -0,0 +1,7 @@
all:
gnostic bookstore.json --swift-generator-out=Sources/Bookstore
swift build
test:
swift test

View File

@ -0,0 +1,26 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// 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.
import PackageDescription
let package = Package(
name: "BookstoreExample",
targets: [
Target(name: "Server", dependencies: ["Bookstore"]),
],
dependencies: [
.Package(url: "https://github.com/IBM-Swift/Kitura.git", majorVersion: 1, minor: 7)
]
)

View File

@ -0,0 +1,22 @@
# Bookstore Example
This directory contains an OpenAPI description of a simple bookstore API.
Use this example to try the `gnostic-swift-generator` plugin, which
generates Swift code that implements an API client and server for
an OpenAPI description.
Run `make all` to build and install `gnostic` and the Swift plugin.
It will generate both client and server code. The API client and
server code will be in the `Sources/Bookstore` package.
The `Sources/Server` directory contains additional code that completes the server.
To build and run the server, do the following:
swift build
.build/debug/Server &
To test the service with the generated client, run `swift build`.
Tests are in the `Tests` directory and use client
code generated in `Bookstore` to verify the service.

View File

@ -0,0 +1,128 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// 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.
import Bookstore
class Server : Service {
private var shelves : [Int64:Shelf] = [:]
private var books : [Int64:[Int64:Book]] = [:]
private var lastShelfIndex : Int64 = 0
private var lastBookIndex : Int64 = 0
// Return all shelves in the bookstore.
func listShelves () throws -> ListShelvesResponses {
let responses = ListShelvesResponses()
let response = ListShelvesResponse()
var shelves : [Shelf] = []
for pair in self.shelves {
shelves.append(pair.value)
}
response.shelves = shelves
responses.ok = response
return responses
}
// Create a new shelf in the bookstore.
func createShelf (_ parameters : CreateShelfParameters) throws -> CreateShelfResponses {
lastShelfIndex += 1
let shelf = parameters.shelf
shelf.name = "shelves/\(lastShelfIndex)"
shelves[lastShelfIndex] = shelf
let responses = CreateShelfResponses()
responses.ok = shelf
return responses
}
// Delete all shelves.
func deleteShelves () throws {
shelves = [:]
books = [:]
lastShelfIndex = 0
lastBookIndex = 0
}
// Get a single shelf resource with the given ID.
func getShelf (_ parameters : GetShelfParameters) throws -> GetShelfResponses {
let responses = GetShelfResponses()
if let shelf : Shelf = shelves[parameters.shelf] {
responses.ok = shelf
} else {
let err = Error()
err.code = 404
err.message = "not found"
responses.error = err
}
return responses
}
// Delete a single shelf with the given ID.
func deleteShelf (_ parameters : DeleteShelfParameters) throws {
shelves[parameters.shelf] = nil
books[parameters.shelf] = nil
}
// Return all books in a shelf with the given ID.
func listBooks (_ parameters : ListBooksParameters) throws -> ListBooksResponses {
let responses = ListBooksResponses()
let response = ListBooksResponse()
var books : [Book] = []
if let shelfBooks = self.books[parameters.shelf] {
for pair in shelfBooks {
books.append(pair.value)
}
}
response.books = books
responses.ok = response
return responses
}
// Create a new book on the shelf.
func createBook (_ parameters : CreateBookParameters) throws -> CreateBookResponses {
let responses = CreateBookResponses()
lastBookIndex += 1
let shelf = parameters.shelf
let book = parameters.book
book.name = "shelves/\(shelf)/books/\(lastBookIndex)"
if var shelfBooks = self.books[shelf] {
shelfBooks[lastBookIndex] = book
self.books[shelf] = shelfBooks
} else {
var shelfBooks : [Int64:Book] = [:]
shelfBooks[lastBookIndex] = book
self.books[shelf] = shelfBooks
}
responses.ok = book
return responses
}
// Get a single book with a given ID from a shelf.
func getBook (_ parameters : GetBookParameters) throws -> GetBookResponses {
let responses = GetBookResponses()
if let shelfBooks = self.books[parameters.shelf],
let book = shelfBooks[parameters.book] {
responses.ok = book
} else {
let err = Error()
err.code = 404
err.message = "not found"
responses.error = err
}
return responses
}
// Delete a single book with a given ID from a shelf.
func deleteBook (_ parameters : DeleteBookParameters) throws {
if var shelfBooks = self.books[parameters.shelf] {
shelfBooks[parameters.book] = nil
self.books[parameters.shelf] = shelfBooks
}
}
}
initialize(service:Server(), port:8080)
run()

View File

@ -0,0 +1,208 @@
/*
*
* Copyright 2017, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
import XCTest
import Foundation
@testable import Bookstore
func Log(_ message : String) {
FileHandle.standardError.write((message + "\n").data(using:.utf8)!)
}
let service = "http://localhost:8080"
class BookstoreTests: XCTestCase {
func testBasic() {
// create a client
let b = Bookstore.Client(service:service)
Log("// reset the service by deleting all shelves")
do {
try b.deleteShelves()
} catch (let error) {
XCTFail("\(error)")
}
Log("// verify that the service has no shelves")
do {
let response = try b.listShelves()
XCTAssertEqual(response.shelves.count, 0)
} catch (let error) {
XCTFail("\(error)")
}
Log("// attempting to get a shelf should return an error")
do {
let _ = try b.getShelf(shelf:1)
XCTFail("server error")
} catch {
}
Log("// attempting to get a book should return an error")
do {
let _ = try b.getBook(shelf:1, book:2)
} catch {
}
Log("// add a shelf")
do {
let shelf = Shelf()
shelf.theme = "mysteries"
let response = try b.createShelf(shelf:shelf)
if (response.name != "shelves/1") ||
(response.theme != "mysteries") {
XCTFail("mismatch")
}
} catch (let error) {
XCTFail("\(error)")
}
Log("// add another shelf")
do {
let shelf = Shelf()
shelf.theme = "comedies"
let response = try b.createShelf(shelf:shelf)
if (response.name != "shelves/2") ||
(response.theme != "comedies") {
XCTFail("mismatch")
}
} catch (let error) {
XCTFail("\(error)")
}
Log("// get the first shelf that was added")
do {
let response = try b.getShelf(shelf:1)
if (response.name != "shelves/1") ||
(response.theme != "mysteries") {
XCTFail("mismatch")
}
} catch (let error) {
XCTFail("\(error)")
}
Log("// list shelves and verify that there are 2")
do {
let response = try b.listShelves()
XCTAssertEqual(response.shelves.count, 2)
} catch (let error) {
XCTFail("\(error)")
}
Log("// delete a shelf")
do {
try b.deleteShelf(shelf:2)
} catch (let error) {
XCTFail("\(error)")
}
Log("// list shelves and verify that there is only 1")
do {
let response = try b.listShelves()
XCTAssertEqual(response.shelves.count, 1)
} catch (let error) {
XCTFail("\(error)")
}
Log("// list books on a shelf, verify that there are none")
do {
let response = try b.listBooks(shelf:1)
XCTAssertEqual(response.books.count, 0)
} catch (let error) {
XCTFail("\(error)")
}
Log("// create a book")
do {
let book = Book()
book.author = "Agatha Christie"
book.title = "And Then There Were None"
let _ = try b.createBook(shelf:1, book:book)
} catch (let error) {
XCTFail("\(error)")
}
Log("// create another book")
do {
let book = Book()
book.author = "Agatha Christie"
book.title = "Murder on the Orient Express"
let _ = try b.createBook(shelf:1, book:book)
} catch (let error) {
XCTFail("\(error)")
}
Log("// get the first book that was added")
do {
let response = try b.getBook(shelf:1, book:1)
if (response.author != "Agatha Christie") ||
(response.title != "And Then There Were None") {
XCTFail("mismatch")
}
} catch (let error) {
XCTFail("\(error)")
}
Log("// list the books on a shelf and verify that there are 2")
do {
let response = try b.listBooks(shelf:1)
XCTAssertEqual(response.books.count, 2)
} catch (let error) {
XCTFail("\(error)")
}
Log("// delete a book")
do {
try b.deleteBook(shelf:1, book:2)
} catch (let error) {
XCTFail("\(error)")
}
Log("// list the books on a shelf and verify that is only 1")
do {
let response = try b.listBooks(shelf:1)
XCTAssertEqual(response.books.count, 1)
} catch (let error) {
XCTFail("\(error)")
}
Log("// verify the handling of a badly-formed request")
var path = service
path = path + "/shelves"
guard let url = URL(string:path) else {
XCTFail("Failed to construct URL")
return
}
var request = URLRequest(url:url)
request.httpMethod = "POST"
request.httpBody = "".data(using:.utf8)
let (_, response, _) = fetch(request)
// we expect a 400 (Bad Request) code
if let response = response {
XCTAssertEqual(response.statusCode, 400)
} else {
// Failed requests are returning nil responses on Linux. For now we'll say that is OK.
//XCTFail("Null response for bad request")
}
}
}
extension BookstoreTests {
static var allTests : [(String, (BookstoreTests) -> () throws -> Void)] {
return [
("testBasic", testBasic),
]
}
}

View File

@ -0,0 +1,38 @@
/*
*
* Copyright 2017, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
import XCTest
@testable import BookstoreTests
XCTMain([
testCase(BookstoreTests.allTests),
])

View File

@ -0,0 +1,357 @@
{
"swagger": "2.0",
"info": {
"description": "A simple Bookstore API example.",
"title": "Bookstore",
"version": "1.0.0"
},
"host": "generated-bookstore.appspot.com",
"basePath": "/",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"schemes": [
"https"
],
"paths": {
"/shelves": {
"get": {
"description": "Return all shelves in the bookstore.",
"operationId": "listShelves",
"produces": [
"application/json"
],
"responses": {
"200": {
"description": "List of shelves in the bookstore.",
"schema": {
"$ref": "#/definitions/listShelvesResponse"
}
}
},
"security": [
]
},
"post": {
"description": "Create a new shelf in the bookstore.",
"operationId": "createShelf",
"parameters": [
{
"description": "A shelf resource to create.",
"in": "body",
"name": "shelf",
"required": true,
"schema": {
"$ref": "#/definitions/shelf"
}
}
],
"produces": [
"application/json"
],
"responses": {
"200": {
"description": "A newly created shelf resource.",
"schema": {
"$ref": "#/definitions/shelf"
}
}
}
},
"delete": {
"description": "Delete all shelves.",
"operationId": "deleteShelves",
"responses": {
"default": {
"description": "An empty response body."
}
}
}
},
"/shelves/{shelf}": {
"get": {
"description": "Get a single shelf resource with the given ID.",
"operationId": "getShelf",
"parameters": [
{
"description": "ID of the shelf to get.",
"format": "int64",
"in": "path",
"name": "shelf",
"required": true,
"type": "integer"
}
],
"produces": [
"application/json"
],
"responses": {
"200": {
"description": "A shelf resource.",
"schema": {
"$ref": "#/definitions/shelf"
}
},
"default": {
"description": "unexpected error",
"schema": {
"$ref": "#/definitions/Error"
}
}
}
},
"delete": {
"description": "Delete a single shelf with the given ID.",
"operationId": "deleteShelf",
"parameters": [
{
"description": "ID of the shelf to delete.",
"format": "int64",
"in": "path",
"name": "shelf",
"required": true,
"type": "integer"
}
],
"responses": {
"default": {
"description": "An empty response body."
}
}
}
},
"/shelves/{shelf}/books": {
"get": {
"description": "Return all books in a shelf with the given ID.",
"operationId": "listBooks",
"parameters": [
{
"description": "ID of the shelf whose books should be returned.",
"format": "int64",
"in": "path",
"name": "shelf",
"required": true,
"type": "integer"
}
],
"produces": [
"application/json"
],
"responses": {
"200": {
"description": "List of books on the specified shelf.",
"schema": {
"$ref": "#/definitions/listBooksResponse"
}
},
"default": {
"description": "unexpected error",
"schema": {
"$ref": "#/definitions/Error"
}
}
}
},
"post": {
"description": "Create a new book on the shelf.",
"operationId": "createBook",
"parameters": [
{
"description": "ID of the shelf where the book should be created.",
"format": "int64",
"in": "path",
"name": "shelf",
"required": true,
"type": "integer"
},
{
"description": "Book to create.",
"in": "body",
"name": "book",
"required": true,
"schema": {
"$ref": "#/definitions/book"
}
}
],
"produces": [
"application/json"
],
"responses": {
"200": {
"description": "A newly created book resource.",
"schema": {
"$ref": "#/definitions/book"
}
},
"default": {
"description": "unexpected error",
"schema": {
"$ref": "#/definitions/Error"
}
}
}
}
},
"/shelves/{shelf}/books/{book}": {
"get": {
"description": "Get a single book with a given ID from a shelf.",
"operationId": "getBook",
"parameters": [
{
"description": "ID of the shelf from which to get the book.",
"format": "int64",
"in": "path",
"name": "shelf",
"required": true,
"type": "integer"
},
{
"description": "ID of the book to get from the shelf.",
"format": "int64",
"in": "path",
"name": "book",
"required": true,
"type": "integer"
}
],
"produces": [
"application/json"
],
"responses": {
"200": {
"description": "A book resource.",
"schema": {
"$ref": "#/definitions/book"
}
},
"default": {
"description": "unexpected error",
"schema": {
"$ref": "#/definitions/Error"
}
}
}
},
"delete": {
"description": "Delete a single book with a given ID from a shelf.",
"operationId": "deleteBook",
"parameters": [
{
"description": "ID of the shelf from which to delete the book.",
"format": "int64",
"in": "path",
"name": "shelf",
"required": true,
"type": "integer"
},
{
"description": "ID of the book to delete from the shelf.",
"format": "int64",
"in": "path",
"name": "book",
"required": true,
"type": "integer"
}
],
"responses": {
"default": {
"description": "An empty response body."
}
}
}
}
},
"definitions": {
"book": {
"properties": {
"author": {
"type": "string"
},
"name": {
"type": "string"
},
"title": {
"type": "string"
}
},
"required": [
"name",
"author",
"title"
]
},
"listBooksResponse": {
"properties": {
"books": {
"items": {
"$ref": "#/definitions/book"
},
"type": "array"
}
},
"required": [
"books"
],
"type": "object"
},
"listShelvesResponse": {
"properties": {
"shelves": {
"items": {
"$ref": "#/definitions/shelf"
},
"type": "array"
}
},
"type": "object"
},
"shelf": {
"properties": {
"name": {
"type": "string"
},
"theme": {
"type": "string"
}
},
"required": [
"name",
"theme"
]
},
"error": {
"required": [
"code",
"message"
],
"properties": {
"code": {
"type": "integer",
"format": "int32"
},
"message": {
"type": "string"
}
}
}
},
"security": [
{
"api_key": [
]
}
],
"securityDefinitions": {
"api_key": {
"in": "query",
"name": "key",
"type": "apiKey"
}
}
}

View File

@ -0,0 +1,15 @@
TGT=gnostic-swift-sample
BINDIR=.build/debug
all:
swift build
install: all
cp $(BINDIR)/$(TGT) $(GOPATH)/bin/$(TGT)
clean :
rm -rf Packages
rm -rf .build
rm -f Package.pins Package.resolved

View File

@ -0,0 +1,26 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// 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.
import PackageDescription
let package = Package(
name: "gnostic-swift-sample",
targets: [
Target(name: "gnostic-swift-sample", dependencies: [ "Gnostic" ]),
Target(name: "Gnostic")
],
dependencies: [
.Package(url: "https://github.com/apple/swift-protobuf.git", Version(0,9,904))
]
)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,441 @@
// DO NOT EDIT.
//
// Generated by the Swift generator plugin for the protocol buffer compiler.
// Source: github.com/googleapis/gnostic/plugins/plugin.proto
//
// For information on using the generated types, please see the documenation:
// https://github.com/apple/swift-protobuf/
// Copyright 2017 Google Inc. All Rights Reserved.
//
// 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.
// gnostic can be extended with plugins.
// A plugin is uset a program that reads a Request from stdin
// and writes a Response to stdout.
//
// A plugin executable needs only to be placed somewhere in the path. The
// plugin should be named "gnostic_$NAME", and will then be used when the
// flag "--${NAME}_out" is passed to gnostic.
import Foundation
import SwiftProtobuf
// If the compiler emits an error on this type, it is because this file
// was generated by a version of the `protoc` Swift plug-in that is
// incompatible with the version of SwiftProtobuf to which you are linking.
// Please ensure that your are building against the same version of the API
// that was used to generate this file.
fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {}
typealias Version = _2
}
/// The version number of gnostic.
public struct Gnostic_Plugin_V1_Version: SwiftProtobuf.Message {
public static let protoMessageName: String = _protobuf_package + ".Version"
public var major: Int32 = 0
public var minor: Int32 = 0
public var patch: Int32 = 0
/// A suffix for alpha, beta or rc release, e.g., "alpha-1", "rc2". It should
/// be empty for mainline stable releases.
public var suffix: String = String()
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
/// Used by the decoding initializers in the SwiftProtobuf library, not generally
/// used directly. `init(serializedData:)`, `init(jsonUTF8Data:)`, and other decoding
/// initializers are defined in the SwiftProtobuf library. See the Message and
/// Message+*Additions` files.
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularInt32Field(value: &self.major)
case 2: try decoder.decodeSingularInt32Field(value: &self.minor)
case 3: try decoder.decodeSingularInt32Field(value: &self.patch)
case 4: try decoder.decodeSingularStringField(value: &self.suffix)
default: break
}
}
}
/// Used by the encoding methods of the SwiftProtobuf library, not generally
/// used directly. `Message.serializedData()`, `Message.jsonUTF8Data()`, and
/// other serializer methods are defined in the SwiftProtobuf library. See the
/// `Message` and `Message+*Additions` files.
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if self.major != 0 {
try visitor.visitSingularInt32Field(value: self.major, fieldNumber: 1)
}
if self.minor != 0 {
try visitor.visitSingularInt32Field(value: self.minor, fieldNumber: 2)
}
if self.patch != 0 {
try visitor.visitSingularInt32Field(value: self.patch, fieldNumber: 3)
}
if !self.suffix.isEmpty {
try visitor.visitSingularStringField(value: self.suffix, fieldNumber: 4)
}
try unknownFields.traverse(visitor: &visitor)
}
}
/// A parameter passed to the plugin from (or through) gnostic.
public struct Gnostic_Plugin_V1_Parameter: SwiftProtobuf.Message {
public static let protoMessageName: String = _protobuf_package + ".Parameter"
/// The name of the parameter as specified in the option string
public var name: String = String()
/// The parameter value as specified in the option string
public var value: String = String()
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
/// Used by the decoding initializers in the SwiftProtobuf library, not generally
/// used directly. `init(serializedData:)`, `init(jsonUTF8Data:)`, and other decoding
/// initializers are defined in the SwiftProtobuf library. See the Message and
/// Message+*Additions` files.
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularStringField(value: &self.name)
case 2: try decoder.decodeSingularStringField(value: &self.value)
default: break
}
}
}
/// Used by the encoding methods of the SwiftProtobuf library, not generally
/// used directly. `Message.serializedData()`, `Message.jsonUTF8Data()`, and
/// other serializer methods are defined in the SwiftProtobuf library. See the
/// `Message` and `Message+*Additions` files.
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.name.isEmpty {
try visitor.visitSingularStringField(value: self.name, fieldNumber: 1)
}
if !self.value.isEmpty {
try visitor.visitSingularStringField(value: self.value, fieldNumber: 2)
}
try unknownFields.traverse(visitor: &visitor)
}
}
/// An encoded Request is written to the plugin's stdin.
public struct Gnostic_Plugin_V1_Request: SwiftProtobuf.Message {
public static let protoMessageName: String = _protobuf_package + ".Request"
/// filename or URL of the original source document
public var sourceName: String {
get {return _storage._sourceName}
set {_uniqueStorage()._sourceName = newValue}
}
/// Output path specified in the plugin invocation.
public var outputPath: String {
get {return _storage._outputPath}
set {_uniqueStorage()._outputPath = newValue}
}
/// Plugin parameters parsed from the invocation string.
public var parameters: [Gnostic_Plugin_V1_Parameter] {
get {return _storage._parameters}
set {_uniqueStorage()._parameters = newValue}
}
/// The version number of gnostic.
public var compilerVersion: Gnostic_Plugin_V1_Version {
get {return _storage._compilerVersion ?? Gnostic_Plugin_V1_Version()}
set {_uniqueStorage()._compilerVersion = newValue}
}
/// Returns true if `compilerVersion` has been explicitly set.
public var hasCompilerVersion: Bool {return _storage._compilerVersion != nil}
/// Clears the value of `compilerVersion`. Subsequent reads from it will return its default value.
public mutating func clearCompilerVersion() {_storage._compilerVersion = nil}
/// API models
public var models: [SwiftProtobuf.Google_Protobuf_Any] {
get {return _storage._models}
set {_uniqueStorage()._models = newValue}
}
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
/// Used by the decoding initializers in the SwiftProtobuf library, not generally
/// used directly. `init(serializedData:)`, `init(jsonUTF8Data:)`, and other decoding
/// initializers are defined in the SwiftProtobuf library. See the Message and
/// Message+*Additions` files.
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
_ = _uniqueStorage()
try withExtendedLifetime(_storage) { (_storage: _StorageClass) in
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularStringField(value: &_storage._sourceName)
case 2: try decoder.decodeSingularStringField(value: &_storage._outputPath)
case 3: try decoder.decodeRepeatedMessageField(value: &_storage._parameters)
case 4: try decoder.decodeSingularMessageField(value: &_storage._compilerVersion)
case 5: try decoder.decodeRepeatedMessageField(value: &_storage._models)
default: break
}
}
}
}
/// Used by the encoding methods of the SwiftProtobuf library, not generally
/// used directly. `Message.serializedData()`, `Message.jsonUTF8Data()`, and
/// other serializer methods are defined in the SwiftProtobuf library. See the
/// `Message` and `Message+*Additions` files.
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
try withExtendedLifetime(_storage) { (_storage: _StorageClass) in
if !_storage._sourceName.isEmpty {
try visitor.visitSingularStringField(value: _storage._sourceName, fieldNumber: 1)
}
if !_storage._outputPath.isEmpty {
try visitor.visitSingularStringField(value: _storage._outputPath, fieldNumber: 2)
}
if !_storage._parameters.isEmpty {
try visitor.visitRepeatedMessageField(value: _storage._parameters, fieldNumber: 3)
}
if let v = _storage._compilerVersion {
try visitor.visitSingularMessageField(value: v, fieldNumber: 4)
}
if !_storage._models.isEmpty {
try visitor.visitRepeatedMessageField(value: _storage._models, fieldNumber: 5)
}
}
try unknownFields.traverse(visitor: &visitor)
}
fileprivate var _storage = _StorageClass.defaultInstance
}
/// The plugin writes an encoded Response to stdout.
public struct Gnostic_Plugin_V1_Response: SwiftProtobuf.Message {
public static let protoMessageName: String = _protobuf_package + ".Response"
/// Error message. If non-empty, the plugin failed.
/// The plugin process should exit with status code zero
/// even if it reports an error in this way.
///
/// This should be used to indicate errors which prevent the plugin from
/// operating as intended. Errors which indicate a problem in gnostic
/// 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.
public var errors: [String] = []
/// file output, each file will be written by gnostic to an appropriate location.
public var files: [Gnostic_Plugin_V1_File] = []
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
/// Used by the decoding initializers in the SwiftProtobuf library, not generally
/// used directly. `init(serializedData:)`, `init(jsonUTF8Data:)`, and other decoding
/// initializers are defined in the SwiftProtobuf library. See the Message and
/// Message+*Additions` files.
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeRepeatedStringField(value: &self.errors)
case 2: try decoder.decodeRepeatedMessageField(value: &self.files)
default: break
}
}
}
/// Used by the encoding methods of the SwiftProtobuf library, not generally
/// used directly. `Message.serializedData()`, `Message.jsonUTF8Data()`, and
/// other serializer methods are defined in the SwiftProtobuf library. See the
/// `Message` and `Message+*Additions` files.
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.errors.isEmpty {
try visitor.visitRepeatedStringField(value: self.errors, fieldNumber: 1)
}
if !self.files.isEmpty {
try visitor.visitRepeatedMessageField(value: self.files, fieldNumber: 2)
}
try unknownFields.traverse(visitor: &visitor)
}
}
/// File describes a file generated by a plugin.
public struct Gnostic_Plugin_V1_File: SwiftProtobuf.Message {
public static let protoMessageName: String = _protobuf_package + ".File"
/// name of the file
public var name: String = String()
/// data to be written to the file
public var data: Data = SwiftProtobuf.Internal.emptyData
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
/// Used by the decoding initializers in the SwiftProtobuf library, not generally
/// used directly. `init(serializedData:)`, `init(jsonUTF8Data:)`, and other decoding
/// initializers are defined in the SwiftProtobuf library. See the Message and
/// Message+*Additions` files.
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularStringField(value: &self.name)
case 2: try decoder.decodeSingularBytesField(value: &self.data)
default: break
}
}
}
/// Used by the encoding methods of the SwiftProtobuf library, not generally
/// used directly. `Message.serializedData()`, `Message.jsonUTF8Data()`, and
/// other serializer methods are defined in the SwiftProtobuf library. See the
/// `Message` and `Message+*Additions` files.
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.name.isEmpty {
try visitor.visitSingularStringField(value: self.name, fieldNumber: 1)
}
if !self.data.isEmpty {
try visitor.visitSingularBytesField(value: self.data, fieldNumber: 2)
}
try unknownFields.traverse(visitor: &visitor)
}
}
// MARK: - Code below here is support for the SwiftProtobuf runtime.
fileprivate let _protobuf_package = "gnostic.plugin.v1"
extension Gnostic_Plugin_V1_Version: SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "major"),
2: .same(proto: "minor"),
3: .same(proto: "patch"),
4: .same(proto: "suffix"),
]
public func _protobuf_generated_isEqualTo(other: Gnostic_Plugin_V1_Version) -> Bool {
if self.major != other.major {return false}
if self.minor != other.minor {return false}
if self.patch != other.patch {return false}
if self.suffix != other.suffix {return false}
if unknownFields != other.unknownFields {return false}
return true
}
}
extension Gnostic_Plugin_V1_Parameter: SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "name"),
2: .same(proto: "value"),
]
public func _protobuf_generated_isEqualTo(other: Gnostic_Plugin_V1_Parameter) -> Bool {
if self.name != other.name {return false}
if self.value != other.value {return false}
if unknownFields != other.unknownFields {return false}
return true
}
}
extension Gnostic_Plugin_V1_Request: SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .standard(proto: "source_name"),
2: .standard(proto: "output_path"),
3: .same(proto: "parameters"),
4: .standard(proto: "compiler_version"),
5: .same(proto: "models"),
]
fileprivate class _StorageClass {
var _sourceName: String = String()
var _outputPath: String = String()
var _parameters: [Gnostic_Plugin_V1_Parameter] = []
var _compilerVersion: Gnostic_Plugin_V1_Version? = nil
var _models: [SwiftProtobuf.Google_Protobuf_Any] = []
static let defaultInstance = _StorageClass()
private init() {}
init(copying source: _StorageClass) {
_sourceName = source._sourceName
_outputPath = source._outputPath
_parameters = source._parameters
_compilerVersion = source._compilerVersion
_models = source._models
}
}
fileprivate mutating func _uniqueStorage() -> _StorageClass {
if !isKnownUniquelyReferenced(&_storage) {
_storage = _StorageClass(copying: _storage)
}
return _storage
}
public func _protobuf_generated_isEqualTo(other: Gnostic_Plugin_V1_Request) -> Bool {
if _storage !== other._storage {
let storagesAreEqual: Bool = withExtendedLifetime((_storage, other._storage)) { (_storage, other_storage) in
if _storage._sourceName != other_storage._sourceName {return false}
if _storage._outputPath != other_storage._outputPath {return false}
if _storage._parameters != other_storage._parameters {return false}
if _storage._compilerVersion != other_storage._compilerVersion {return false}
if _storage._models != other_storage._models {return false}
return true
}
if !storagesAreEqual {return false}
}
if unknownFields != other.unknownFields {return false}
return true
}
}
extension Gnostic_Plugin_V1_Response: SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "errors"),
2: .same(proto: "files"),
]
public func _protobuf_generated_isEqualTo(other: Gnostic_Plugin_V1_Response) -> Bool {
if self.errors != other.errors {return false}
if self.files != other.files {return false}
if unknownFields != other.unknownFields {return false}
return true
}
}
extension Gnostic_Plugin_V1_File: SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "name"),
2: .same(proto: "data"),
]
public func _protobuf_generated_isEqualTo(other: Gnostic_Plugin_V1_File) -> Bool {
if self.name != other.name {return false}
if self.data != other.data {return false}
if unknownFields != other.unknownFields {return false}
return true
}
}

View File

@ -0,0 +1,579 @@
// DO NOT EDIT.
//
// Generated by the Swift generator plugin for the protocol buffer compiler.
// Source: github.com/googleapis/gnostic/surface/surface.proto
//
// For information on using the generated types, please see the documenation:
// https://github.com/apple/swift-protobuf/
// Copyright 2017 Google Inc. All Rights Reserved.
//
// 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.
// Model an API surface for code generation.
import Foundation
import SwiftProtobuf
// If the compiler emits an error on this type, it is because this file
// was generated by a version of the `protoc` Swift plug-in that is
// incompatible with the version of SwiftProtobuf to which you are linking.
// Please ensure that your are building against the same version of the API
// that was used to generate this file.
fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {}
typealias Version = _2
}
public enum Surface_V1_FieldKind: SwiftProtobuf.Enum {
public typealias RawValue = Int
case scalar // = 0
case map // = 1
case array // = 2
case reference // = 3
case UNRECOGNIZED(Int)
public init() {
self = .scalar
}
public init?(rawValue: Int) {
switch rawValue {
case 0: self = .scalar
case 1: self = .map
case 2: self = .array
case 3: self = .reference
default: self = .UNRECOGNIZED(rawValue)
}
}
public var rawValue: Int {
switch self {
case .scalar: return 0
case .map: return 1
case .array: return 2
case .reference: return 3
case .UNRECOGNIZED(let i): return i
}
}
}
public enum Surface_V1_TypeKind: SwiftProtobuf.Enum {
public typealias RawValue = Int
/// implement with named fields
case `struct` // = 0
/// implement with a map
case object // = 1
case UNRECOGNIZED(Int)
public init() {
self = .struct
}
public init?(rawValue: Int) {
switch rawValue {
case 0: self = .struct
case 1: self = .object
default: self = .UNRECOGNIZED(rawValue)
}
}
public var rawValue: Int {
switch self {
case .struct: return 0
case .object: return 1
case .UNRECOGNIZED(let i): return i
}
}
}
public enum Surface_V1_Position: SwiftProtobuf.Enum {
public typealias RawValue = Int
case body // = 0
case header // = 1
case formdata // = 2
case query // = 3
case path // = 4
case UNRECOGNIZED(Int)
public init() {
self = .body
}
public init?(rawValue: Int) {
switch rawValue {
case 0: self = .body
case 1: self = .header
case 2: self = .formdata
case 3: self = .query
case 4: self = .path
default: self = .UNRECOGNIZED(rawValue)
}
}
public var rawValue: Int {
switch self {
case .body: return 0
case .header: return 1
case .formdata: return 2
case .query: return 3
case .path: return 4
case .UNRECOGNIZED(let i): return i
}
}
}
/// Field is a field in a definition and can be associated with
/// a position in a request structure.
public struct Surface_V1_Field: SwiftProtobuf.Message {
public static let protoMessageName: String = _protobuf_package + ".Field"
/// the name as specified in the API description
public var name: String = String()
/// the specified content type of the field
public var type: String = String()
/// what kind of thing is this field? scalar, reference, array, map of strings to the specified type
public var kind: Surface_V1_FieldKind = .scalar
/// the specified format of the field
public var format: String = String()
/// "body", "header", "formdata", "query", or "path"
public var position: Surface_V1_Position = .body
/// the programming-language native type of the field
public var nativeType: String = String()
/// the name to use for a data structure field
public var fieldName: String = String()
/// the name to use for a function parameter
public var parameterName: String = String()
/// true if this field should be serialized (to JSON, etc)
public var serialize: Bool = false
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
/// Used by the decoding initializers in the SwiftProtobuf library, not generally
/// used directly. `init(serializedData:)`, `init(jsonUTF8Data:)`, and other decoding
/// initializers are defined in the SwiftProtobuf library. See the Message and
/// Message+*Additions` files.
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularStringField(value: &self.name)
case 2: try decoder.decodeSingularStringField(value: &self.type)
case 3: try decoder.decodeSingularEnumField(value: &self.kind)
case 4: try decoder.decodeSingularStringField(value: &self.format)
case 5: try decoder.decodeSingularEnumField(value: &self.position)
case 6: try decoder.decodeSingularStringField(value: &self.nativeType)
case 7: try decoder.decodeSingularStringField(value: &self.fieldName)
case 8: try decoder.decodeSingularStringField(value: &self.parameterName)
case 9: try decoder.decodeSingularBoolField(value: &self.serialize)
default: break
}
}
}
/// Used by the encoding methods of the SwiftProtobuf library, not generally
/// used directly. `Message.serializedData()`, `Message.jsonUTF8Data()`, and
/// other serializer methods are defined in the SwiftProtobuf library. See the
/// `Message` and `Message+*Additions` files.
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.name.isEmpty {
try visitor.visitSingularStringField(value: self.name, fieldNumber: 1)
}
if !self.type.isEmpty {
try visitor.visitSingularStringField(value: self.type, fieldNumber: 2)
}
if self.kind != .scalar {
try visitor.visitSingularEnumField(value: self.kind, fieldNumber: 3)
}
if !self.format.isEmpty {
try visitor.visitSingularStringField(value: self.format, fieldNumber: 4)
}
if self.position != .body {
try visitor.visitSingularEnumField(value: self.position, fieldNumber: 5)
}
if !self.nativeType.isEmpty {
try visitor.visitSingularStringField(value: self.nativeType, fieldNumber: 6)
}
if !self.fieldName.isEmpty {
try visitor.visitSingularStringField(value: self.fieldName, fieldNumber: 7)
}
if !self.parameterName.isEmpty {
try visitor.visitSingularStringField(value: self.parameterName, fieldNumber: 8)
}
if self.serialize != false {
try visitor.visitSingularBoolField(value: self.serialize, fieldNumber: 9)
}
try unknownFields.traverse(visitor: &visitor)
}
}
/// Type typically corresponds to a definition, parameter, or response
/// in an API and is represented by a type in generated code.
public struct Surface_V1_Type: SwiftProtobuf.Message {
public static let protoMessageName: String = _protobuf_package + ".Type"
/// the name to use for the type
public var name: String = String()
/// a meta-description of the type (struct, map, etc)
public var kind: Surface_V1_TypeKind = .struct
/// a comment describing the type
public var description_p: String = String()
/// if the type is a map, this is its content type
public var contentType: String = String()
/// the fields of the type
public var fields: [Surface_V1_Field] = []
/// language-specific type name
public var typeName: String = String()
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
/// Used by the decoding initializers in the SwiftProtobuf library, not generally
/// used directly. `init(serializedData:)`, `init(jsonUTF8Data:)`, and other decoding
/// initializers are defined in the SwiftProtobuf library. See the Message and
/// Message+*Additions` files.
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularStringField(value: &self.name)
case 2: try decoder.decodeSingularEnumField(value: &self.kind)
case 3: try decoder.decodeSingularStringField(value: &self.description_p)
case 4: try decoder.decodeSingularStringField(value: &self.contentType)
case 5: try decoder.decodeRepeatedMessageField(value: &self.fields)
case 6: try decoder.decodeSingularStringField(value: &self.typeName)
default: break
}
}
}
/// Used by the encoding methods of the SwiftProtobuf library, not generally
/// used directly. `Message.serializedData()`, `Message.jsonUTF8Data()`, and
/// other serializer methods are defined in the SwiftProtobuf library. See the
/// `Message` and `Message+*Additions` files.
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.name.isEmpty {
try visitor.visitSingularStringField(value: self.name, fieldNumber: 1)
}
if self.kind != .struct {
try visitor.visitSingularEnumField(value: self.kind, fieldNumber: 2)
}
if !self.description_p.isEmpty {
try visitor.visitSingularStringField(value: self.description_p, fieldNumber: 3)
}
if !self.contentType.isEmpty {
try visitor.visitSingularStringField(value: self.contentType, fieldNumber: 4)
}
if !self.fields.isEmpty {
try visitor.visitRepeatedMessageField(value: self.fields, fieldNumber: 5)
}
if !self.typeName.isEmpty {
try visitor.visitSingularStringField(value: self.typeName, fieldNumber: 6)
}
try unknownFields.traverse(visitor: &visitor)
}
}
/// Method is an operation of an API and typically has associated client and server code.
public struct Surface_V1_Method: SwiftProtobuf.Message {
public static let protoMessageName: String = _protobuf_package + ".Method"
/// Operation ID
public var operation: String = String()
/// HTTP path
public var path: String = String()
/// HTTP method name
public var method: String = String()
/// description of method
public var description_p: String = String()
/// Operation name, possibly generated from method and path
public var name: String = String()
/// name of the generated handler
public var handlerName: String = String()
/// name of the processing function in the service interface
public var processorName: String = String()
/// name of client
public var clientName: String = String()
/// parameters (input), with fields corresponding to input parameters
public var parametersTypeName: String = String()
/// responses (output), with fields corresponding to possible response values
public var responsesTypeName: String = String()
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
/// Used by the decoding initializers in the SwiftProtobuf library, not generally
/// used directly. `init(serializedData:)`, `init(jsonUTF8Data:)`, and other decoding
/// initializers are defined in the SwiftProtobuf library. See the Message and
/// Message+*Additions` files.
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularStringField(value: &self.operation)
case 2: try decoder.decodeSingularStringField(value: &self.path)
case 3: try decoder.decodeSingularStringField(value: &self.method)
case 4: try decoder.decodeSingularStringField(value: &self.description_p)
case 5: try decoder.decodeSingularStringField(value: &self.name)
case 6: try decoder.decodeSingularStringField(value: &self.handlerName)
case 7: try decoder.decodeSingularStringField(value: &self.processorName)
case 8: try decoder.decodeSingularStringField(value: &self.clientName)
case 9: try decoder.decodeSingularStringField(value: &self.parametersTypeName)
case 10: try decoder.decodeSingularStringField(value: &self.responsesTypeName)
default: break
}
}
}
/// Used by the encoding methods of the SwiftProtobuf library, not generally
/// used directly. `Message.serializedData()`, `Message.jsonUTF8Data()`, and
/// other serializer methods are defined in the SwiftProtobuf library. See the
/// `Message` and `Message+*Additions` files.
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.operation.isEmpty {
try visitor.visitSingularStringField(value: self.operation, fieldNumber: 1)
}
if !self.path.isEmpty {
try visitor.visitSingularStringField(value: self.path, fieldNumber: 2)
}
if !self.method.isEmpty {
try visitor.visitSingularStringField(value: self.method, fieldNumber: 3)
}
if !self.description_p.isEmpty {
try visitor.visitSingularStringField(value: self.description_p, fieldNumber: 4)
}
if !self.name.isEmpty {
try visitor.visitSingularStringField(value: self.name, fieldNumber: 5)
}
if !self.handlerName.isEmpty {
try visitor.visitSingularStringField(value: self.handlerName, fieldNumber: 6)
}
if !self.processorName.isEmpty {
try visitor.visitSingularStringField(value: self.processorName, fieldNumber: 7)
}
if !self.clientName.isEmpty {
try visitor.visitSingularStringField(value: self.clientName, fieldNumber: 8)
}
if !self.parametersTypeName.isEmpty {
try visitor.visitSingularStringField(value: self.parametersTypeName, fieldNumber: 9)
}
if !self.responsesTypeName.isEmpty {
try visitor.visitSingularStringField(value: self.responsesTypeName, fieldNumber: 10)
}
try unknownFields.traverse(visitor: &visitor)
}
}
/// Model represents an API for code generation.
public struct Surface_V1_Model: SwiftProtobuf.Message {
public static let protoMessageName: String = _protobuf_package + ".Model"
/// a free-form title for the API
public var name: String = String()
/// the types used by the API
public var types: [Surface_V1_Type] = []
/// the methods (functions) of the API
public var methods: [Surface_V1_Method] = []
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
/// Used by the decoding initializers in the SwiftProtobuf library, not generally
/// used directly. `init(serializedData:)`, `init(jsonUTF8Data:)`, and other decoding
/// initializers are defined in the SwiftProtobuf library. See the Message and
/// Message+*Additions` files.
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularStringField(value: &self.name)
case 2: try decoder.decodeRepeatedMessageField(value: &self.types)
case 3: try decoder.decodeRepeatedMessageField(value: &self.methods)
default: break
}
}
}
/// Used by the encoding methods of the SwiftProtobuf library, not generally
/// used directly. `Message.serializedData()`, `Message.jsonUTF8Data()`, and
/// other serializer methods are defined in the SwiftProtobuf library. See the
/// `Message` and `Message+*Additions` files.
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.name.isEmpty {
try visitor.visitSingularStringField(value: self.name, fieldNumber: 1)
}
if !self.types.isEmpty {
try visitor.visitRepeatedMessageField(value: self.types, fieldNumber: 2)
}
if !self.methods.isEmpty {
try visitor.visitRepeatedMessageField(value: self.methods, fieldNumber: 3)
}
try unknownFields.traverse(visitor: &visitor)
}
}
// MARK: - Code below here is support for the SwiftProtobuf runtime.
fileprivate let _protobuf_package = "surface.v1"
extension Surface_V1_FieldKind: SwiftProtobuf._ProtoNameProviding {
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
0: .same(proto: "SCALAR"),
1: .same(proto: "MAP"),
2: .same(proto: "ARRAY"),
3: .same(proto: "REFERENCE"),
]
}
extension Surface_V1_TypeKind: SwiftProtobuf._ProtoNameProviding {
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
0: .same(proto: "STRUCT"),
1: .same(proto: "OBJECT"),
]
}
extension Surface_V1_Position: SwiftProtobuf._ProtoNameProviding {
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
0: .same(proto: "BODY"),
1: .same(proto: "HEADER"),
2: .same(proto: "FORMDATA"),
3: .same(proto: "QUERY"),
4: .same(proto: "PATH"),
]
}
extension Surface_V1_Field: SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "name"),
2: .same(proto: "type"),
3: .same(proto: "kind"),
4: .same(proto: "format"),
5: .same(proto: "position"),
6: .same(proto: "nativeType"),
7: .same(proto: "fieldName"),
8: .same(proto: "parameterName"),
9: .same(proto: "serialize"),
]
public func _protobuf_generated_isEqualTo(other: Surface_V1_Field) -> Bool {
if self.name != other.name {return false}
if self.type != other.type {return false}
if self.kind != other.kind {return false}
if self.format != other.format {return false}
if self.position != other.position {return false}
if self.nativeType != other.nativeType {return false}
if self.fieldName != other.fieldName {return false}
if self.parameterName != other.parameterName {return false}
if self.serialize != other.serialize {return false}
if unknownFields != other.unknownFields {return false}
return true
}
}
extension Surface_V1_Type: SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "name"),
2: .same(proto: "kind"),
3: .same(proto: "description"),
4: .same(proto: "contentType"),
5: .same(proto: "fields"),
6: .same(proto: "typeName"),
]
public func _protobuf_generated_isEqualTo(other: Surface_V1_Type) -> Bool {
if self.name != other.name {return false}
if self.kind != other.kind {return false}
if self.description_p != other.description_p {return false}
if self.contentType != other.contentType {return false}
if self.fields != other.fields {return false}
if self.typeName != other.typeName {return false}
if unknownFields != other.unknownFields {return false}
return true
}
}
extension Surface_V1_Method: SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "operation"),
2: .same(proto: "path"),
3: .same(proto: "method"),
4: .same(proto: "description"),
5: .same(proto: "name"),
6: .same(proto: "handlerName"),
7: .same(proto: "processorName"),
8: .same(proto: "clientName"),
9: .same(proto: "parametersTypeName"),
10: .same(proto: "responsesTypeName"),
]
public func _protobuf_generated_isEqualTo(other: Surface_V1_Method) -> Bool {
if self.operation != other.operation {return false}
if self.path != other.path {return false}
if self.method != other.method {return false}
if self.description_p != other.description_p {return false}
if self.name != other.name {return false}
if self.handlerName != other.handlerName {return false}
if self.processorName != other.processorName {return false}
if self.clientName != other.clientName {return false}
if self.parametersTypeName != other.parametersTypeName {return false}
if self.responsesTypeName != other.responsesTypeName {return false}
if unknownFields != other.unknownFields {return false}
return true
}
}
extension Surface_V1_Model: SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "name"),
2: .same(proto: "types"),
3: .same(proto: "methods"),
]
public func _protobuf_generated_isEqualTo(other: Surface_V1_Model) -> Bool {
if self.name != other.name {return false}
if self.types != other.types {return false}
if self.methods != other.methods {return false}
if unknownFields != other.unknownFields {return false}
return true
}
}

View File

@ -0,0 +1,100 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// 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.
import Foundation
// The I/O code below is derived from Apple's swift-protobuf project.
// https://github.com/apple/swift-protobuf
// BEGIN swift-protobuf derivation
#if os(Linux)
import Glibc
#else
import Darwin.C
#endif
enum PluginError: Error {
/// Raised for any errors reading the input
case readFailure
}
// Alias clib's write() so Stdout.write(bytes:) can call it.
private let _write = write
class Stdin {
static func readall() throws -> Data {
let fd: Int32 = 0
let buffSize = 32
var buff = [UInt8]()
while true {
var fragment = [UInt8](repeating: 0, count: buffSize)
let count = read(fd, &fragment, buffSize)
if count < 0 {
throw PluginError.readFailure
}
if count < buffSize {
buff += fragment[0..<count]
return Data(bytes: buff)
}
buff += fragment
}
}
}
class Stdout {
static func write(bytes: Data) {
bytes.withUnsafeBytes { (p: UnsafePointer<UInt8>) -> () in
_ = _write(1, p, bytes.count)
}
}
}
struct CodePrinter {
private(set) var content = ""
private var currentIndentDepth = 0
private var currentIndent = ""
private var atLineStart = true
mutating func print(_ text: String...) {
for t in text {
for c in t.characters {
if c == "\n" {
content.append(c)
atLineStart = true
} else {
if atLineStart {
content.append(currentIndent)
atLineStart = false
}
content.append(c)
}
}
}
}
mutating private func resetIndent() {
currentIndent = (0..<currentIndentDepth).map { Int -> String in return " " } .joined(separator:"")
}
mutating func indent() {
currentIndentDepth += 1
resetIndent()
}
mutating func outdent() {
currentIndentDepth -= 1
resetIndent()
}
}
// END swift-protobuf derivation

View File

@ -0,0 +1,75 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// 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.
import Foundation
import Gnostic
func printDocument(document:Openapi_V2_Document,
name:String) -> String {
var code = CodePrinter()
code.print("READING \(name)\n")
code.print("Swagger: \(document.swagger)\n")
code.print("Host: \(document.host)\n")
code.print("BasePath: \(document.basePath)\n")
if document.hasInfo {
code.print("Info:\n")
code.indent()
if document.info.title != "" {
code.print("Title: \(document.info.title)\n")
}
if document.info.description_p != "" {
code.print("Description: \(document.info.description_p)\n")
}
if document.info.version != "" {
code.print("Version: \(document.info.version)\n")
}
code.outdent()
}
code.print("Paths:\n")
code.indent()
for pair in document.paths.path {
let v = pair.value
if v.hasGet {
code.print("GET \(pair.name)\n")
}
if v.hasPost {
code.print("POST \(pair.name)\n")
}
}
code.outdent()
return code.content
}
func main() throws {
var response = Gnostic_Plugin_V1_Response()
let rawRequest = try Stdin.readall()
let request = try Gnostic_Plugin_V1_Request(serializedData: rawRequest)
for model in request.models {
if model.typeURL == "openapi.v2.Document" {
let document = try Openapi_V2_Document(serializedData: model.value)
let report = printDocument(document:document, name:request.sourceName)
if let reportData = report.data(using:.utf8) {
var file = Gnostic_Plugin_V1_File()
file.name = "report.txt"
file.data = reportData
response.files.append(file)
}
}
}
let serializedResponse = try response.serializedData()
Stdout.write(bytes: serializedResponse)
}
try main()

View File

@ -0,0 +1,52 @@
#!/bin/sh
#
# Copyright 2017 Google Inc. All Rights Reserved.
#
# 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.
# Use this script to run protoc and swift-proto to generate
# support code for gnostic protos.
GNOSTIC=$GOPATH/src/github.com/googleapis/gnostic
PROTOS=(
plugins/plugin.proto
OpenAPIv2/OpenAPIv2.proto
OpenAPIv3/OpenAPIv3.proto
surface/surface.proto
discovery/discovery.proto
)
# remove old compiled pb files
rm -rf Sources/Gnostic/*.pb.swift
# remove any prior compilations
rm -rf Sources/Gnostic/github.com
# compile protos
for proto in "${PROTOS[@]}"
do
echo "COMPILING $proto"
protoc $GNOSTIC/$proto \
--swift_opt=Visibility=Public \
--swift_out=Sources/Gnostic \
--proto_path=$GOPATH/src
# relocate compiled protos
find Sources/Gnostic/github.com -name "*.pb.swift" -exec mv {} Sources/Gnostic \;
# remove scaffolding of compilation
rm -rf Sources/Gnostic/github.com
done

View File

@ -0,0 +1,372 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: plugins/plugin.proto
/*
Package gnostic_plugin_v1 is a generated protocol buffer package.
It is generated from these files:
plugins/plugin.proto
It has these top-level messages:
Version
Parameter
Request
Message
Messages
Response
File
*/
package gnostic_plugin_v1
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
import google_protobuf "github.com/golang/protobuf/ptypes/any"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
type Message_Level int32
const (
Message_UNKNOWN Message_Level = 0
Message_INFO Message_Level = 1
Message_WARNING Message_Level = 2
Message_ERROR Message_Level = 3
Message_FATAL Message_Level = 4
)
var Message_Level_name = map[int32]string{
0: "UNKNOWN",
1: "INFO",
2: "WARNING",
3: "ERROR",
4: "FATAL",
}
var Message_Level_value = map[string]int32{
"UNKNOWN": 0,
"INFO": 1,
"WARNING": 2,
"ERROR": 3,
"FATAL": 4,
}
func (x Message_Level) String() string {
return proto.EnumName(Message_Level_name, int32(x))
}
func (Message_Level) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{3, 0} }
// The version number of gnostic.
type Version struct {
Major int32 `protobuf:"varint,1,opt,name=major" json:"major,omitempty"`
Minor int32 `protobuf:"varint,2,opt,name=minor" json:"minor,omitempty"`
Patch int32 `protobuf:"varint,3,opt,name=patch" json:"patch,omitempty"`
// A suffix for alpha, beta or rc release, e.g., "alpha-1", "rc2". It should
// be empty for mainline stable releases.
Suffix string `protobuf:"bytes,4,opt,name=suffix" json:"suffix,omitempty"`
}
func (m *Version) Reset() { *m = Version{} }
func (m *Version) String() string { return proto.CompactTextString(m) }
func (*Version) ProtoMessage() {}
func (*Version) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
func (m *Version) GetMajor() int32 {
if m != nil {
return m.Major
}
return 0
}
func (m *Version) GetMinor() int32 {
if m != nil {
return m.Minor
}
return 0
}
func (m *Version) GetPatch() int32 {
if m != nil {
return m.Patch
}
return 0
}
func (m *Version) GetSuffix() string {
if m != nil {
return m.Suffix
}
return ""
}
// A parameter passed to the plugin from (or through) gnostic.
type Parameter struct {
// The name of the parameter as specified in the option string
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
// The parameter value as specified in the option string
Value string `protobuf:"bytes,2,opt,name=value" json:"value,omitempty"`
}
func (m *Parameter) Reset() { *m = Parameter{} }
func (m *Parameter) String() string { return proto.CompactTextString(m) }
func (*Parameter) ProtoMessage() {}
func (*Parameter) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
func (m *Parameter) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *Parameter) GetValue() string {
if m != nil {
return m.Value
}
return ""
}
// An encoded Request is written to the plugin's stdin.
type Request struct {
// filename or URL of the original source document
SourceName string `protobuf:"bytes,1,opt,name=source_name,json=sourceName" json:"source_name,omitempty"`
// Output path specified in the plugin invocation.
OutputPath string `protobuf:"bytes,2,opt,name=output_path,json=outputPath" json:"output_path,omitempty"`
// Plugin parameters parsed from the invocation string.
Parameters []*Parameter `protobuf:"bytes,3,rep,name=parameters" json:"parameters,omitempty"`
// The version number of gnostic.
CompilerVersion *Version `protobuf:"bytes,4,opt,name=compiler_version,json=compilerVersion" json:"compiler_version,omitempty"`
// API models
Models []*google_protobuf.Any `protobuf:"bytes,5,rep,name=models" json:"models,omitempty"`
}
func (m *Request) Reset() { *m = Request{} }
func (m *Request) String() string { return proto.CompactTextString(m) }
func (*Request) ProtoMessage() {}
func (*Request) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
func (m *Request) GetSourceName() string {
if m != nil {
return m.SourceName
}
return ""
}
func (m *Request) GetOutputPath() string {
if m != nil {
return m.OutputPath
}
return ""
}
func (m *Request) GetParameters() []*Parameter {
if m != nil {
return m.Parameters
}
return nil
}
func (m *Request) GetCompilerVersion() *Version {
if m != nil {
return m.CompilerVersion
}
return nil
}
func (m *Request) GetModels() []*google_protobuf.Any {
if m != nil {
return m.Models
}
return nil
}
// Plugins can return messages to be collated and reported by gnostic.
type Message struct {
// message severity
Level Message_Level `protobuf:"varint,1,opt,name=level,enum=gnostic.plugin.v1.Message_Level" json:"level,omitempty"`
// a unique message identifier
Code string `protobuf:"bytes,2,opt,name=code" json:"code,omitempty"`
// message text
Text string `protobuf:"bytes,3,opt,name=text" json:"text,omitempty"`
// an associated key path in an API description
Keys []string `protobuf:"bytes,4,rep,name=keys" json:"keys,omitempty"`
}
func (m *Message) Reset() { *m = Message{} }
func (m *Message) String() string { return proto.CompactTextString(m) }
func (*Message) ProtoMessage() {}
func (*Message) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
func (m *Message) GetLevel() Message_Level {
if m != nil {
return m.Level
}
return Message_UNKNOWN
}
func (m *Message) GetCode() string {
if m != nil {
return m.Code
}
return ""
}
func (m *Message) GetText() string {
if m != nil {
return m.Text
}
return ""
}
func (m *Message) GetKeys() []string {
if m != nil {
return m.Keys
}
return nil
}
type Messages struct {
Messages []*Message `protobuf:"bytes,1,rep,name=messages" json:"messages,omitempty"`
}
func (m *Messages) Reset() { *m = Messages{} }
func (m *Messages) String() string { return proto.CompactTextString(m) }
func (*Messages) ProtoMessage() {}
func (*Messages) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} }
func (m *Messages) GetMessages() []*Message {
if m != nil {
return m.Messages
}
return nil
}
// The plugin writes an encoded Response to stdout.
type Response struct {
// Error message. If non-empty, the plugin failed.
// The plugin process should exit with status code zero
// even if it reports an error in this way.
//
// This should be used to indicate errors which prevent the plugin from
// operating as intended. Errors which indicate a problem in gnostic
// 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.
Errors []string `protobuf:"bytes,1,rep,name=errors" json:"errors,omitempty"`
// file output, each file will be written by gnostic to an appropriate location.
Files []*File `protobuf:"bytes,2,rep,name=files" json:"files,omitempty"`
// informational messages to be collected and reported by gnostic.
Messages []*Message `protobuf:"bytes,3,rep,name=messages" json:"messages,omitempty"`
}
func (m *Response) Reset() { *m = Response{} }
func (m *Response) String() string { return proto.CompactTextString(m) }
func (*Response) ProtoMessage() {}
func (*Response) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} }
func (m *Response) GetErrors() []string {
if m != nil {
return m.Errors
}
return nil
}
func (m *Response) GetFiles() []*File {
if m != nil {
return m.Files
}
return nil
}
func (m *Response) GetMessages() []*Message {
if m != nil {
return m.Messages
}
return nil
}
// File describes a file generated by a plugin.
type File struct {
// name of the file
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
// data to be written to the file
Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"`
}
func (m *File) Reset() { *m = File{} }
func (m *File) String() string { return proto.CompactTextString(m) }
func (*File) ProtoMessage() {}
func (*File) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} }
func (m *File) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *File) GetData() []byte {
if m != nil {
return m.Data
}
return nil
}
func init() {
proto.RegisterType((*Version)(nil), "gnostic.plugin.v1.Version")
proto.RegisterType((*Parameter)(nil), "gnostic.plugin.v1.Parameter")
proto.RegisterType((*Request)(nil), "gnostic.plugin.v1.Request")
proto.RegisterType((*Message)(nil), "gnostic.plugin.v1.Message")
proto.RegisterType((*Messages)(nil), "gnostic.plugin.v1.Messages")
proto.RegisterType((*Response)(nil), "gnostic.plugin.v1.Response")
proto.RegisterType((*File)(nil), "gnostic.plugin.v1.File")
proto.RegisterEnum("gnostic.plugin.v1.Message_Level", Message_Level_name, Message_Level_value)
}
func init() { proto.RegisterFile("plugins/plugin.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
// 539 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x52, 0xc1, 0x6e, 0xd3, 0x40,
0x10, 0xc5, 0xb1, 0xdd, 0xc4, 0x13, 0x28, 0x66, 0x55, 0x81, 0xa9, 0x90, 0x1a, 0xf9, 0x42, 0x0e,
0xe0, 0xa8, 0x41, 0xf4, 0xc4, 0x25, 0x91, 0x9a, 0xa8, 0xa2, 0x38, 0xd1, 0x0a, 0xe8, 0x31, 0xda,
0x3a, 0x1b, 0xc7, 0x60, 0x7b, 0xcd, 0xee, 0x3a, 0x6a, 0x3e, 0x81, 0xdf, 0xe0, 0x4b, 0xf8, 0x32,
0x84, 0x76, 0xd7, 0x89, 0x8a, 0x08, 0x07, 0x4e, 0x7e, 0xf3, 0xf4, 0xfc, 0x66, 0xe6, 0xed, 0xc0,
0x49, 0x95, 0xd7, 0x69, 0x56, 0x8a, 0x81, 0xf9, 0x46, 0x15, 0x67, 0x92, 0xa1, 0x27, 0x69, 0xc9,
0x84, 0xcc, 0x92, 0xa8, 0x61, 0x37, 0xe7, 0xa7, 0xcf, 0x53, 0xc6, 0xd2, 0x9c, 0x0e, 0xb4, 0xe0,
0xb6, 0x5e, 0x0d, 0x48, 0xb9, 0x35, 0xea, 0x30, 0x81, 0xf6, 0x67, 0xca, 0x45, 0xc6, 0x4a, 0x74,
0x02, 0x6e, 0x41, 0xbe, 0x30, 0x1e, 0x58, 0x3d, 0xab, 0xef, 0x62, 0x53, 0x68, 0x36, 0x2b, 0x19,
0x0f, 0x5a, 0x0d, 0xab, 0x0a, 0xc5, 0x56, 0x44, 0x26, 0xeb, 0xc0, 0x36, 0xac, 0x2e, 0xd0, 0x53,
0x38, 0x12, 0xf5, 0x6a, 0x95, 0xdd, 0x05, 0x4e, 0xcf, 0xea, 0x7b, 0xb8, 0xa9, 0xc2, 0xb7, 0xe0,
0xcd, 0x09, 0x27, 0x05, 0x95, 0x94, 0x23, 0x04, 0x4e, 0x49, 0x0a, 0xaa, 0xbb, 0x78, 0x58, 0x63,
0x65, 0xb7, 0x21, 0x79, 0x4d, 0x75, 0x13, 0x0f, 0x9b, 0x22, 0xfc, 0x65, 0x41, 0x1b, 0xd3, 0x6f,
0x35, 0x15, 0x12, 0x9d, 0x41, 0x57, 0xb0, 0x9a, 0x27, 0x74, 0x71, 0xef, 0x67, 0x30, 0x54, 0xac,
0x2c, 0xce, 0xa0, 0xcb, 0x6a, 0x59, 0xd5, 0x72, 0x51, 0x11, 0xb9, 0x6e, 0x8c, 0xc0, 0x50, 0x73,
0x22, 0xd7, 0xe8, 0x1d, 0x40, 0xb5, 0x1b, 0x42, 0x04, 0x76, 0xcf, 0xee, 0x77, 0x87, 0x2f, 0xa2,
0xbf, 0xc2, 0x8a, 0xf6, 0x93, 0xe2, 0x7b, 0x7a, 0x74, 0x09, 0x7e, 0xc2, 0x8a, 0x2a, 0xcb, 0x29,
0x5f, 0x6c, 0x4c, 0x60, 0x7a, 0xc9, 0xee, 0xf0, 0xf4, 0x80, 0x47, 0x13, 0x29, 0x7e, 0xbc, 0xfb,
0x67, 0x97, 0xf1, 0x2b, 0x38, 0x2a, 0xd8, 0x92, 0xe6, 0x22, 0x70, 0xf5, 0x00, 0x27, 0x91, 0x79,
0x9a, 0x68, 0xf7, 0x34, 0xd1, 0xa8, 0xdc, 0xe2, 0x46, 0x13, 0xfe, 0xb4, 0xa0, 0xfd, 0x81, 0x0a,
0x41, 0x52, 0x8a, 0x2e, 0xc0, 0xcd, 0xe9, 0x86, 0xe6, 0x7a, 0xf5, 0xe3, 0x61, 0xef, 0x40, 0xd7,
0x46, 0x1a, 0x5d, 0x2b, 0x1d, 0x36, 0x72, 0x15, 0x77, 0xc2, 0x96, 0xbb, 0x64, 0x35, 0x56, 0x9c,
0xa4, 0x77, 0x52, 0x3f, 0x9e, 0x87, 0x35, 0x56, 0xdc, 0x57, 0xba, 0x15, 0x81, 0xd3, 0xb3, 0x15,
0xa7, 0x70, 0x38, 0x02, 0x57, 0x7b, 0xa1, 0x2e, 0xb4, 0x3f, 0xc5, 0xef, 0xe3, 0xd9, 0x4d, 0xec,
0x3f, 0x40, 0x1d, 0x70, 0xae, 0xe2, 0xc9, 0xcc, 0xb7, 0x14, 0x7d, 0x33, 0xc2, 0xf1, 0x55, 0x3c,
0xf5, 0x5b, 0xc8, 0x03, 0xf7, 0x12, 0xe3, 0x19, 0xf6, 0x6d, 0x05, 0x27, 0xa3, 0x8f, 0xa3, 0x6b,
0xdf, 0x09, 0xc7, 0xd0, 0x69, 0xc6, 0x12, 0xe8, 0x02, 0x3a, 0x45, 0x83, 0x03, 0x4b, 0xaf, 0x7f,
0xfa, 0xef, 0x2d, 0xf0, 0x5e, 0x1b, 0x7e, 0xb7, 0xa0, 0x83, 0xa9, 0xa8, 0x58, 0x29, 0xa8, 0xba,
0x31, 0xca, 0x39, 0xe3, 0xc6, 0xc2, 0xc3, 0x4d, 0x85, 0x5e, 0x83, 0xbb, 0xca, 0x72, 0x2a, 0x82,
0x96, 0x76, 0x7e, 0x76, 0xc0, 0x79, 0x92, 0xe5, 0x14, 0x1b, 0xd5, 0x1f, 0xb3, 0xd8, 0xff, 0x31,
0x4b, 0x04, 0x8e, 0xb2, 0x39, 0x78, 0xc5, 0x08, 0x9c, 0x25, 0x91, 0x44, 0x47, 0xfd, 0x10, 0x6b,
0x3c, 0x7e, 0x09, 0xc7, 0x8c, 0xa7, 0x7b, 0xeb, 0xcd, 0xf9, 0xf8, 0xd1, 0xd4, 0xe0, 0xb9, 0xee,
0x32, 0xb7, 0x7e, 0xb4, 0xec, 0x69, 0x3c, 0xbb, 0x3d, 0xd2, 0x17, 0xf0, 0xe6, 0x77, 0x00, 0x00,
0x00, 0xff, 0xff, 0x97, 0xa0, 0x65, 0xe7, 0xd5, 0x03, 0x00, 0x00,
}

View File

@ -0,0 +1,148 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// 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.
// gnostic can be extended with plugins.
// A plugin is uset a program that reads a Request from stdin
// and writes a Response to stdout.
//
// A plugin executable needs only to be placed somewhere in the path. The
// plugin should be named "gnostic_$NAME", and will then be used when the
// flag "--${NAME}_out" is passed to gnostic.
syntax = "proto3";
import "google/protobuf/any.proto";
package gnostic.plugin.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
// developer experience by reducing one-level of name nesting and be
// consistent with most programming languages that don't support outer classes.
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 = "GnosticPlugin";
// The Java package name must be proto package name with proper prefix.
option java_package = "org.gnostic.v1";
// A reasonable prefix for the Objective-C symbols generated from the package.
// It should at a minimum be 3 characters long, all uppercase, and convention
// is to use an abbreviation of the package name. Something short, but
// 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 = "GNO";
// The version number of gnostic.
message Version {
int32 major = 1;
int32 minor = 2;
int32 patch = 3;
// A suffix for alpha, beta or rc release, e.g., "alpha-1", "rc2". It should
// be empty for mainline stable releases.
string suffix = 4;
}
// A parameter passed to the plugin from (or through) gnostic.
message Parameter {
// The name of the parameter as specified in the option string
string name = 1;
// The parameter value as specified in the option string
string value = 2;
}
// An encoded Request is written to the plugin's stdin.
message Request {
// filename or URL of the original source document
string source_name = 1;
// Output path specified in the plugin invocation.
string output_path = 2;
// Plugin parameters parsed from the invocation string.
repeated Parameter parameters = 3;
// The version number of gnostic.
Version compiler_version = 4;
// API models
repeated google.protobuf.Any models = 5;
}
// Plugins can return messages to be collated and reported by gnostic.
message Message {
enum Level {
UNKNOWN = 0;
INFO = 1;
WARNING = 2;
ERROR = 3;
FATAL = 4;
}
// message severity
Level level = 1;
// a unique message identifier
string code = 2;
// message text
string text = 3;
// an associated key path in an API description
repeated string keys = 4;
}
message Messages {
repeated Message messages = 1;
}
// The plugin writes an encoded Response to stdout.
message Response {
// Error message. If non-empty, the plugin failed.
// The plugin process should exit with status code zero
// even if it reports an error in this way.
//
// This should be used to indicate errors which prevent the plugin from
// operating as intended. Errors which indicate a problem in gnostic
// 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 errors = 1;
// file output, each file will be written by gnostic to an appropriate location.
repeated File files = 2;
// informational messages to be collected and reported by gnostic.
repeated Message messages = 3;
}
// File describes a file generated by a plugin.
message File {
// name of the file
string name = 1;
// data to be written to the file
bytes data = 2;
}