Add vendor folder to git

This commit is contained in:
Lucas Käldström 2017-06-26 19:23:05 +03:00
parent 66cf5eaafb
commit 183585f56f
No known key found for this signature in database
GPG key ID: 600FEFBBD0D40D21
6916 changed files with 2629581 additions and 1 deletions

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,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.
// 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"
"errors"
"fmt"
"io/ioutil"
"os"
"path"
"strings"
"github.com/golang/protobuf/proto"
"github.com/googleapis/gnostic/plugins/go/gnostic_analyze/statistics"
openapi "github.com/googleapis/gnostic/OpenAPIv2"
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)
}
func main() {
// Initialize the response.
response := &plugins.Response{}
// Read the request.
data, err := ioutil.ReadAll(os.Stdin)
sendAndExitIfError(err, response)
// Unmarshal the request.
request := &plugins.Request{}
err = proto.Unmarshal(data, request)
sendAndExitIfError(err, response)
// Verify that the passed-in description is supported.
wrapper := request.Wrapper
if wrapper.Version != "v2" {
err = errors.New(
fmt.Sprintf("%s requires an OpenAPI v2 description.",
os.Args[0]))
sendAndExitIfError(err, response)
}
// Unmarshal the description.
document := &openapi.Document{}
err = proto.Unmarshal(wrapper.Value, document)
sendAndExitIfError(err, response)
// Analyze the API document.
stats := statistics.NewDocumentStatistics(wrapper.Name, document)
// 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")...)
sendAndExitIfError(err, response)
response.Files = append(response.Files, file)
// Send the final results. Success!
sendAndExit(response)
}

View file

@ -0,0 +1,328 @@
// 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"`
}
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 {
if p.Type != "" {
return p.Type
} else if p.Items != nil && p.Items.Type != "" {
return p.Items.Type
} else {
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" {
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,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/go/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.
apis_with_anonymous_operations := 0
apis_with_anonymous_objects := 0
apis_with_anonymous_anything := 0
op_frequencies := make(map[string]int, 0)
parameter_type_frequencies := make(map[string]int, 0)
result_type_frequencies := make(map[string]int, 0)
definition_field_type_frequencies := make(map[string]int, 0)
definition_array_type_frequencies := make(map[string]int, 0)
definition_primitive_type_frequencies := make(map[string]int, 0)
for _, api := range stats {
if api.Operations["anonymous"] != 0 {
apis_with_anonymous_operations += 1
}
if len(api.AnonymousObjects) > 0 {
apis_with_anonymous_objects += 1
}
if len(api.AnonymousOperations) > 0 {
apis_with_anonymous_anything += 1
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 {
apis_with_anonymous_anything += 1
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 {
op_frequencies[k] += v
}
for k, v := range api.ParameterTypes {
parameter_type_frequencies[k] += v
}
for k, v := range api.ResultTypes {
result_type_frequencies[k] += v
}
for k, v := range api.DefinitionFieldTypes {
definition_field_type_frequencies[k] += v
}
for k, v := range api.DefinitionArrayTypes {
definition_array_type_frequencies[k] += v
}
for k, v := range api.DefinitionPrimitiveTypes {
definition_primitive_type_frequencies[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", apis_with_anonymous_operations)
fmt.Printf("APIs with anonymous objects: %d\n", apis_with_anonymous_objects)
fmt.Printf("APIs with anonymous anything: %d\n", apis_with_anonymous_anything)
fmt.Printf("\nOperation frequencies:\n")
printFrequencies(op_frequencies)
fmt.Printf("\nParameter type frequencies:\n")
printFrequencies(parameter_type_frequencies)
fmt.Printf("\nResult type frequencies:\n")
printFrequencies(result_type_frequencies)
fmt.Printf("\nDefinition object field type frequencies:\n")
printFrequencies(definition_field_type_frequencies)
fmt.Printf("\nDefinition array type frequencies:\n")
printFrequencies(definition_array_type_frequencies)
fmt.Printf("\nDefinition primitive type frequencies:\n")
printFrequencies(definition_primitive_type_frequencies)
}

View file

@ -0,0 +1,10 @@
build:
go install github.com/googleapis/gnostic
go install github.com/googleapis/gnostic/plugins/go/gnostic_go_generator/encode_templates
go generate github.com/googleapis/gnostic/plugins/go/gnostic_go_generator
go install github.com/googleapis/gnostic/plugins/go/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,17 @@
# OpenAPI Go Generator Plugin
This directory contains an `openapic` 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:
openapic bookstore.json --go_generator_out=package=bookstore:bookstore
Where `bookstore` is the name of a directory where the generated code will be written and `package=bookstore` indicates that "bookstore" should also be the package name used for generated code.
By default, both client and server code will be generated. If the `openapi_go_generator` binary is also linked from the names `openapi_go_client` and `openapi_go_server`, then only client or only server code can be generated as follows:
openapic bookstore.json --go_client_out=package=bookstore:bookstore
openapic bookstore.json --go_server_out=package=bookstore:bookstore
For example usage, see the [examples/bookstore](examples/bookstore) directory.

View file

@ -0,0 +1,87 @@
// 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.
// encode-templates is a support tool for building text templates
// directly into an application binary.
//
// It reads files from a "templates" directory, and generates
// a Go source file containing base64-encoded representations of those
// files. This allows these files to be directly compiled into the
// executable.
package main
import (
"bytes"
"encoding/base64"
"io/ioutil"
"path/filepath"
"strings"
"text/template"
)
const TEMPLATES_GO = `// 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 templates() map[string]string {
return map[string]string{ {{range .TemplateStrings}}
"{{.Name}}": "{{.Encoding}}",{{end}}
}
}`
type NamedTemplateString struct {
Name string // the name of the file to be generated by the template
Encoding string // base64-encoded string
}
func main() {
fileinfos, err := ioutil.ReadDir("templates")
if err != nil {
panic(err)
}
templateStrings := make([]*NamedTemplateString, 0)
for _, fileinfo := range fileinfos {
name := fileinfo.Name()
if filepath.Ext(name) == ".tmpl" {
data, _ := ioutil.ReadFile("templates/" + name)
encoding := base64.StdEncoding.EncodeToString(data)
templateStrings = append(templateStrings,
&NamedTemplateString{
Name: strings.TrimSuffix(name, ".tmpl"),
Encoding: encoding})
}
}
t, err := template.New("").Parse(TEMPLATES_GO)
if err != nil {
panic(err)
}
f := new(bytes.Buffer)
err = t.Execute(f, struct{ TemplateStrings []*NamedTemplateString }{templateStrings})
if err != nil {
panic(err)
}
ioutil.WriteFile("templates.go", f.Bytes(), 0644)
}

View file

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

View file

@ -0,0 +1,41 @@
package main
import (
"fmt"
"github.com/googleapis/gnostic/plugins/go/gnostic_go_generator/examples/v2.0/apis_guru/apis_guru"
"sort"
)
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 {
keys = append(keys, key)
}
sort.Strings(keys)
for _, key := range keys {
api := (*apis)[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)["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,21 @@
build:
go install github.com/googleapis/gnostic
go install github.com/googleapis/gnostic/plugins/go/gnostic_go_generator/encode_templates
go generate github.com/googleapis/gnostic/plugins/go/gnostic_go_generator
go install github.com/googleapis/gnostic/plugins/go/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
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 `openapi_go_generator` plugin, which implements
`openapi_go_client` and `openapi_go_server` for generating API client and
server code, respectively.
Run "make all" to build and install `openapic` 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,209 @@
/*
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 (
"net/http"
"strings"
"testing"
"github.com/googleapis/gnostic/plugins/go/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)
// reset the service by deleting all shelves
{
err := b.DeleteShelves()
if err != nil {
t.Fail()
}
}
// verify that the service has no shelves
{
response, err := b.ListShelves()
if err != nil {
t.Fail()
}
if len(response.Shelves) != 0 {
t.Fail()
}
}
// attempting to get a shelf should return an error
{
_, err := b.GetShelf(1)
if err == nil {
t.Fail()
}
}
// attempting to get a book should return an error
{
_, err := b.GetBook(1, 2)
if err == nil {
t.Fail()
}
}
// add a shelf
{
var shelf bookstore.Shelf
shelf.Theme = "mysteries"
response, err := b.CreateShelf(shelf)
if err != nil {
t.Fail()
}
if (response.Name != "shelves/1") ||
(response.Theme != "mysteries") {
t.Fail()
}
}
// add another shelf
{
var shelf bookstore.Shelf
shelf.Theme = "comedies"
response, err := b.CreateShelf(shelf)
if err != nil {
t.Fail()
}
if (response.Name != "shelves/2") ||
(response.Theme != "comedies") {
t.Fail()
}
}
// get the first shelf that was added
{
response, err := b.GetShelf(1)
if err != nil {
t.Fail()
}
if (response.Name != "shelves/1") ||
(response.Theme != "mysteries") {
t.Fail()
}
}
// list shelves and verify that there are 2
{
response, err := b.ListShelves()
if err != nil {
t.Fail()
}
if len(response.Shelves) != 2 {
t.Fail()
}
}
// delete a shelf
{
err := b.DeleteShelf(2)
if err != nil {
t.Fail()
}
}
// list shelves and verify that there is only 1
{
response, err := b.ListShelves()
if err != nil {
t.Fail()
}
if len(response.Shelves) != 1 {
t.Fail()
}
}
// list books on a shelf, verify that there are none
{
response, err := b.ListBooks(1)
if err != nil {
t.Fail()
}
if len(response.Books) != 0 {
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.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.Fail()
}
}
// get the first book that was added
{
_, err := b.GetBook(1, 1)
if err != nil {
t.Fail()
}
}
// list the books on a shelf and verify that there are 2
{
response, err := b.ListBooks(1)
if err != nil {
t.Fail()
}
if len(response.Books) != 2 {
t.Fail()
}
}
// delete a book
{
err := b.DeleteBook(1, 2)
if err != nil {
t.Fail()
}
}
// list the books on a shelf and verify that is only 1
{
response, err := b.ListBooks(1)
if err != nil {
t.Fail()
}
if len(response.Books) != 1 {
t.Fail()
}
}
// verify the handling of a badly-formed request
{
req, err := http.NewRequest("POST", service+"/shelves", strings.NewReader(""))
if err != nil {
return
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return
}
// we expect a 400 (Bad Request) code
if resp.StatusCode != 400 {
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/go/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/go/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/go/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,3 @@
all:
gnostic swagger.json --go_client_out=xkcd
go install

View file

@ -0,0 +1,22 @@
package main
import (
"fmt"
"github.com/googleapis/gnostic/plugins/go/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,125 @@
// 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"
"text/template"
)
// This file contains support functions that are passed into template
// evaluation for use within templates.
func hasFieldNamedOK(s *ServiceType) bool {
return s.hasFieldNamed("OK")
}
func hasFieldNamedDefault(s *ServiceType) bool {
return s.hasFieldNamed("Default")
}
func hasParameters(m *ServiceMethod) bool {
return m.ParametersType != nil
}
func hasResponses(m *ServiceMethod) bool {
return m.ResponsesType != nil
}
func hasPathParameters(m *ServiceMethod) bool {
for _, field := range m.ParametersType.Fields {
if field.Position == "path" {
return true
}
}
return false
}
func hasFormParameters(m *ServiceMethod) bool {
for _, field := range m.ParametersType.Fields {
if field.Position == "formdata" {
return true
}
}
return false
}
func goType(openapi_type string) string {
switch openapi_type {
case "number":
return "int"
default:
return openapi_type
}
}
func parameterList(m *ServiceMethod) string {
result := ""
if m.ParametersType != nil {
for i, field := range m.ParametersType.Fields {
if i > 0 {
result += ", "
}
result += field.ParameterName + " " + field.NativeType
}
}
return result
}
func bodyParameterName(m *ServiceMethod) string {
for _, field := range m.ParametersType.Fields {
if field.Position == "body" {
return field.JSONName
}
}
return ""
}
func bodyParameterFieldName(m *ServiceMethod) string {
for _, field := range m.ParametersType.Fields {
if field.Position == "body" {
return field.Name
}
}
return ""
}
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
}
func templateHelpers() template.FuncMap {
return template.FuncMap{
"hasFieldNamedOK": hasFieldNamedOK,
"hasFieldNamedDefault": hasFieldNamedDefault,
"hasParameters": hasParameters,
"hasPathParameters": hasPathParameters,
"hasFormParameters": hasFormParameters,
"hasResponses": hasResponses,
"goType": goType,
"parameterList": parameterList,
"bodyParameterName": bodyParameterName,
"bodyParameterFieldName": bodyParameterFieldName,
"commentForText": commentForText,
}
}

View file

@ -0,0 +1,71 @@
// 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/exec"
"runtime"
"strings"
)
// Remove lines containing only "//-" after templates have been expanded.
// Code templates use "//-" prefixes to mark template operators
// that otherwise would add unnecessary blank lines.
func stripMarkers(inputBytes []byte) (outputBytes []byte) {
inputString := string(inputBytes)
inputLines := strings.Split(inputString, "\n")
outputLines := make([]string, 0)
for _, line := range inputLines {
if strings.Contains(line, "//-") {
removed := strings.TrimSpace(strings.Replace(line, "//-", "", 1))
if removed != "" {
outputLines = append(outputLines, removed)
}
} else {
outputLines = append(outputLines, line)
}
}
outputString := strings.Join(outputLines, "\n")
return []byte(outputString)
}
// Run the gofmt tool to format generated code.
func gofmt(filename string, inputBytes []byte) (outputBytes []byte, err error) {
if false {
return inputBytes, nil
}
cmd := exec.Command(runtime.GOROOT() + "/bin/gofmt")
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,108 @@
// 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.
//go:generate encode_templates
// gnostic_go_generator is a sample Gnostic plugin that generates Go
// code that supports an API.
package main
import (
"errors"
"fmt"
"io/ioutil"
"log"
"os"
"github.com/golang/protobuf/proto"
openapi "github.com/googleapis/gnostic/OpenAPIv2"
plugins "github.com/googleapis/gnostic/plugins"
)
// Helper: if error is not nil, record it, serializes and returns the response and exits
func sendAndExitIfError(err error, response *plugins.Response) {
if err != nil {
response.Errors = append(response.Errors, err.Error())
sendAndExit(response)
}
}
// Helper: serializes and returns the response
func sendAndExit(response *plugins.Response) {
responseBytes, _ := proto.Marshal(response)
os.Stdout.Write(responseBytes)
os.Exit(0)
}
// This is the main function for the code generation plugin.
func main() {
// Use the name used to run the plugin to decide which files to generate.
var files []string
switch os.Args[0] {
case "gnostic_go_client":
files = []string{"client.go", "types.go"}
case "gnostic_go_server":
files = []string{"server.go", "provider.go", "types.go"}
default:
files = []string{"client.go", "server.go", "provider.go", "types.go"}
}
// Initialize the plugin response.
response := &plugins.Response{}
// Read the plugin input.
data, err := ioutil.ReadAll(os.Stdin)
sendAndExitIfError(err, response)
// Deserialize the input.
request := &plugins.Request{}
err = proto.Unmarshal(data, request)
sendAndExitIfError(err, response)
// Collect parameters passed to the plugin.
invocation := os.Args[0]
parameters := request.Parameters
packageName := request.OutputPath // the default package name is the output directory
for _, parameter := range parameters {
invocation += " " + parameter.Name + "=" + parameter.Value
if parameter.Name == "package" {
packageName = parameter.Value
}
}
// Log the invocation.
log.Printf("Running %s(input:%s)", invocation, request.Wrapper.Version)
// Read the document sent by the plugin and use it to generate client/server code.
if request.Wrapper.Version != "v2" {
err = errors.New(fmt.Sprintf("Unsupported OpenAPI version %s", request.Wrapper.Version))
sendAndExitIfError(err, response)
}
document := &openapi.Document{}
err = proto.Unmarshal(request.Wrapper.Value, document)
sendAndExitIfError(err, response)
// Create the renderer.
renderer, err := NewServiceRenderer(document, packageName)
sendAndExitIfError(err, response)
// Run the renderer to generate files and add them to the response object.
err = renderer.Generate(response, files)
sendAndExitIfError(err, response)
// Return with success.
sendAndExit(response)
}

View file

@ -0,0 +1,441 @@
// 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"
"encoding/base64"
"fmt"
"log"
_ "os"
"path"
"path/filepath"
"strings"
"text/template"
"unicode"
"unicode/utf8"
openapi "github.com/googleapis/gnostic/OpenAPIv2"
plugins "github.com/googleapis/gnostic/plugins"
)
// A service type typically corresponds to a definition, parameter,
// or response in the API and is represented by a type in generated code.
type ServiceType struct {
Name string
Kind string
Fields []*ServiceTypeField
}
func (s *ServiceType) hasFieldNamed(name string) bool {
for _, f := range s.Fields {
if f.Name == name {
return true
}
}
return false
}
// A service type field is a field in a definition and can be
// associated with a position in a request structure.
type ServiceTypeField struct {
Name string // the name as specified
Type string // the specified type of the field
NativeType string // the programming-language native type of the field
FieldName string // the name to use for data structure fields
ParameterName string // the name to use for parameters
JSONName string // the name to use in JSON serialization
Position string // "body", "header", "formdata", "query", or "path"
}
// A service method is an operation of an API and typically
// has associated client and server code.
type ServiceMethod struct {
Name string // Operation name, possibly generated from method and path
Path string // HTTP path
Method string // HTTP method name
Description string // description of method
HandlerName string // name of the generated handler
ProcessorName string // name of the processing function in the service interface
ClientName string // name of client
ResultTypeName string // native type name for the result structure
ParametersTypeName string // native type name for the input parameters structure
ResponsesTypeName string // native type name for the responses
ParametersType *ServiceType // parameters (input)
ResponsesType *ServiceType // responses (output)
}
// A renderer reads an OpenAPI document and generates code.
type ServiceRenderer struct {
Templates map[string]*template.Template
Name string
Package string
Types []*ServiceType
Methods []*ServiceMethod
}
// Create a renderer.
func NewServiceRenderer(document *openapi.Document, packageName string) (renderer *ServiceRenderer, err error) {
renderer = &ServiceRenderer{}
// Load templates.
err = renderer.loadTemplates(templates())
if err != nil {
return nil, err
}
// Set renderer properties from passed-in document.
renderer.Name = document.Info.Title
renderer.Package = packageName // Set package name from argument.
renderer.Types = make([]*ServiceType, 0)
renderer.Methods = make([]*ServiceMethod, 0)
err = renderer.loadService(document)
if err != nil {
return nil, err
}
return renderer, nil
}
// Load templates that will be used by the renderer.
func (renderer *ServiceRenderer) loadTemplates(files map[string]string) (err error) {
helpers := templateHelpers()
renderer.Templates = make(map[string]*template.Template, 0)
for filename, encoding := range files {
templateData, err := base64.StdEncoding.DecodeString(encoding)
if err != nil {
return err
}
t, err := template.New(filename).Funcs(helpers).Parse(string(templateData))
if err != nil {
return err
} else {
renderer.Templates[filename] = t
}
}
return err
}
// Preprocess the types and methods of the API.
func (renderer *ServiceRenderer) loadService(document *openapi.Document) (err error) {
// Collect service type descriptions from Definitions section.
if document.Definitions != nil {
for _, pair := range document.Definitions.AdditionalProperties {
var t ServiceType
t.Fields = make([]*ServiceTypeField, 0)
schema := pair.Value
if schema.Properties != nil {
if len(schema.Properties.AdditionalProperties) > 0 {
// If the schema has properties, generate a struct.
t.Kind = "struct"
}
for _, pair2 := range schema.Properties.AdditionalProperties {
var f ServiceTypeField
f.Name = strings.Title(pair2.Name)
f.Type = typeForSchema(pair2.Value)
f.JSONName = pair2.Name
t.Fields = append(t.Fields, &f)
}
}
t.Name = strings.Title(filteredTypeName(pair.Name))
if len(t.Fields) == 0 {
if schema.AdditionalProperties != nil {
// If the schema has no fixed properties and additional properties of a specified type,
// generate a map pointing to objects of that type.
mapType := typeForRef(schema.AdditionalProperties.GetSchema().XRef)
t.Kind = "map[string]" + mapType
}
}
renderer.Types = append(renderer.Types, &t)
}
}
// Collect service method descriptions from Paths section.
for _, pair := range document.Paths.Path {
v := pair.Value
if v.Get != nil {
renderer.loadOperation(v.Get, "GET", pair.Name)
}
if v.Post != nil {
renderer.loadOperation(v.Post, "POST", pair.Name)
}
if v.Put != nil {
renderer.loadOperation(v.Put, "PUT", pair.Name)
}
if v.Delete != nil {
renderer.loadOperation(v.Delete, "DELETE", pair.Name)
}
}
return err
}
// convert the first character of a string to upper case
func upperFirst(s string) string {
if s == "" {
return ""
}
r, n := utf8.DecodeRuneInString(s)
return string(unicode.ToUpper(r)) + strings.ToLower(s[n:])
}
func generate_operation_name(method, path string) string {
filteredPath := strings.Replace(path, "/", "_", -1)
filteredPath = strings.Replace(filteredPath, ".", "_", -1)
filteredPath = strings.Replace(filteredPath, "{", "", -1)
filteredPath = strings.Replace(filteredPath, "}", "", -1)
return upperFirst(method) + filteredPath
}
func (renderer *ServiceRenderer) loadOperation(op *openapi.Operation, method string, path string) (err error) {
var m ServiceMethod
m.Name = strings.Title(op.OperationId)
m.Path = path
m.Method = method
if m.Name == "" {
m.Name = generate_operation_name(method, path)
}
m.Description = op.Description
m.HandlerName = "Handle" + m.Name
m.ProcessorName = m.Name
m.ClientName = m.Name
m.ParametersType, err = renderer.loadServiceTypeFromParameters(m.Name+"Parameters", op.Parameters)
if m.ParametersType != nil {
m.ParametersTypeName = m.ParametersType.Name
}
m.ResponsesType, err = renderer.loadServiceTypeFromResponses(&m, m.Name+"Responses", op.Responses)
if m.ResponsesType != nil {
m.ResponsesTypeName = m.ResponsesType.Name
}
renderer.Methods = append(renderer.Methods, &m)
return err
}
func (renderer *ServiceRenderer) loadServiceTypeFromParameters(name string, parameters []*openapi.ParametersItem) (t *ServiceType, err error) {
t = &ServiceType{}
t.Kind = "struct"
t.Fields = make([]*ServiceTypeField, 0)
for _, parametersItem := range parameters {
var f ServiceTypeField
f.Type = fmt.Sprintf("%+v", parametersItem)
parameter := parametersItem.GetParameter()
if parameter != nil {
bodyParameter := parameter.GetBodyParameter()
if bodyParameter != nil {
f.Name = bodyParameter.Name
f.FieldName = strings.Replace(f.Name, "-", "_", -1)
if bodyParameter.Schema != nil {
f.Type = typeForSchema(bodyParameter.Schema)
f.NativeType = f.Type
f.Position = "body"
}
}
nonBodyParameter := parameter.GetNonBodyParameter()
if nonBodyParameter != nil {
headerParameter := nonBodyParameter.GetHeaderParameterSubSchema()
if headerParameter != nil {
f.Name = headerParameter.Name
f.FieldName = strings.Replace(f.Name, "-", "_", -1)
f.Type = headerParameter.Type
f.NativeType = f.Type
f.Position = "header"
}
formDataParameter := nonBodyParameter.GetFormDataParameterSubSchema()
if formDataParameter != nil {
f.Name = formDataParameter.Name
f.FieldName = strings.Replace(f.Name, "-", "_", -1)
f.Type = formDataParameter.Type
f.NativeType = f.Type
f.Position = "formdata"
}
queryParameter := nonBodyParameter.GetQueryParameterSubSchema()
if queryParameter != nil {
f.Name = queryParameter.Name
f.FieldName = strings.Replace(f.Name, "-", "_", -1)
f.Type = queryParameter.Type
f.NativeType = f.Type
f.Position = "query"
}
pathParameter := nonBodyParameter.GetPathParameterSubSchema()
if pathParameter != nil {
f.Name = pathParameter.Name
f.FieldName = strings.Replace(f.Name, "-", "_", -1)
f.Type = pathParameter.Type
f.NativeType = f.Type
f.Position = "path"
f.Type = typeForName(pathParameter.Type, pathParameter.Format)
}
}
f.JSONName = f.Name
f.ParameterName = replaceReservedWords(f.FieldName)
f.Name = strings.Title(f.Name)
t.Fields = append(t.Fields, &f)
if f.NativeType == "integer" {
f.NativeType = "int64"
}
}
}
t.Name = name
if len(t.Fields) > 0 {
renderer.Types = append(renderer.Types, t)
return t, err
} else {
return nil, err
}
}
func (renderer *ServiceRenderer) loadServiceTypeFromResponses(m *ServiceMethod, name string, responses *openapi.Responses) (t *ServiceType, err error) {
t = &ServiceType{}
t.Kind = "struct"
t.Fields = make([]*ServiceTypeField, 0)
for _, responseCode := range responses.ResponseCode {
var f ServiceTypeField
f.Name = propertyNameForResponseCode(responseCode.Name)
f.JSONName = ""
response := responseCode.Value.GetResponse()
if response != nil && response.Schema != nil && response.Schema.GetSchema() != nil {
f.Type = "*" + typeForSchema(response.Schema.GetSchema())
t.Fields = append(t.Fields, &f)
if f.Name == "OK" {
m.ResultTypeName = typeForSchema(response.Schema.GetSchema())
}
}
}
t.Name = name
if len(t.Fields) > 0 {
renderer.Types = append(renderer.Types, t)
return t, err
} else {
return nil, err
}
}
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
}
func typeForName(name string, format string) (typeName string) {
switch name {
case "integer":
if format == "int32" {
return "int32"
} else if format == "int64" {
return "int64"
} else {
return "int32"
}
default:
return name
}
}
func typeForSchema(schema *openapi.Schema) (typeName string) {
ref := schema.XRef
if ref != "" {
return typeForRef(ref)
}
if schema.Type != nil {
types := schema.Type.Value
format := schema.Format
if len(types) == 1 && types[0] == "string" {
return "string"
}
if len(types) == 1 && types[0] == "integer" && format == "int32" {
return "int32"
}
if len(types) == 1 && types[0] == "integer" {
return "int"
}
if len(types) == 1 && types[0] == "number" {
return "int"
}
if len(types) == 1 && types[0] == "array" && schema.Items != nil {
// we have an array.., but of what?
items := schema.Items.Schema
if len(items) == 1 && items[0].XRef != "" {
return "[]" + typeForRef(items[0].XRef)
}
}
if len(types) == 1 && types[0] == "object" && schema.AdditionalProperties == nil {
return "map[string]interface{}"
}
}
if schema.AdditionalProperties != nil {
additionalProperties := schema.AdditionalProperties
if propertySchema := additionalProperties.GetSchema(); propertySchema != nil {
if ref := propertySchema.XRef; ref != "" {
return "map[string]" + typeForRef(ref)
}
}
}
// this function is incomplete... so return a string representing anything that we don't handle
return fmt.Sprintf("%v", schema)
}
func typeForRef(ref string) (typeName string) {
return strings.Replace(strings.Title(path.Base(ref)), "-", "_", -1)
}
func propertyNameForResponseCode(code string) string {
if code == "200" {
return "OK"
} else {
return strings.Title(code)
}
}
// Run the renderer to generate the named files.
func (renderer *ServiceRenderer) Generate(response *plugins.Response, files []string) (err error) {
for _, filename := range files {
file := &plugins.File{}
file.Name = filename
f := new(bytes.Buffer)
t := renderer.Templates[filename]
log.Printf("Generating %s", filename)
err = t.Execute(f, struct {
Renderer *ServiceRenderer
}{
renderer,
})
if err != nil {
response.Errors = append(response.Errors, fmt.Sprintf("ERROR %v", err))
}
inputBytes := f.Bytes()
// run generated Go files through gofmt
if filepath.Ext(file.Name) == ".go" {
strippedBytes := stripMarkers(inputBytes)
file.Data, err = gofmt(file.Name, strippedBytes)
} else {
file.Data = inputBytes
}
response.Files = append(response.Files, file)
}
return
}
func replaceReservedWords(name string) string {
log.Printf("replacing %s\n", name)
if name == "type" {
return "ttttype"
}
return name
}

View file

@ -0,0 +1,24 @@
// 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 templates() map[string]string {
return map[string]string{
"client.go": "Ly8gR0VORVJBVEVEIEZJTEU6IERPIE5PVCBFRElUIQoKcGFja2FnZSB7ey5SZW5kZXJlci5QYWNrYWdlfX0KCmltcG9ydCAoCiAgImJ5dGVzIgogICJlcnJvcnMiCiAgImVuY29kaW5nL2pzb24iCiAgImZtdCIKICAibmV0L2h0dHAiCiAgInN0cmluZ3MiCikKICAKLy8gQVBJIGNsaWVudCByZXByZXNlbnRhdGlvbi4KdHlwZSBDbGllbnQgc3RydWN0IHsKCXNlcnZpY2Ugc3RyaW5nCn0gCgovLyBDcmVhdGUgYW4gQVBJIGNsaWVudC4KZnVuYyBOZXdDbGllbnQoc2VydmljZSBzdHJpbmcpICpDbGllbnQgewoJY2xpZW50IDo9ICZDbGllbnR7fQoJY2xpZW50LnNlcnZpY2UgPSBzZXJ2aWNlCglyZXR1cm4gY2xpZW50Cn0KCi8vLXt7cmFuZ2UgLlJlbmRlcmVyLk1ldGhvZHN9fQp7e2NvbW1lbnRGb3JUZXh0IC5EZXNjcmlwdGlvbn19Ci8vLXt7aWYgZXEgLlJlc3VsdFR5cGVOYW1lICIifX0KZnVuYyAoY2xpZW50ICpDbGllbnQpIHt7LkNsaWVudE5hbWV9fSh7e3BhcmFtZXRlckxpc3QgLn19KSAoZXJyIGVycm9yKSB7Ci8vLXt7ZWxzZX19CmZ1bmMgKGNsaWVudCAqQ2xpZW50KSB7ey5DbGllbnROYW1lfX0oe3twYXJhbWV0ZXJMaXN0IC59fSkgKHJlc3VsdCAqe3suUmVzdWx0VHlwZU5hbWV9fSwgZXJyIGVycm9yKSB7Ci8vLXt7ZW5kfX0KCXBhdGggOj0gY2xpZW50LnNlcnZpY2UgKyAie3suUGF0aH19IgoJLy8te3tpZiBoYXNQYXJhbWV0ZXJzIC59fQoJLy8te3tyYW5nZSAuUGFyYW1ldGVyc1R5cGUuRmllbGRzfX0JCgkvLy17e2lmIGVxIC5Qb3NpdGlvbiAicGF0aCJ9fQoJcGF0aCA9IHN0cmluZ3MuUmVwbGFjZShwYXRoLCAieyIgKyAie3suSlNPTk5hbWV9fSIgKyAifSIsIGZtdC5TcHJpbnRmKCIldiIsIHt7LkpTT05OYW1lfX0pLCAxKQoJLy8te3tlbmR9fQoJLy8te3tlbmR9fQoJLy8te3tlbmR9fQoJLy8te3tpZiBlcSAuTWV0aG9kICJQT1NUIn19Cglib2R5IDo9IG5ldyhieXRlcy5CdWZmZXIpCglqc29uLk5ld0VuY29kZXIoYm9keSkuRW5jb2RlKHt7Ym9keVBhcmFtZXRlck5hbWUgLn19KQoJcmVxLCBlcnIgOj0gaHR0cC5OZXdSZXF1ZXN0KCJ7ey5NZXRob2R9fSIsIHBhdGgsIGJvZHkpCgkvLy17e2Vsc2V9fQoJcmVxLCBlcnIgOj0gaHR0cC5OZXdSZXF1ZXN0KCJ7ey5NZXRob2R9fSIsIHBhdGgsIG5pbCkKCS8vLXt7ZW5kfX0KCWlmIGVyciAhPSBuaWwgewoJCXJldHVybgoJfQoJcmVzcCwgZXJyIDo9IGh0dHAuRGVmYXVsdENsaWVudC5EbyhyZXEpCglpZiBlcnIgIT0gbmlsIHsKCQlyZXR1cm4KCX0KCWlmIHJlc3AuU3RhdHVzQ29kZSA9PSAyMDAgewoJCWRlZmVyIHJlc3AuQm9keS5DbG9zZSgpCgkJLy8te3tpZiBuZSAuUmVzdWx0VHlwZU5hbWUgIiJ9fQoJCWRlY29kZXIgOj0ganNvbi5OZXdEZWNvZGVyKHJlc3AuQm9keSkKCQlyZXN1bHQgPSAme3suUmVzdWx0VHlwZU5hbWV9fXt9CgkJZGVjb2Rlci5EZWNvZGUocmVzdWx0KQoJCS8vLXt7ZW5kfX0KCX0gZWxzZSB7CgkJZXJyID0gZXJyb3JzLk5ldyhyZXNwLlN0YXR1cykKCX0KCXJldHVybgp9CgovLy17e2VuZH19CgovLyByZWZlciB0byBpbXBvcnRlZCBwYWNrYWdlcyB0aGF0IG1heSBvciBtYXkgbm90IGJlIHVzZWQgaW4gZ2VuZXJhdGVkIGNvZGUKZnVuYyBmb3JjZWRfcGFja2FnZV9yZWZlcmVuY2VzKCkgewoJXyA9IG5ldyhieXRlcy5CdWZmZXIpCglfID0gZm10LlNwcmludGYoIiIpCglfID0gc3RyaW5ncy5TcGxpdCgiIiwiIikKfQ==",
"provider.go": "Ly8gR0VORVJBVEVEIEZJTEU6IERPIE5PVCBFRElUIQoKcGFja2FnZSB7ey5SZW5kZXJlci5QYWNrYWdlfX0KCi8vIFRvIGNyZWF0ZSBhIHNlcnZlciwgZmlyc3Qgd3JpdGUgYSBjbGFzcyB0aGF0IGltcGxlbWVudHMgdGhpcyBpbnRlcmZhY2UuCi8vIFRoZW4gcGFzcyBhbiBpbnN0YW5jZSBvZiBpdCB0byBJbml0aWFsaXplKCkuCnR5cGUgUHJvdmlkZXIgaW50ZXJmYWNlIHsKLy8te3tyYW5nZSAuUmVuZGVyZXIuTWV0aG9kc319CgovLyBQcm92aWRlcgp7e2NvbW1lbnRGb3JUZXh0IC5EZXNjcmlwdGlvbn19Ci8vLXt7aWYgaGFzUGFyYW1ldGVycyAufX0KLy8te3tpZiBoYXNSZXNwb25zZXMgLn19CiAge3suUHJvY2Vzc29yTmFtZX19KHBhcmFtZXRlcnMgKnt7LlBhcmFtZXRlcnNUeXBlTmFtZX19LCByZXNwb25zZXMgKnt7LlJlc3BvbnNlc1R5cGVOYW1lfX0pIChlcnIgZXJyb3IpCi8vLXt7ZWxzZX19CiAge3suUHJvY2Vzc29yTmFtZX19KHBhcmFtZXRlcnMgKnt7LlBhcmFtZXRlcnNUeXBlTmFtZX19KSAoZXJyIGVycm9yKQovLy17e2VuZH19Ci8vLXt7ZWxzZX19Ci8vLXt7aWYgaGFzUmVzcG9uc2VzIC59fQogIHt7LlByb2Nlc3Nvck5hbWV9fShyZXNwb25zZXMgKnt7LlJlc3BvbnNlc1R5cGVOYW1lfX0pIChlcnIgZXJyb3IpCi8vLXt7ZWxzZX19CiAge3suUHJvY2Vzc29yTmFtZX19KCkgKGVyciBlcnJvcikKLy8te3tlbmR9fQovLy17e2VuZH19CQovLy17e2VuZH19Cn0K",
"server.go": "Ly8gR0VORVJBVEVEIEZJTEU6IERPIE5PVCBFRElUIQoKcGFja2FnZSB7ey5SZW5kZXJlci5QYWNrYWdlfX0KCmltcG9ydCAoCgkiZW5jb2RpbmcvanNvbiIKCSJlcnJvcnMiCgkibmV0L2h0dHAiCgkic3RyY29udiIKCgkiZ2l0aHViLmNvbS9nb3JpbGxhL211eCIKKQoKZnVuYyBpbnRWYWx1ZShzIHN0cmluZykgKHYgaW50NjQpIHsKCXYsIF8gPSBzdHJjb252LlBhcnNlSW50KHMsIDEwLCA2NCkKCXJldHVybiB2Cn0KCi8vIFRoaXMgcGFja2FnZS1nbG9iYWwgdmFyaWFibGUgaG9sZHMgdGhlIHVzZXItd3JpdHRlbiBQcm92aWRlciBmb3IgQVBJIHNlcnZpY2VzLgovLyBTZWUgdGhlIFByb3ZpZGVyIGludGVyZmFjZSBmb3IgZGV0YWlscy4KdmFyIHByb3ZpZGVyIFByb3ZpZGVyCgovLyBUaGVzZSBoYW5kbGVycyBzZXJ2ZSBBUEkgbWV0aG9kcy4KLy8te3tyYW5nZSAuUmVuZGVyZXIuTWV0aG9kc319CgovLyBIYW5kbGVyCnt7Y29tbWVudEZvclRleHQgLkRlc2NyaXB0aW9ufX0KZnVuYyB7ey5IYW5kbGVyTmFtZX19KHcgaHR0cC5SZXNwb25zZVdyaXRlciwgciAqaHR0cC5SZXF1ZXN0KSB7Cgl2YXIgZXJyIGVycm9yCgkvLy17e2lmIGhhc1BhcmFtZXRlcnMgLn19CgkvLyBpbnN0YW50aWF0ZSB0aGUgcGFyYW1ldGVycyBzdHJ1Y3R1cmUKCXZhciBwYXJhbWV0ZXJzIHt7LlBhcmFtZXRlcnNUeXBlTmFtZX19CgkvLy17e2lmIGVxIC5NZXRob2QgIlBPU1QifX0KCS8vIGRlc2VyaWFsaXplIHJlcXVlc3QgZnJvbSBwb3N0IGRhdGEKCWRlY29kZXIgOj0ganNvbi5OZXdEZWNvZGVyKHIuQm9keSkKCWVyciA9IGRlY29kZXIuRGVjb2RlKCZwYXJhbWV0ZXJzLnt7Ym9keVBhcmFtZXRlckZpZWxkTmFtZSAufX0pCglpZiBlcnIgIT0gbmlsIHsKCQl3LldyaXRlSGVhZGVyKGh0dHAuU3RhdHVzQmFkUmVxdWVzdCkKCQl3LldyaXRlKFtdYnl0ZShlcnIuRXJyb3IoKSArICJcbiIpKQoJCXJldHVybgoJfQoJLy8te3tlbmR9fQoJLy8gZ2V0IHJlcXVlc3QgZmllbGRzIGluIHBhdGggYW5kIHF1ZXJ5IHBhcmFtZXRlcnMKCS8vLXt7aWYgaGFzUGF0aFBhcmFtZXRlcnMgLn19Cgl2YXJzIDo9IG11eC5WYXJzKHIpCgkvLy17e2VuZH19CgkvLy17e2lmIGhhc0Zvcm1QYXJhbWV0ZXJzIC59fQoJci5QYXJzZUZvcm0oKQoJLy8te3tlbmR9fQoJLy8te3tyYW5nZSAuUGFyYW1ldGVyc1R5cGUuRmllbGRzfX0JCgkvLy17e2lmIGVxIC5Qb3NpdGlvbiAicGF0aCJ9fQoJaWYgdmFsdWUsIG9rIDo9IHZhcnNbInt7LkpTT05OYW1lfX0iXTsgb2sgewoJCXBhcmFtZXRlcnMue3suTmFtZX19ID0gaW50VmFsdWUodmFsdWUpCgl9CgkvLy17e2VuZH19CQoJLy8te3tpZiBlcSAuUG9zaXRpb24gImZvcm1kYXRhIn19CglpZiBsZW4oci5Gb3JtWyJ7ey5KU09OTmFtZX19Il0pID4gMCB7CgkJcGFyYW1ldGVycy57ey5OYW1lfX0gPSBpbnRWYWx1ZShyLkZvcm1bInt7LkpTT05OYW1lfX0iXVswXSkKCX0KCS8vLXt7ZW5kfX0KCS8vLXt7ZW5kfX0KCS8vLXt7ZW5kfX0KCS8vLXt7aWYgaGFzUmVzcG9uc2VzIC59fQkKCS8vIGluc3RhbnRpYXRlIHRoZSByZXNwb25zZXMgc3RydWN0dXJlCgl2YXIgcmVzcG9uc2VzIHt7LlJlc3BvbnNlc1R5cGVOYW1lfX0KCS8vLXt7ZW5kfX0KCS8vIGNhbGwgdGhlIHNlcnZpY2UgcHJvdmlkZXIJCgkvLy17e2lmIGhhc1BhcmFtZXRlcnMgLn19CgkvLy17e2lmIGhhc1Jlc3BvbnNlcyAufX0KCWVyciA9IHByb3ZpZGVyLnt7LlByb2Nlc3Nvck5hbWV9fSgmcGFyYW1ldGVycywgJnJlc3BvbnNlcykKCS8vLXt7ZWxzZX19CgllcnIgPSBwcm92aWRlci57ey5Qcm9jZXNzb3JOYW1lfX0oJnBhcmFtZXRlcnMpCgkvLy17e2VuZH19CgkvLy17e2Vsc2V9fQoJLy8te3tpZiBoYXNSZXNwb25zZXMgLn19CgllcnIgPSBwcm92aWRlci57ey5Qcm9jZXNzb3JOYW1lfX0oJnJlc3BvbnNlcykKCS8vLXt7ZWxzZX19CgllcnIgPSBwcm92aWRlci57ey5Qcm9jZXNzb3JOYW1lfX0oKQoJLy8te3tlbmR9fQoJLy8te3tlbmR9fQkKCWlmIGVyciA9PSBuaWwgewoJLy8te3sgaWYgaGFzUmVzcG9uc2VzIC59fQoJCS8vLXt7IGlmIC5SZXNwb25zZXNUeXBlIHwgaGFzRmllbGROYW1lZE9LIH19CQkKCQlpZiByZXNwb25zZXMuT0sgIT0gbmlsIHsKCQkJLy8gd3JpdGUgdGhlIG5vcm1hbCByZXNwb25zZQoJCQllbmNvZGVyIDo9IGpzb24uTmV3RW5jb2Rlcih3KQoJCQllbmNvZGVyLkVuY29kZShyZXNwb25zZXMuT0spCgkJCXJldHVybgoJCX0gCgkJLy8te3tlbmR9fQoJCS8vLXt7IGlmIC5SZXNwb25zZXNUeXBlIHwgaGFzRmllbGROYW1lZERlZmF1bHQgfX0JCQoJCWlmIHJlc3BvbnNlcy5EZWZhdWx0ICE9IG5pbCB7CgkJCXcuV3JpdGVIZWFkZXIoaW50KHJlc3BvbnNlcy5EZWZhdWx0LkNvZGUpKQoJCQl3LldyaXRlKFtdYnl0ZShyZXNwb25zZXMuRGVmYXVsdC5NZXNzYWdlICsgIlxuIikpCgkJCXJldHVybgoJCX0KCQkvLy17e2VuZH19CgkvLy17e2VuZH19Cgl9IGVsc2UgewoJCXcuV3JpdGVIZWFkZXIoaHR0cC5TdGF0dXNJbnRlcm5hbFNlcnZlckVycm9yKQoJCXcuV3JpdGUoW11ieXRlKGVyci5FcnJvcigpICsgIlxuIikpCgkJcmV0dXJuCgl9Cn0KLy8te3tlbmR9fQoKLy8gSW5pdGlhbGl6ZSB0aGUgQVBJIHNlcnZpY2UuCmZ1bmMgSW5pdGlhbGl6ZShwIFByb3ZpZGVyKSB7Cglwcm92aWRlciA9IHAKCXZhciByb3V0ZXIgPSBtdXguTmV3Um91dGVyKCl7e3JhbmdlIC5SZW5kZXJlci5NZXRob2RzfX0KCXJvdXRlci5IYW5kbGVGdW5jKCJ7ey5QYXRofX0iLCB7ey5IYW5kbGVyTmFtZX19KS5NZXRob2RzKCJ7ey5NZXRob2R9fSIpe3tlbmR9fQoJaHR0cC5IYW5kbGUoIi8iLCByb3V0ZXIpCn0KCi8vIFByb3ZpZGUgdGhlIEFQSSBzZXJ2aWNlIG92ZXIgSFRUUC4KZnVuYyBTZXJ2ZUhUVFAoYWRkcmVzcyBzdHJpbmcpIGVycm9yIHsKCWlmIHByb3ZpZGVyID09IG5pbCB7CgkJcmV0dXJuIGVycm9ycy5OZXcoIlVzZSB7ey5SZW5kZXJlci5QYWNrYWdlfX0uSW5pdGlhbGl6ZSgpIHRvIHNldCBhIHNlcnZpY2UgcHJvdmlkZXIuIikKCX0KCXJldHVybiBodHRwLkxpc3RlbkFuZFNlcnZlKGFkZHJlc3MsIG5pbCkKfQo=",
"types.go": "Ly8gR0VORVJBVEVEIEZJTEU6IERPIE5PVCBFRElUIQoKcGFja2FnZSB7ey5SZW5kZXJlci5QYWNrYWdlfX0KCi8vIFR5cGVzIHVzZWQgYnkgdGhlIEFQSS4KLy8te3tyYW5nZSAuUmVuZGVyZXIuVHlwZXN9fQoKLy8te3tpZiBlcSAuS2luZCAic3RydWN0In19CnR5cGUge3suTmFtZX19IHN0cnVjdCB7IAovLy17e3JhbmdlIC5GaWVsZHN9fQogIHt7Lk5hbWV9fSB7e2dvVHlwZSAuVHlwZX19e3tpZiBuZSAuSlNPTk5hbWUgIiJ9fSBganNvbjoie3suSlNPTk5hbWV9fSJgCi8vLXt7ZW5kfX0KLy8te3tlbmR9fQp9Ci8vLXt7ZWxzZX19CnR5cGUge3suTmFtZX19IHt7LktpbmR9fQovLy17e2VuZH19CgovLy17e2VuZH19",
}
}

View file

@ -0,0 +1,75 @@
// GENERATED FILE: DO NOT EDIT!
package {{.Renderer.Package}}
import (
"bytes"
"errors"
"encoding/json"
"fmt"
"net/http"
"strings"
)
// API client representation.
type Client struct {
service string
}
// Create an API client.
func NewClient(service string) *Client {
client := &Client{}
client.service = service
return client
}
//-{{range .Renderer.Methods}}
{{commentForText .Description}}
//-{{if eq .ResultTypeName ""}}
func (client *Client) {{.ClientName}}({{parameterList .}}) (err error) {
//-{{else}}
func (client *Client) {{.ClientName}}({{parameterList .}}) (result *{{.ResultTypeName}}, err error) {
//-{{end}}
path := client.service + "{{.Path}}"
//-{{if hasParameters .}}
//-{{range .ParametersType.Fields}}
//-{{if eq .Position "path"}}
path = strings.Replace(path, "{" + "{{.JSONName}}" + "}", fmt.Sprintf("%v", {{.JSONName}}), 1)
//-{{end}}
//-{{end}}
//-{{end}}
//-{{if eq .Method "POST"}}
body := new(bytes.Buffer)
json.NewEncoder(body).Encode({{bodyParameterName .}})
req, err := http.NewRequest("{{.Method}}", path, body)
//-{{else}}
req, err := http.NewRequest("{{.Method}}", path, nil)
//-{{end}}
if err != nil {
return
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return
}
if resp.StatusCode == 200 {
defer resp.Body.Close()
//-{{if ne .ResultTypeName ""}}
decoder := json.NewDecoder(resp.Body)
result = &{{.ResultTypeName}}{}
decoder.Decode(result)
//-{{end}}
} else {
err = errors.New(resp.Status)
}
return
}
//-{{end}}
// refer to imported packages that may or may not be used in generated code
func forced_package_references() {
_ = new(bytes.Buffer)
_ = fmt.Sprintf("")
_ = strings.Split("","")
}

View file

@ -0,0 +1,26 @@
// GENERATED FILE: DO NOT EDIT!
package {{.Renderer.Package}}
// To create a server, first write a class that implements this interface.
// Then pass an instance of it to Initialize().
type Provider interface {
//-{{range .Renderer.Methods}}
// Provider
{{commentForText .Description}}
//-{{if hasParameters .}}
//-{{if hasResponses .}}
{{.ProcessorName}}(parameters *{{.ParametersTypeName}}, responses *{{.ResponsesTypeName}}) (err error)
//-{{else}}
{{.ProcessorName}}(parameters *{{.ParametersTypeName}}) (err error)
//-{{end}}
//-{{else}}
//-{{if hasResponses .}}
{{.ProcessorName}}(responses *{{.ResponsesTypeName}}) (err error)
//-{{else}}
{{.ProcessorName}}() (err error)
//-{{end}}
//-{{end}}
//-{{end}}
}

View file

@ -0,0 +1,121 @@
// GENERATED FILE: DO NOT EDIT!
package {{.Renderer.Package}}
import (
"encoding/json"
"errors"
"net/http"
"strconv"
"github.com/gorilla/mux"
)
func intValue(s string) (v int64) {
v, _ = strconv.ParseInt(s, 10, 64)
return v
}
// This package-global variable holds the user-written Provider for API services.
// See the Provider interface for details.
var provider Provider
// These handlers serve API methods.
//-{{range .Renderer.Methods}}
// Handler
{{commentForText .Description}}
func {{.HandlerName}}(w http.ResponseWriter, r *http.Request) {
var err error
//-{{if hasParameters .}}
// instantiate the parameters structure
var parameters {{.ParametersTypeName}}
//-{{if eq .Method "POST"}}
// deserialize request from post data
decoder := json.NewDecoder(r.Body)
err = decoder.Decode(&parameters.{{bodyParameterFieldName .}})
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(err.Error() + "\n"))
return
}
//-{{end}}
// get request fields in path and query parameters
//-{{if hasPathParameters .}}
vars := mux.Vars(r)
//-{{end}}
//-{{if hasFormParameters .}}
r.ParseForm()
//-{{end}}
//-{{range .ParametersType.Fields}}
//-{{if eq .Position "path"}}
if value, ok := vars["{{.JSONName}}"]; ok {
parameters.{{.Name}} = intValue(value)
}
//-{{end}}
//-{{if eq .Position "formdata"}}
if len(r.Form["{{.JSONName}}"]) > 0 {
parameters.{{.Name}} = intValue(r.Form["{{.JSONName}}"][0])
}
//-{{end}}
//-{{end}}
//-{{end}}
//-{{if hasResponses .}}
// instantiate the responses structure
var responses {{.ResponsesTypeName}}
//-{{end}}
// call the service provider
//-{{if hasParameters .}}
//-{{if hasResponses .}}
err = provider.{{.ProcessorName}}(&parameters, &responses)
//-{{else}}
err = provider.{{.ProcessorName}}(&parameters)
//-{{end}}
//-{{else}}
//-{{if hasResponses .}}
err = provider.{{.ProcessorName}}(&responses)
//-{{else}}
err = provider.{{.ProcessorName}}()
//-{{end}}
//-{{end}}
if err == nil {
//-{{ if hasResponses .}}
//-{{ if .ResponsesType | hasFieldNamedOK }}
if responses.OK != nil {
// write the normal response
encoder := json.NewEncoder(w)
encoder.Encode(responses.OK)
return
}
//-{{end}}
//-{{ if .ResponsesType | hasFieldNamedDefault }}
if responses.Default != nil {
w.WriteHeader(int(responses.Default.Code))
w.Write([]byte(responses.Default.Message + "\n"))
return
}
//-{{end}}
//-{{end}}
} else {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error() + "\n"))
return
}
}
//-{{end}}
// Initialize the API service.
func Initialize(p Provider) {
provider = p
var router = mux.NewRouter(){{range .Renderer.Methods}}
router.HandleFunc("{{.Path}}", {{.HandlerName}}).Methods("{{.Method}}"){{end}}
http.Handle("/", router)
}
// Provide the API service over HTTP.
func ServeHTTP(address string) error {
if provider == nil {
return errors.New("Use {{.Renderer.Package}}.Initialize() to set a service provider.")
}
return http.ListenAndServe(address, nil)
}

View file

@ -0,0 +1,19 @@
// GENERATED FILE: DO NOT EDIT!
package {{.Renderer.Package}}
// Types used by the API.
//-{{range .Renderer.Types}}
//-{{if eq .Kind "struct"}}
type {{.Name}} struct {
//-{{range .Fields}}
{{.Name}} {{goType .Type}}{{if ne .JSONName ""}} `json:"{{.JSONName}}"`
//-{{end}}
//-{{end}}
}
//-{{else}}
type {{.Name}} {{.Kind}}
//-{{end}}
//-{{end}}

View file

@ -0,0 +1,105 @@
// 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_sample is a sample Gnostic plugin written in Go.
package main
import (
"io/ioutil"
"os"
"github.com/golang/protobuf/proto"
"github.com/googleapis/gnostic/printer"
openapi "github.com/googleapis/gnostic/OpenAPIv2"
plugins "github.com/googleapis/gnostic/plugins"
)
// generate a simple report of an OpenAPI document's contents
func printDocument(code *printer.Code, document *openapi.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()
}
// record an error, then serialize and return the response
func sendAndExitIfError(err error, response *plugins.Response) {
if err != nil {
response.Errors = append(response.Errors, err.Error())
sendAndExit(response)
}
}
// serialize and return the response
func sendAndExit(response *plugins.Response) {
responseBytes, _ := proto.Marshal(response)
os.Stdout.Write(responseBytes)
os.Exit(0)
}
func main() {
// initialize the response
response := &plugins.Response{}
// read and deserialize the request
data, err := ioutil.ReadAll(os.Stdin)
sendAndExitIfError(err, response)
request := &plugins.Request{}
err = proto.Unmarshal(data, request)
sendAndExitIfError(err, response)
wrapper := request.Wrapper
document := &openapi.Document{}
err = proto.Unmarshal(wrapper.Value, document)
sendAndExitIfError(err, response)
// generate report
code := &printer.Code{}
code.Print("READING %s (%s)", wrapper.Name, wrapper.Version)
printDocument(code, document)
file := &plugins.File{}
file.Name = "report.txt"
file.Data = []byte(code.String())
response.Files = append(response.Files, file)
// send with success
sendAndExit(response)
}