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,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,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)
}

View file

@ -0,0 +1,287 @@
// Code generated by protoc-gen-go.
// source: plugins/plugin.proto
// DO NOT EDIT!
/*
Package openapi_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
Response
File
Wrapper
*/
package openapi_plugin_v1
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
// 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
// The version number of OpenAPI compiler.
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) the OpenAPI compiler.
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 {
// A wrapped OpenAPI document to process.
Wrapper *Wrapper `protobuf:"bytes,1,opt,name=wrapper" json:"wrapper,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 openapi compiler.
CompilerVersion *Version `protobuf:"bytes,4,opt,name=compiler_version,json=compilerVersion" json:"compiler_version,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) GetWrapper() *Wrapper {
if m != nil {
return m.Wrapper
}
return nil
}
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
}
// 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 openapic
// 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 openapic to an appropriate location.
Files []*File `protobuf:"bytes,2,rep,name=files" json:"files,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{3} }
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
}
// 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{4} }
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
}
// Wrapper wraps an OpenAPI document with its version.
type Wrapper struct {
// filename or URL of the wrapped document
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
// version of the OpenAPI specification that is used by the wrapped document
Version string `protobuf:"bytes,2,opt,name=version" json:"version,omitempty"`
// valid serialized protocol buffer of the named OpenAPI specification version
Value []byte `protobuf:"bytes,3,opt,name=value,proto3" json:"value,omitempty"`
}
func (m *Wrapper) Reset() { *m = Wrapper{} }
func (m *Wrapper) String() string { return proto.CompactTextString(m) }
func (*Wrapper) ProtoMessage() {}
func (*Wrapper) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} }
func (m *Wrapper) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *Wrapper) GetVersion() string {
if m != nil {
return m.Version
}
return ""
}
func (m *Wrapper) GetValue() []byte {
if m != nil {
return m.Value
}
return nil
}
func init() {
proto.RegisterType((*Version)(nil), "openapi.plugin.v1.Version")
proto.RegisterType((*Parameter)(nil), "openapi.plugin.v1.Parameter")
proto.RegisterType((*Request)(nil), "openapi.plugin.v1.Request")
proto.RegisterType((*Response)(nil), "openapi.plugin.v1.Response")
proto.RegisterType((*File)(nil), "openapi.plugin.v1.File")
proto.RegisterType((*Wrapper)(nil), "openapi.plugin.v1.Wrapper")
}
func init() { proto.RegisterFile("plugins/plugin.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
// 390 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x6c, 0x92, 0x41, 0x8f, 0xd3, 0x30,
0x10, 0x85, 0x95, 0x26, 0xdd, 0x90, 0x29, 0xd2, 0x82, 0xb5, 0x80, 0x85, 0x90, 0x88, 0x72, 0xea,
0x85, 0xc0, 0x2e, 0x70, 0xe3, 0xd2, 0x56, 0x20, 0x71, 0x40, 0x0d, 0x3e, 0xc0, 0xb1, 0x32, 0x61,
0xda, 0x1a, 0x25, 0xb1, 0xb1, 0x9d, 0xc0, 0xef, 0xe1, 0xbf, 0xf1, 0x3f, 0x50, 0x6c, 0x07, 0x90,
0x36, 0xa7, 0xfa, 0x7b, 0x1a, 0x3f, 0xbf, 0x79, 0x0d, 0x5c, 0xa9, 0xa6, 0x3f, 0x89, 0xce, 0x3c,
0xf7, 0xbf, 0xa5, 0xd2, 0xd2, 0x4a, 0x72, 0x5f, 0x2a, 0xec, 0xb8, 0x12, 0x65, 0x50, 0x87, 0xeb,
0xa2, 0x86, 0xf4, 0x13, 0x6a, 0x23, 0x64, 0x47, 0xae, 0x60, 0xd9, 0xf2, 0x6f, 0x52, 0xd3, 0x28,
0x8f, 0xd6, 0x4b, 0xe6, 0xc1, 0xa9, 0xa2, 0x93, 0x9a, 0x2e, 0x82, 0x3a, 0xc2, 0xa8, 0x2a, 0x6e,
0xeb, 0x33, 0x8d, 0xbd, 0xea, 0x80, 0x3c, 0x84, 0x0b, 0xd3, 0x1f, 0x8f, 0xe2, 0x27, 0x4d, 0xf2,
0x68, 0x9d, 0xb1, 0x40, 0xc5, 0x6b, 0xc8, 0x2a, 0xae, 0x79, 0x8b, 0x16, 0x35, 0x21, 0x90, 0x74,
0xbc, 0x45, 0xf7, 0x4a, 0xc6, 0xdc, 0x79, 0xb4, 0x1b, 0x78, 0xd3, 0xa3, 0x7b, 0x24, 0x63, 0x1e,
0x8a, 0xdf, 0x11, 0xa4, 0x0c, 0xbf, 0xf7, 0x68, 0x2c, 0x79, 0x05, 0xe9, 0x0f, 0xcd, 0x95, 0x42,
0x1f, 0x6f, 0x75, 0xf3, 0xb8, 0xbc, 0xb5, 0x4c, 0xf9, 0xd9, 0x4f, 0xb0, 0x69, 0x94, 0x3c, 0x85,
0x95, 0xec, 0xad, 0xea, 0xed, 0x41, 0x71, 0x7b, 0x0e, 0xee, 0xe0, 0xa5, 0x8a, 0xdb, 0x33, 0x79,
0x03, 0xa0, 0xa6, 0x64, 0x86, 0xc6, 0x79, 0xbc, 0x5e, 0xdd, 0x3c, 0x99, 0x71, 0xfe, 0x1b, 0x9f,
0xfd, 0x37, 0x4f, 0xde, 0xc2, 0xbd, 0x5a, 0xb6, 0x4a, 0x34, 0xa8, 0x0f, 0x83, 0x6f, 0xd1, 0x6d,
0x3e, 0x9f, 0x2e, 0xf4, 0xcc, 0x2e, 0xa7, 0x3b, 0x41, 0x28, 0x3e, 0xc2, 0x1d, 0x86, 0x46, 0xc9,
0xce, 0xe0, 0x58, 0x21, 0x6a, 0x2d, 0xb5, 0xa1, 0x51, 0x1e, 0x8f, 0x15, 0x7a, 0x22, 0xcf, 0x60,
0x79, 0x14, 0x0d, 0x1a, 0xba, 0x70, 0x19, 0x1f, 0xcd, 0xf8, 0xbf, 0x13, 0x0d, 0x32, 0x3f, 0x55,
0x94, 0x90, 0x8c, 0x38, 0x5b, 0x36, 0x81, 0xe4, 0x2b, 0xb7, 0xdc, 0xb5, 0x71, 0x97, 0xb9, 0x73,
0xf1, 0x01, 0xd2, 0x50, 0xde, 0xec, 0x15, 0x0a, 0xe9, 0xb4, 0x9f, 0xef, 0x70, 0xc2, 0x7f, 0xff,
0x5c, 0xec, 0xdc, 0x3c, 0x6c, 0x5f, 0xc0, 0xa5, 0xd4, 0xa7, 0x29, 0x63, 0x5d, 0x0e, 0xd7, 0xdb,
0x07, 0x7b, 0x85, 0xdd, 0xa6, 0x7a, 0xbf, 0x0b, 0xcb, 0x57, 0x2e, 0x77, 0x15, 0xfd, 0x5a, 0xc4,
0xfb, 0xcd, 0xee, 0xcb, 0x85, 0xfb, 0x42, 0x5f, 0xfe, 0x09, 0x00, 0x00, 0xff, 0xff, 0x51, 0x4d,
0xec, 0x80, 0xb9, 0x02, 0x00, 0x00,
}

View file

@ -0,0 +1,517 @@
/*
* DO NOT EDIT.
*
* Generated by the protocol buffer compiler.
* Source: plugin.proto
*
*/
// Copyright 2016 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.
// openapic (aka the OpenAPI Compiler) can be extended via plugins.
// A plugin is just 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 "openapi_$NAME", and will then be used when the
// flag "--${NAME}_out" is passed to openapic.
import Foundation
import SwiftProtobuf
/// The version number of OpenAPI compiler.
public struct Openapi_Plugin_V1_Version: ProtobufGeneratedMessage {
public var swiftClassName: String {return "Openapi_Plugin_V1_Version"}
public var protoMessageName: String {return "Version"}
public var protoPackageName: String {return "openapi.plugin.v1"}
public var jsonFieldNames: [String: Int] {return [
"major": 1,
"minor": 2,
"patch": 3,
"suffix": 4,
]}
public var protoFieldNames: [String: Int] {return [
"major": 1,
"minor": 2,
"patch": 3,
"suffix": 4,
]}
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 = ""
public init() {}
public init(major: Int32? = nil,
minor: Int32? = nil,
patch: Int32? = nil,
suffix: String? = nil)
{
if let v = major {
self.major = v
}
if let v = minor {
self.minor = v
}
if let v = patch {
self.patch = v
}
if let v = suffix {
self.suffix = v
}
}
public mutating func _protoc_generated_decodeField(setter: inout ProtobufFieldDecoder, protoFieldNumber: Int) throws -> Bool {
let handled: Bool
switch protoFieldNumber {
case 1: handled = try setter.decodeSingularField(fieldType: ProtobufInt32.self, value: &major)
case 2: handled = try setter.decodeSingularField(fieldType: ProtobufInt32.self, value: &minor)
case 3: handled = try setter.decodeSingularField(fieldType: ProtobufInt32.self, value: &patch)
case 4: handled = try setter.decodeSingularField(fieldType: ProtobufString.self, value: &suffix)
default:
handled = false
}
return handled
}
public func _protoc_generated_traverse(visitor: inout ProtobufVisitor) throws {
if major != 0 {
try visitor.visitSingularField(fieldType: ProtobufInt32.self, value: major, protoFieldNumber: 1, protoFieldName: "major", jsonFieldName: "major", swiftFieldName: "major")
}
if minor != 0 {
try visitor.visitSingularField(fieldType: ProtobufInt32.self, value: minor, protoFieldNumber: 2, protoFieldName: "minor", jsonFieldName: "minor", swiftFieldName: "minor")
}
if patch != 0 {
try visitor.visitSingularField(fieldType: ProtobufInt32.self, value: patch, protoFieldNumber: 3, protoFieldName: "patch", jsonFieldName: "patch", swiftFieldName: "patch")
}
if suffix != "" {
try visitor.visitSingularField(fieldType: ProtobufString.self, value: suffix, protoFieldNumber: 4, protoFieldName: "suffix", jsonFieldName: "suffix", swiftFieldName: "suffix")
}
}
public func _protoc_generated_isEqualTo(other: Openapi_Plugin_V1_Version) -> Bool {
if major != other.major {return false}
if minor != other.minor {return false}
if patch != other.patch {return false}
if suffix != other.suffix {return false}
return true
}
}
/// A parameter passed to the plugin from (or through) the OpenAPI compiler.
public struct Openapi_Plugin_V1_Parameter: ProtobufGeneratedMessage {
public var swiftClassName: String {return "Openapi_Plugin_V1_Parameter"}
public var protoMessageName: String {return "Parameter"}
public var protoPackageName: String {return "openapi.plugin.v1"}
public var jsonFieldNames: [String: Int] {return [
"name": 1,
"value": 2,
]}
public var protoFieldNames: [String: Int] {return [
"name": 1,
"value": 2,
]}
/// The name of the parameter as specified in the option string
public var name: String = ""
/// The parameter value as specified in the option string
public var value: String = ""
public init() {}
public init(name: String? = nil,
value: String? = nil)
{
if let v = name {
self.name = v
}
if let v = value {
self.value = v
}
}
public mutating func _protoc_generated_decodeField(setter: inout ProtobufFieldDecoder, protoFieldNumber: Int) throws -> Bool {
let handled: Bool
switch protoFieldNumber {
case 1: handled = try setter.decodeSingularField(fieldType: ProtobufString.self, value: &name)
case 2: handled = try setter.decodeSingularField(fieldType: ProtobufString.self, value: &value)
default:
handled = false
}
return handled
}
public func _protoc_generated_traverse(visitor: inout ProtobufVisitor) throws {
if name != "" {
try visitor.visitSingularField(fieldType: ProtobufString.self, value: name, protoFieldNumber: 1, protoFieldName: "name", jsonFieldName: "name", swiftFieldName: "name")
}
if value != "" {
try visitor.visitSingularField(fieldType: ProtobufString.self, value: value, protoFieldNumber: 2, protoFieldName: "value", jsonFieldName: "value", swiftFieldName: "value")
}
}
public func _protoc_generated_isEqualTo(other: Openapi_Plugin_V1_Parameter) -> Bool {
if name != other.name {return false}
if value != other.value {return false}
return true
}
}
/// An encoded Request is written to the plugin's stdin.
public struct Openapi_Plugin_V1_Request: ProtobufGeneratedMessage {
public var swiftClassName: String {return "Openapi_Plugin_V1_Request"}
public var protoMessageName: String {return "Request"}
public var protoPackageName: String {return "openapi.plugin.v1"}
public var jsonFieldNames: [String: Int] {return [
"wrapper": 1,
"outputPath": 2,
"parameters": 3,
"compilerVersion": 4,
]}
public var protoFieldNames: [String: Int] {return [
"wrapper": 1,
"output_path": 2,
"parameters": 3,
"compiler_version": 4,
]}
private class _StorageClass {
typealias ProtobufExtendedMessage = Openapi_Plugin_V1_Request
var _wrapper: Openapi_Plugin_V1_Wrapper? = nil
var _outputPath: String = ""
var _parameters: [Openapi_Plugin_V1_Parameter] = []
var _compilerVersion: Openapi_Plugin_V1_Version? = nil
init() {}
func decodeField(setter: inout ProtobufFieldDecoder, protoFieldNumber: Int) throws -> Bool {
let handled: Bool
switch protoFieldNumber {
case 1: handled = try setter.decodeSingularMessageField(fieldType: Openapi_Plugin_V1_Wrapper.self, value: &_wrapper)
case 2: handled = try setter.decodeSingularField(fieldType: ProtobufString.self, value: &_outputPath)
case 3: handled = try setter.decodeRepeatedMessageField(fieldType: Openapi_Plugin_V1_Parameter.self, value: &_parameters)
case 4: handled = try setter.decodeSingularMessageField(fieldType: Openapi_Plugin_V1_Version.self, value: &_compilerVersion)
default:
handled = false
}
return handled
}
func traverse(visitor: inout ProtobufVisitor) throws {
if let v = _wrapper {
try visitor.visitSingularMessageField(value: v, protoFieldNumber: 1, protoFieldName: "wrapper", jsonFieldName: "wrapper", swiftFieldName: "wrapper")
}
if _outputPath != "" {
try visitor.visitSingularField(fieldType: ProtobufString.self, value: _outputPath, protoFieldNumber: 2, protoFieldName: "output_path", jsonFieldName: "outputPath", swiftFieldName: "outputPath")
}
if !_parameters.isEmpty {
try visitor.visitRepeatedMessageField(value: _parameters, protoFieldNumber: 3, protoFieldName: "parameters", jsonFieldName: "parameters", swiftFieldName: "parameters")
}
if let v = _compilerVersion {
try visitor.visitSingularMessageField(value: v, protoFieldNumber: 4, protoFieldName: "compiler_version", jsonFieldName: "compilerVersion", swiftFieldName: "compilerVersion")
}
}
func isEqualTo(other: _StorageClass) -> Bool {
if _wrapper != other._wrapper {return false}
if _outputPath != other._outputPath {return false}
if _parameters != other._parameters {return false}
if _compilerVersion != other._compilerVersion {return false}
return true
}
func copy() -> _StorageClass {
let clone = _StorageClass()
clone._wrapper = _wrapper
clone._outputPath = _outputPath
clone._parameters = _parameters
clone._compilerVersion = _compilerVersion
return clone
}
}
private var _storage = _StorageClass()
/// A wrapped OpenAPI document to process.
public var wrapper: Openapi_Plugin_V1_Wrapper {
get {return _storage._wrapper ?? Openapi_Plugin_V1_Wrapper()}
set {_uniqueStorage()._wrapper = 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: [Openapi_Plugin_V1_Parameter] {
get {return _storage._parameters}
set {_uniqueStorage()._parameters = newValue}
}
/// The version number of openapi compiler.
public var compilerVersion: Openapi_Plugin_V1_Version {
get {return _storage._compilerVersion ?? Openapi_Plugin_V1_Version()}
set {_uniqueStorage()._compilerVersion = newValue}
}
public init() {}
public init(wrapper: Openapi_Plugin_V1_Wrapper? = nil,
outputPath: String? = nil,
parameters: [Openapi_Plugin_V1_Parameter] = [],
compilerVersion: Openapi_Plugin_V1_Version? = nil)
{
let storage = _uniqueStorage()
storage._wrapper = wrapper
if let v = outputPath {
storage._outputPath = v
}
if !parameters.isEmpty {
storage._parameters = parameters
}
storage._compilerVersion = compilerVersion
}
public mutating func _protoc_generated_decodeField(setter: inout ProtobufFieldDecoder, protoFieldNumber: Int) throws -> Bool {
return try _uniqueStorage().decodeField(setter: &setter, protoFieldNumber: protoFieldNumber)
}
public func _protoc_generated_traverse(visitor: inout ProtobufVisitor) throws {
try _storage.traverse(visitor: &visitor)
}
public func _protoc_generated_isEqualTo(other: Openapi_Plugin_V1_Request) -> Bool {
return _storage === other._storage || _storage.isEqualTo(other: other._storage)
}
private mutating func _uniqueStorage() -> _StorageClass {
if !isKnownUniquelyReferenced(&_storage) {
_storage = _storage.copy()
}
return _storage
}
}
/// The plugin writes an encoded Response to stdout.
public struct Openapi_Plugin_V1_Response: ProtobufGeneratedMessage {
public var swiftClassName: String {return "Openapi_Plugin_V1_Response"}
public var protoMessageName: String {return "Response"}
public var protoPackageName: String {return "openapi.plugin.v1"}
public var jsonFieldNames: [String: Int] {return [
"errors": 1,
"files": 2,
]}
public var protoFieldNames: [String: Int] {return [
"errors": 1,
"files": 2,
]}
/// 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 openapic
/// 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 openapic to an appropriate location.
public var files: [Openapi_Plugin_V1_File] = []
public init() {}
public init(errors: [String] = [],
files: [Openapi_Plugin_V1_File] = [])
{
if !errors.isEmpty {
self.errors = errors
}
if !files.isEmpty {
self.files = files
}
}
public mutating func _protoc_generated_decodeField(setter: inout ProtobufFieldDecoder, protoFieldNumber: Int) throws -> Bool {
let handled: Bool
switch protoFieldNumber {
case 1: handled = try setter.decodeRepeatedField(fieldType: ProtobufString.self, value: &errors)
case 2: handled = try setter.decodeRepeatedMessageField(fieldType: Openapi_Plugin_V1_File.self, value: &files)
default:
handled = false
}
return handled
}
public func _protoc_generated_traverse(visitor: inout ProtobufVisitor) throws {
if !errors.isEmpty {
try visitor.visitRepeatedField(fieldType: ProtobufString.self, value: errors, protoFieldNumber: 1, protoFieldName: "errors", jsonFieldName: "errors", swiftFieldName: "errors")
}
if !files.isEmpty {
try visitor.visitRepeatedMessageField(value: files, protoFieldNumber: 2, protoFieldName: "files", jsonFieldName: "files", swiftFieldName: "files")
}
}
public func _protoc_generated_isEqualTo(other: Openapi_Plugin_V1_Response) -> Bool {
if errors != other.errors {return false}
if files != other.files {return false}
return true
}
}
/// File describes a file generated by a plugin.
public struct Openapi_Plugin_V1_File: ProtobufGeneratedMessage {
public var swiftClassName: String {return "Openapi_Plugin_V1_File"}
public var protoMessageName: String {return "File"}
public var protoPackageName: String {return "openapi.plugin.v1"}
public var jsonFieldNames: [String: Int] {return [
"name": 1,
"data": 2,
]}
public var protoFieldNames: [String: Int] {return [
"name": 1,
"data": 2,
]}
/// name of the file
public var name: String = ""
/// data to be written to the file
public var data: Data = Data()
public init() {}
public init(name: String? = nil,
data: Data? = nil)
{
if let v = name {
self.name = v
}
if let v = data {
self.data = v
}
}
public mutating func _protoc_generated_decodeField(setter: inout ProtobufFieldDecoder, protoFieldNumber: Int) throws -> Bool {
let handled: Bool
switch protoFieldNumber {
case 1: handled = try setter.decodeSingularField(fieldType: ProtobufString.self, value: &name)
case 2: handled = try setter.decodeSingularField(fieldType: ProtobufBytes.self, value: &data)
default:
handled = false
}
return handled
}
public func _protoc_generated_traverse(visitor: inout ProtobufVisitor) throws {
if name != "" {
try visitor.visitSingularField(fieldType: ProtobufString.self, value: name, protoFieldNumber: 1, protoFieldName: "name", jsonFieldName: "name", swiftFieldName: "name")
}
if data != Data() {
try visitor.visitSingularField(fieldType: ProtobufBytes.self, value: data, protoFieldNumber: 2, protoFieldName: "data", jsonFieldName: "data", swiftFieldName: "data")
}
}
public func _protoc_generated_isEqualTo(other: Openapi_Plugin_V1_File) -> Bool {
if name != other.name {return false}
if data != other.data {return false}
return true
}
}
/// Wrapper wraps an OpenAPI document with its version.
public struct Openapi_Plugin_V1_Wrapper: ProtobufGeneratedMessage {
public var swiftClassName: String {return "Openapi_Plugin_V1_Wrapper"}
public var protoMessageName: String {return "Wrapper"}
public var protoPackageName: String {return "openapi.plugin.v1"}
public var jsonFieldNames: [String: Int] {return [
"name": 1,
"version": 2,
"value": 3,
]}
public var protoFieldNames: [String: Int] {return [
"name": 1,
"version": 2,
"value": 3,
]}
/// filename or URL of the wrapped document
public var name: String = ""
/// version of the OpenAPI specification that is used by the wrapped document
public var version: String = ""
/// valid serialized protocol buffer of the named OpenAPI specification version
public var value: Data = Data()
public init() {}
public init(name: String? = nil,
version: String? = nil,
value: Data? = nil)
{
if let v = name {
self.name = v
}
if let v = version {
self.version = v
}
if let v = value {
self.value = v
}
}
public mutating func _protoc_generated_decodeField(setter: inout ProtobufFieldDecoder, protoFieldNumber: Int) throws -> Bool {
let handled: Bool
switch protoFieldNumber {
case 1: handled = try setter.decodeSingularField(fieldType: ProtobufString.self, value: &name)
case 2: handled = try setter.decodeSingularField(fieldType: ProtobufString.self, value: &version)
case 3: handled = try setter.decodeSingularField(fieldType: ProtobufBytes.self, value: &value)
default:
handled = false
}
return handled
}
public func _protoc_generated_traverse(visitor: inout ProtobufVisitor) throws {
if name != "" {
try visitor.visitSingularField(fieldType: ProtobufString.self, value: name, protoFieldNumber: 1, protoFieldName: "name", jsonFieldName: "name", swiftFieldName: "name")
}
if version != "" {
try visitor.visitSingularField(fieldType: ProtobufString.self, value: version, protoFieldNumber: 2, protoFieldName: "version", jsonFieldName: "version", swiftFieldName: "version")
}
if value != Data() {
try visitor.visitSingularField(fieldType: ProtobufBytes.self, value: value, protoFieldNumber: 3, protoFieldName: "value", jsonFieldName: "value", swiftFieldName: "value")
}
}
public func _protoc_generated_isEqualTo(other: Openapi_Plugin_V1_Wrapper) -> Bool {
if name != other.name {return false}
if version != other.version {return false}
if value != other.value {return false}
return true
}
}

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.
// openapic (aka the OpenAPI Compiler) can be extended via plugins.
// A plugin is just 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 "openapi_$NAME", and will then be used when the
// flag "--${NAME}_out" is passed to openapic.
syntax = "proto3";
package openapi.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 = "OpenAPICompilerPlugin";
// The Java package name must be proto package name with proper prefix.
option java_package = "org.openapic.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 = "OAC"; // "OpenAPI Compiler"
// The version number of OpenAPI compiler.
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) the OpenAPI compiler.
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 {
// A wrapped OpenAPI document to process.
Wrapper wrapper = 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 openapi compiler.
Version compiler_version = 4;
}
// 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 openapic
// 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 openapic to an appropriate location.
repeated File files = 2;
}
// 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;
}
// Wrapper wraps an OpenAPI document with its version.
message Wrapper {
// filename or URL of the wrapped document
string name = 1;
// version of the OpenAPI specification that is used by the wrapped document
string version = 2;
// valid serialized protocol buffer of the named OpenAPI specification version
bytes value = 3;
}

View file

@ -0,0 +1,13 @@
all:
swift build
.build/debug/TemplateEncoder > Sources/gnostic_swift_generator/Templates.swift
swift build
cp .build/debug/gnostic_swift_generator gnostic_swift_generator
rm -f gnostic_swift_client gnostic_swift_server
ln -s gnostic_swift_generator gnostic_swift_client
ln -s gnostic_swift_generator gnostic_swift_server
clean:
rm -rf .build Packages
rm -rf gnostic_swift_client gnostic_swift_service

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.
import PackageDescription
let package = Package(
name: "SwiftOpenAPIPlugin",
targets: [
Target(name: "gnostic_swift_generator",
dependencies: [
"TemplateEncoder",
]),
Target(name: "TemplateEncoder")
],
dependencies: [
.Package(url: "https://github.com/apple/swift-protobuf.git", Version(0,9,24)),
.Package(url: "https://github.com/kylef/Stencil.git", Version(0,8,0))
]
)

View file

@ -0,0 +1,15 @@
# OpenAPI Swift Generator Plugin
This directory contains an `openapic` 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:
openapic 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.

View file

@ -0,0 +1,43 @@
// 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
let TEMPLATES = "Templates"
var s = ""
s += "// GENERATED: DO NOT EDIT\n"
s += "//\n"
s += "// This file contains base64 encodings of templates used for Swift OpenAPI code generation.\n"
s += "//\n"
s += "func loadTemplates() -> [String:String] {\n"
s += " return [\n"
let filenames = try FileManager.default.contentsOfDirectory(atPath:TEMPLATES)
for filename in filenames {
if filename.hasSuffix(".tmpl") {
let fileURL = URL(fileURLWithPath:TEMPLATES + "/" + filename)
let filedata = try Data(contentsOf:fileURL)
let encoding = filedata.base64EncodedString()
var templatename = filename
if let extRange = templatename.range(of: ".tmpl") {
templatename.replaceSubrange(extRange, with: "")
}
s += " \"" + templatename + "\": \"" + encoding + "\",\n"
}
}
s += " ]\n"
s += "}\n"
print(s)

View file

@ -0,0 +1,313 @@
// 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 Stencil
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 {
private var templateEnvironment : Environment
private var name : String = ""
private var package: String = ""
private var types : [ServiceType] = []
private var methods : [ServiceMethod] = []
public init(document : Openapi_V2_Document) {
templateEnvironment = Environment(loader:TemplateLoader(), extensions:[TemplateExtensions()])
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)
default:
Log("?")
}
default:
Log("?")
}
case .jsonReference: // (let reference):
Log("?")
default:
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 = ""
switch responseCode.value.oneof {
case .response(let response):
let schema = response.schema
switch schema.oneof {
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 Openapi_Plugin_V1_Response) throws {
let context = ["renderer": self]
for filename in filenames {
let clientcode = try templateEnvironment.renderTemplate(name:filename,
context:context)
if let data = stripMarkers(clientcode).data(using:.utf8) {
var clientfile = Openapi_Plugin_V1_File()
clientfile.name = filename
clientfile.data = data
response.files.append(clientfile)
}
}
}
}

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 Stencil
func TemplateExtensions() -> Extension {
let ext = Extension()
ext.registerFilter("hasParameters") { (value: Any?, arguments: [Any?]) in
let method : ServiceMethod = value as! ServiceMethod
return method.parametersType != nil
}
ext.registerFilter("hasResponses") { (value: Any?, arguments: [Any?]) in
let method : ServiceMethod = value as! ServiceMethod
return method.responsesType != nil
}
ext.registerFilter("syncClientParametersDeclaration") { (value: Any?, arguments: [Any?]) in
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
}
ext.registerFilter("syncClientReturnDeclaration") { (value: Any?, arguments: [Any?]) in
let method : ServiceMethod = value as! ServiceMethod
var result = ""
if let resultTypeName = method.resultTypeName {
result = " -> " + resultTypeName
}
return result
}
ext.registerFilter("asyncClientParametersDeclaration") { (value: Any?, arguments: [Any?]) in
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
}
ext.registerFilter("protocolParametersDeclaration") { (value: Any?, arguments: [Any?]) in
let method : ServiceMethod = value as! ServiceMethod
var result = ""
if let parametersTypeName = method.parametersTypeName {
result = "_ parameters : " + parametersTypeName
}
return result
}
ext.registerFilter("protocolReturnDeclaration") { (value: Any?, arguments: [Any?]) in
let method : ServiceMethod = value as! ServiceMethod
var result = ""
if let responsesTypeName = method.responsesTypeName {
result = "-> " + responsesTypeName
}
return result
}
ext.registerFilter("parameterFieldNames") { (value: Any?, arguments: [Any?]) in
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
}
ext.registerFilter("parametersTypeFields") { (value: Any?, arguments: [Any?]) in
let method : ServiceMethod = value as! ServiceMethod
if let parametersType = method.parametersType {
return parametersType.fields
} else {
return []
}
}
ext.registerFilter("kituraPath") { (value: Any?, arguments: [Any?]) in
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
}
ext.registerFilter("bodyParameterFieldName") { (value: Any?, arguments: [Any?]) in
let method : ServiceMethod = value as! ServiceMethod
if let parametersType = method.parametersType {
for field in parametersType.fields {
if field.position == "body" {
return field.name
}
}
}
return ""
}
ext.registerFilter("responsesHasFieldNamedOK") { (value: Any?, arguments: [Any?]) in
let method : ServiceMethod = value as! ServiceMethod
if let responsesType = method.responsesType {
for field in responsesType.fields {
if field.name == "ok" {
return true
}
}
}
return false
}
ext.registerFilter("responsesHasFieldNamedError") { (value: Any?, arguments: [Any?]) in
let method : ServiceMethod = value as! ServiceMethod
if let responsesType = method.responsesType {
for field in responsesType.fields {
if field.name == "error" {
return true
}
}
}
return false
}
return ext
}

View file

@ -0,0 +1,51 @@
// 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 Stencil
import Foundation
// A class for loading Stencil templates from compiled-in representations
public class TemplateLoader: Loader {
private var templates: [String:String]
public init() {
self.templates = loadTemplates()
}
public func loadTemplate(name: String, environment: Environment) throws -> Template {
if let encoding = templates[name],
let data = Data(base64Encoded: encoding, options:[]),
let template = String(data:data, encoding:.utf8) {
return environment.templateClass.init(templateString: template,
environment: environment,
name: name)
} else {
throw TemplateDoesNotExist(templateNames: [name], loader: self)
}
}
public func loadTemplate(names: [String], environment: Environment) throws -> Template {
for name in names {
if let encoding = templates[name],
let data = Data(base64Encoded: encoding, options:[]),
let template = String(data:data, encoding:.utf8) {
return environment.templateClass.init(templateString: template,
environment: environment,
name: name)
}
}
throw TemplateDoesNotExist(templateNames: names, loader: self)
}
}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,36 @@
// 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
// Code templates use "//-" prefixes to comment-out template operators
// to keep them from interfering with Swift code formatting tools.
// Use this to remove them after templates have been expanded.
func stripMarkers(_ code:String) -> String {
let inputLines = code.components(separatedBy:"\n")
var outputLines : [String] = []
for line in inputLines {
if line.contains("//-") {
let removed = line.replacingOccurrences(of:"//-", with:"")
if (removed.trimmingCharacters(in:CharacterSet.whitespaces) != "") {
outputLines.append(removed)
}
} else {
outputLines.append(line)
}
}
return outputLines.joined(separator:"\n")
}

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,51 @@
// 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
func Log(_ message : String) {
FileHandle.standardError.write((message + "\n").data(using:.utf8)!)
}
func main() throws {
// read the OpenAPI document
let rawRequest = try Stdin.readall()
let request = try Openapi_Plugin_V1_Request(protobuf: rawRequest)
let wrapper = request.wrapper
let document = try Openapi_V2_Document(protobuf:wrapper.value)
// build the service renderer
let renderer = ServiceRenderer(document:document)
// generate the desired files
var response = Openapi_Plugin_V1_Response()
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.serializeProtobuf()
Stdout.write(bytes: serializedResponse)
}
try main()

View file

@ -0,0 +1,517 @@
/*
* DO NOT EDIT.
*
* Generated by the protocol buffer compiler.
* Source: plugin.proto
*
*/
// Copyright 2016 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.
// openapic (aka the OpenAPI Compiler) can be extended via plugins.
// A plugin is just 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 "openapi_$NAME", and will then be used when the
// flag "--${NAME}_out" is passed to openapic.
import Foundation
import SwiftProtobuf
/// The version number of OpenAPI compiler.
public struct Openapi_Plugin_V1_Version: ProtobufGeneratedMessage {
public var swiftClassName: String {return "Openapi_Plugin_V1_Version"}
public var protoMessageName: String {return "Version"}
public var protoPackageName: String {return "openapi.plugin.v1"}
public var jsonFieldNames: [String: Int] {return [
"major": 1,
"minor": 2,
"patch": 3,
"suffix": 4,
]}
public var protoFieldNames: [String: Int] {return [
"major": 1,
"minor": 2,
"patch": 3,
"suffix": 4,
]}
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 = ""
public init() {}
public init(major: Int32? = nil,
minor: Int32? = nil,
patch: Int32? = nil,
suffix: String? = nil)
{
if let v = major {
self.major = v
}
if let v = minor {
self.minor = v
}
if let v = patch {
self.patch = v
}
if let v = suffix {
self.suffix = v
}
}
public mutating func _protoc_generated_decodeField(setter: inout ProtobufFieldDecoder, protoFieldNumber: Int) throws -> Bool {
let handled: Bool
switch protoFieldNumber {
case 1: handled = try setter.decodeSingularField(fieldType: ProtobufInt32.self, value: &major)
case 2: handled = try setter.decodeSingularField(fieldType: ProtobufInt32.self, value: &minor)
case 3: handled = try setter.decodeSingularField(fieldType: ProtobufInt32.self, value: &patch)
case 4: handled = try setter.decodeSingularField(fieldType: ProtobufString.self, value: &suffix)
default:
handled = false
}
return handled
}
public func _protoc_generated_traverse(visitor: inout ProtobufVisitor) throws {
if major != 0 {
try visitor.visitSingularField(fieldType: ProtobufInt32.self, value: major, protoFieldNumber: 1, protoFieldName: "major", jsonFieldName: "major", swiftFieldName: "major")
}
if minor != 0 {
try visitor.visitSingularField(fieldType: ProtobufInt32.self, value: minor, protoFieldNumber: 2, protoFieldName: "minor", jsonFieldName: "minor", swiftFieldName: "minor")
}
if patch != 0 {
try visitor.visitSingularField(fieldType: ProtobufInt32.self, value: patch, protoFieldNumber: 3, protoFieldName: "patch", jsonFieldName: "patch", swiftFieldName: "patch")
}
if suffix != "" {
try visitor.visitSingularField(fieldType: ProtobufString.self, value: suffix, protoFieldNumber: 4, protoFieldName: "suffix", jsonFieldName: "suffix", swiftFieldName: "suffix")
}
}
public func _protoc_generated_isEqualTo(other: Openapi_Plugin_V1_Version) -> Bool {
if major != other.major {return false}
if minor != other.minor {return false}
if patch != other.patch {return false}
if suffix != other.suffix {return false}
return true
}
}
/// A parameter passed to the plugin from (or through) the OpenAPI compiler.
public struct Openapi_Plugin_V1_Parameter: ProtobufGeneratedMessage {
public var swiftClassName: String {return "Openapi_Plugin_V1_Parameter"}
public var protoMessageName: String {return "Parameter"}
public var protoPackageName: String {return "openapi.plugin.v1"}
public var jsonFieldNames: [String: Int] {return [
"name": 1,
"value": 2,
]}
public var protoFieldNames: [String: Int] {return [
"name": 1,
"value": 2,
]}
/// The name of the parameter as specified in the option string
public var name: String = ""
/// The parameter value as specified in the option string
public var value: String = ""
public init() {}
public init(name: String? = nil,
value: String? = nil)
{
if let v = name {
self.name = v
}
if let v = value {
self.value = v
}
}
public mutating func _protoc_generated_decodeField(setter: inout ProtobufFieldDecoder, protoFieldNumber: Int) throws -> Bool {
let handled: Bool
switch protoFieldNumber {
case 1: handled = try setter.decodeSingularField(fieldType: ProtobufString.self, value: &name)
case 2: handled = try setter.decodeSingularField(fieldType: ProtobufString.self, value: &value)
default:
handled = false
}
return handled
}
public func _protoc_generated_traverse(visitor: inout ProtobufVisitor) throws {
if name != "" {
try visitor.visitSingularField(fieldType: ProtobufString.self, value: name, protoFieldNumber: 1, protoFieldName: "name", jsonFieldName: "name", swiftFieldName: "name")
}
if value != "" {
try visitor.visitSingularField(fieldType: ProtobufString.self, value: value, protoFieldNumber: 2, protoFieldName: "value", jsonFieldName: "value", swiftFieldName: "value")
}
}
public func _protoc_generated_isEqualTo(other: Openapi_Plugin_V1_Parameter) -> Bool {
if name != other.name {return false}
if value != other.value {return false}
return true
}
}
/// An encoded Request is written to the plugin's stdin.
public struct Openapi_Plugin_V1_Request: ProtobufGeneratedMessage {
public var swiftClassName: String {return "Openapi_Plugin_V1_Request"}
public var protoMessageName: String {return "Request"}
public var protoPackageName: String {return "openapi.plugin.v1"}
public var jsonFieldNames: [String: Int] {return [
"wrapper": 1,
"outputPath": 2,
"parameters": 3,
"compilerVersion": 4,
]}
public var protoFieldNames: [String: Int] {return [
"wrapper": 1,
"output_path": 2,
"parameters": 3,
"compiler_version": 4,
]}
private class _StorageClass {
typealias ProtobufExtendedMessage = Openapi_Plugin_V1_Request
var _wrapper: Openapi_Plugin_V1_Wrapper? = nil
var _outputPath: String = ""
var _parameters: [Openapi_Plugin_V1_Parameter] = []
var _compilerVersion: Openapi_Plugin_V1_Version? = nil
init() {}
func decodeField(setter: inout ProtobufFieldDecoder, protoFieldNumber: Int) throws -> Bool {
let handled: Bool
switch protoFieldNumber {
case 1: handled = try setter.decodeSingularMessageField(fieldType: Openapi_Plugin_V1_Wrapper.self, value: &_wrapper)
case 2: handled = try setter.decodeSingularField(fieldType: ProtobufString.self, value: &_outputPath)
case 3: handled = try setter.decodeRepeatedMessageField(fieldType: Openapi_Plugin_V1_Parameter.self, value: &_parameters)
case 4: handled = try setter.decodeSingularMessageField(fieldType: Openapi_Plugin_V1_Version.self, value: &_compilerVersion)
default:
handled = false
}
return handled
}
func traverse(visitor: inout ProtobufVisitor) throws {
if let v = _wrapper {
try visitor.visitSingularMessageField(value: v, protoFieldNumber: 1, protoFieldName: "wrapper", jsonFieldName: "wrapper", swiftFieldName: "wrapper")
}
if _outputPath != "" {
try visitor.visitSingularField(fieldType: ProtobufString.self, value: _outputPath, protoFieldNumber: 2, protoFieldName: "output_path", jsonFieldName: "outputPath", swiftFieldName: "outputPath")
}
if !_parameters.isEmpty {
try visitor.visitRepeatedMessageField(value: _parameters, protoFieldNumber: 3, protoFieldName: "parameters", jsonFieldName: "parameters", swiftFieldName: "parameters")
}
if let v = _compilerVersion {
try visitor.visitSingularMessageField(value: v, protoFieldNumber: 4, protoFieldName: "compiler_version", jsonFieldName: "compilerVersion", swiftFieldName: "compilerVersion")
}
}
func isEqualTo(other: _StorageClass) -> Bool {
if _wrapper != other._wrapper {return false}
if _outputPath != other._outputPath {return false}
if _parameters != other._parameters {return false}
if _compilerVersion != other._compilerVersion {return false}
return true
}
func copy() -> _StorageClass {
let clone = _StorageClass()
clone._wrapper = _wrapper
clone._outputPath = _outputPath
clone._parameters = _parameters
clone._compilerVersion = _compilerVersion
return clone
}
}
private var _storage = _StorageClass()
/// A wrapped OpenAPI document to process.
public var wrapper: Openapi_Plugin_V1_Wrapper {
get {return _storage._wrapper ?? Openapi_Plugin_V1_Wrapper()}
set {_uniqueStorage()._wrapper = 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: [Openapi_Plugin_V1_Parameter] {
get {return _storage._parameters}
set {_uniqueStorage()._parameters = newValue}
}
/// The version number of openapi compiler.
public var compilerVersion: Openapi_Plugin_V1_Version {
get {return _storage._compilerVersion ?? Openapi_Plugin_V1_Version()}
set {_uniqueStorage()._compilerVersion = newValue}
}
public init() {}
public init(wrapper: Openapi_Plugin_V1_Wrapper? = nil,
outputPath: String? = nil,
parameters: [Openapi_Plugin_V1_Parameter] = [],
compilerVersion: Openapi_Plugin_V1_Version? = nil)
{
let storage = _uniqueStorage()
storage._wrapper = wrapper
if let v = outputPath {
storage._outputPath = v
}
if !parameters.isEmpty {
storage._parameters = parameters
}
storage._compilerVersion = compilerVersion
}
public mutating func _protoc_generated_decodeField(setter: inout ProtobufFieldDecoder, protoFieldNumber: Int) throws -> Bool {
return try _uniqueStorage().decodeField(setter: &setter, protoFieldNumber: protoFieldNumber)
}
public func _protoc_generated_traverse(visitor: inout ProtobufVisitor) throws {
try _storage.traverse(visitor: &visitor)
}
public func _protoc_generated_isEqualTo(other: Openapi_Plugin_V1_Request) -> Bool {
return _storage === other._storage || _storage.isEqualTo(other: other._storage)
}
private mutating func _uniqueStorage() -> _StorageClass {
if !isKnownUniquelyReferenced(&_storage) {
_storage = _storage.copy()
}
return _storage
}
}
/// The plugin writes an encoded Response to stdout.
public struct Openapi_Plugin_V1_Response: ProtobufGeneratedMessage {
public var swiftClassName: String {return "Openapi_Plugin_V1_Response"}
public var protoMessageName: String {return "Response"}
public var protoPackageName: String {return "openapi.plugin.v1"}
public var jsonFieldNames: [String: Int] {return [
"errors": 1,
"files": 2,
]}
public var protoFieldNames: [String: Int] {return [
"errors": 1,
"files": 2,
]}
/// 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 openapic
/// 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 openapic to an appropriate location.
public var files: [Openapi_Plugin_V1_File] = []
public init() {}
public init(errors: [String] = [],
files: [Openapi_Plugin_V1_File] = [])
{
if !errors.isEmpty {
self.errors = errors
}
if !files.isEmpty {
self.files = files
}
}
public mutating func _protoc_generated_decodeField(setter: inout ProtobufFieldDecoder, protoFieldNumber: Int) throws -> Bool {
let handled: Bool
switch protoFieldNumber {
case 1: handled = try setter.decodeRepeatedField(fieldType: ProtobufString.self, value: &errors)
case 2: handled = try setter.decodeRepeatedMessageField(fieldType: Openapi_Plugin_V1_File.self, value: &files)
default:
handled = false
}
return handled
}
public func _protoc_generated_traverse(visitor: inout ProtobufVisitor) throws {
if !errors.isEmpty {
try visitor.visitRepeatedField(fieldType: ProtobufString.self, value: errors, protoFieldNumber: 1, protoFieldName: "errors", jsonFieldName: "errors", swiftFieldName: "errors")
}
if !files.isEmpty {
try visitor.visitRepeatedMessageField(value: files, protoFieldNumber: 2, protoFieldName: "files", jsonFieldName: "files", swiftFieldName: "files")
}
}
public func _protoc_generated_isEqualTo(other: Openapi_Plugin_V1_Response) -> Bool {
if errors != other.errors {return false}
if files != other.files {return false}
return true
}
}
/// File describes a file generated by a plugin.
public struct Openapi_Plugin_V1_File: ProtobufGeneratedMessage {
public var swiftClassName: String {return "Openapi_Plugin_V1_File"}
public var protoMessageName: String {return "File"}
public var protoPackageName: String {return "openapi.plugin.v1"}
public var jsonFieldNames: [String: Int] {return [
"name": 1,
"data": 2,
]}
public var protoFieldNames: [String: Int] {return [
"name": 1,
"data": 2,
]}
/// name of the file
public var name: String = ""
/// data to be written to the file
public var data: Data = Data()
public init() {}
public init(name: String? = nil,
data: Data? = nil)
{
if let v = name {
self.name = v
}
if let v = data {
self.data = v
}
}
public mutating func _protoc_generated_decodeField(setter: inout ProtobufFieldDecoder, protoFieldNumber: Int) throws -> Bool {
let handled: Bool
switch protoFieldNumber {
case 1: handled = try setter.decodeSingularField(fieldType: ProtobufString.self, value: &name)
case 2: handled = try setter.decodeSingularField(fieldType: ProtobufBytes.self, value: &data)
default:
handled = false
}
return handled
}
public func _protoc_generated_traverse(visitor: inout ProtobufVisitor) throws {
if name != "" {
try visitor.visitSingularField(fieldType: ProtobufString.self, value: name, protoFieldNumber: 1, protoFieldName: "name", jsonFieldName: "name", swiftFieldName: "name")
}
if data != Data() {
try visitor.visitSingularField(fieldType: ProtobufBytes.self, value: data, protoFieldNumber: 2, protoFieldName: "data", jsonFieldName: "data", swiftFieldName: "data")
}
}
public func _protoc_generated_isEqualTo(other: Openapi_Plugin_V1_File) -> Bool {
if name != other.name {return false}
if data != other.data {return false}
return true
}
}
/// Wrapper wraps an OpenAPI document with its version.
public struct Openapi_Plugin_V1_Wrapper: ProtobufGeneratedMessage {
public var swiftClassName: String {return "Openapi_Plugin_V1_Wrapper"}
public var protoMessageName: String {return "Wrapper"}
public var protoPackageName: String {return "openapi.plugin.v1"}
public var jsonFieldNames: [String: Int] {return [
"name": 1,
"version": 2,
"value": 3,
]}
public var protoFieldNames: [String: Int] {return [
"name": 1,
"version": 2,
"value": 3,
]}
/// filename or URL of the wrapped document
public var name: String = ""
/// version of the OpenAPI specification that is used by the wrapped document
public var version: String = ""
/// valid serialized protocol buffer of the named OpenAPI specification version
public var value: Data = Data()
public init() {}
public init(name: String? = nil,
version: String? = nil,
value: Data? = nil)
{
if let v = name {
self.name = v
}
if let v = version {
self.version = v
}
if let v = value {
self.value = v
}
}
public mutating func _protoc_generated_decodeField(setter: inout ProtobufFieldDecoder, protoFieldNumber: Int) throws -> Bool {
let handled: Bool
switch protoFieldNumber {
case 1: handled = try setter.decodeSingularField(fieldType: ProtobufString.self, value: &name)
case 2: handled = try setter.decodeSingularField(fieldType: ProtobufString.self, value: &version)
case 3: handled = try setter.decodeSingularField(fieldType: ProtobufBytes.self, value: &value)
default:
handled = false
}
return handled
}
public func _protoc_generated_traverse(visitor: inout ProtobufVisitor) throws {
if name != "" {
try visitor.visitSingularField(fieldType: ProtobufString.self, value: name, protoFieldNumber: 1, protoFieldName: "name", jsonFieldName: "name", swiftFieldName: "name")
}
if version != "" {
try visitor.visitSingularField(fieldType: ProtobufString.self, value: version, protoFieldNumber: 2, protoFieldName: "version", jsonFieldName: "version", swiftFieldName: "version")
}
if value != Data() {
try visitor.visitSingularField(fieldType: ProtobufBytes.self, value: value, protoFieldNumber: 3, protoFieldName: "value", jsonFieldName: "value", swiftFieldName: "value")
}
}
public func _protoc_generated_isEqualTo(other: Openapi_Plugin_V1_Wrapper) -> Bool {
if name != other.name {return false}
if version != other.version {return false}
if value != other.value {return false}
return true
}
}

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

View file

@ -0,0 +1,143 @@
// 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 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
}

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

View file

@ -0,0 +1,83 @@
// 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.
// Common type declarations
//-{% for serviceType in renderer.types %}
public class {{ serviceType.name }} : CustomStringConvertible {
//-{% for serviceTypeField in serviceType.fields %}
public var {{serviceTypeField.name}} : {{serviceTypeField.typeName}} = {{serviceTypeField.initialValue}}
//-{% endfor %}
public init() {}
//-{% if serviceType.isInterfaceType %}
public init?(jsonObject: Any?) {
if let jsonDictionary = jsonObject as? [String:Any] {
//-{% for serviceTypeField in serviceType.fields %}
if let value : Any = jsonDictionary["{{ serviceTypeField.jsonName }}"] {
//-{% if serviceTypeField.isArrayType %}
var outArray : [{{ serviceTypeField.elementType }}] = []
let array = value as! [Any]
for arrayValue in array {
if let element = {{ serviceTypeField.elementType }}(jsonObject:arrayValue) {
outArray.append(element)
}
}
self.{{serviceTypeField.name}} = outArray
//-{% endif %}
//-{% if serviceTypeField.isCastableType %}
self.{{serviceTypeField.name}} = value as! {{serviceTypeField.typeName}}
//-{% endif %}
//-{% if serviceTypeField.isConvertableType %}
self.{{serviceTypeField.name}} = {{serviceTypeField.typeName}}(value)
//-{% endif %}
}
//-{% endfor %}
} else {
return nil
}
}
public func jsonObject() -> Any {
var result : [String:Any] = [:]
//-{% for serviceTypeField in serviceType.fields %}
//-{% if serviceTypeField.isArrayType %}
var outArray : [Any] = []
for arrayValue in self.{{ serviceTypeField.name }} {
outArray.append(arrayValue.jsonObject())
}
result["{{ serviceTypeField.jsonName }}"] = outArray
//-{% endif %}
//-{% if serviceTypeField.isCastableType %}
result["{{ serviceTypeField.jsonName }}"] = self.{{serviceTypeField.name}}
//-{% endif %}
//-{% if serviceTypeField.isConvertableType %}
result["{{ serviceTypeField.jsonName }}"] = self.{{serviceTypeField.name}}.jsonObject()
//-{% endif %}
//-{% endfor %}
return result
}
//-{% endif %}
public var description: String{
return "[{{ serviceType.name }}" +
//-{% for serviceTypeField in serviceType.fields %}
" {{ serviceTypeField.name }}: \(self.{{ serviceTypeField.name }})" +
//-{% endfor %}
"]"
}
}
//-{% endfor %}

View file

@ -0,0 +1,9 @@
all:
rm -f gnostic_swift_generator
ln -s ../../gnostic_swift_generator
gnostic bookstore.json --swift_generator_out=Sources/Bookstore
swift build
test:
swift test

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.
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: 4)
]
)

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 `openapi_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 `openapic` 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,22 @@
// 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: "openapi_swift_sample",
dependencies: [
.Package(url: "https://github.com/apple/swift-protobuf.git", Version(0,9,24))
]
)

File diff suppressed because it is too large Load diff

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,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.
import Foundation
func printDocument(document:Openapi_V2_Document,
name:String,
version:String) -> String {
var code = CodePrinter()
code.print("READING \(name) (\(version))\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 = Openapi_Plugin_V1_Response()
let rawRequest = try Stdin.readall()
let request = try Openapi_Plugin_V1_Request(protobuf: rawRequest)
let wrapper = request.wrapper
let document = try Openapi_V2_Document(protobuf:wrapper.value)
let report = printDocument(document:document, name:wrapper.name, version:wrapper.version)
if let reportData = report.data(using:.utf8) {
var file = Openapi_Plugin_V1_File()
file.name = "report.txt"
file.data = reportData
response.files.append(file)
}
let serializedResponse = try response.serializeProtobuf()
Stdout.write(bytes: serializedResponse)
}
try main()

View file

@ -0,0 +1,517 @@
/*
* DO NOT EDIT.
*
* Generated by the protocol buffer compiler.
* Source: plugin.proto
*
*/
// Copyright 2016 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.
// openapic (aka the OpenAPI Compiler) can be extended via plugins.
// A plugin is just 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 "openapi_$NAME", and will then be used when the
// flag "--${NAME}_out" is passed to openapic.
import Foundation
import SwiftProtobuf
/// The version number of OpenAPI compiler.
public struct Openapi_Plugin_V1_Version: ProtobufGeneratedMessage {
public var swiftClassName: String {return "Openapi_Plugin_V1_Version"}
public var protoMessageName: String {return "Version"}
public var protoPackageName: String {return "openapi.plugin.v1"}
public var jsonFieldNames: [String: Int] {return [
"major": 1,
"minor": 2,
"patch": 3,
"suffix": 4,
]}
public var protoFieldNames: [String: Int] {return [
"major": 1,
"minor": 2,
"patch": 3,
"suffix": 4,
]}
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 = ""
public init() {}
public init(major: Int32? = nil,
minor: Int32? = nil,
patch: Int32? = nil,
suffix: String? = nil)
{
if let v = major {
self.major = v
}
if let v = minor {
self.minor = v
}
if let v = patch {
self.patch = v
}
if let v = suffix {
self.suffix = v
}
}
public mutating func _protoc_generated_decodeField(setter: inout ProtobufFieldDecoder, protoFieldNumber: Int) throws -> Bool {
let handled: Bool
switch protoFieldNumber {
case 1: handled = try setter.decodeSingularField(fieldType: ProtobufInt32.self, value: &major)
case 2: handled = try setter.decodeSingularField(fieldType: ProtobufInt32.self, value: &minor)
case 3: handled = try setter.decodeSingularField(fieldType: ProtobufInt32.self, value: &patch)
case 4: handled = try setter.decodeSingularField(fieldType: ProtobufString.self, value: &suffix)
default:
handled = false
}
return handled
}
public func _protoc_generated_traverse(visitor: inout ProtobufVisitor) throws {
if major != 0 {
try visitor.visitSingularField(fieldType: ProtobufInt32.self, value: major, protoFieldNumber: 1, protoFieldName: "major", jsonFieldName: "major", swiftFieldName: "major")
}
if minor != 0 {
try visitor.visitSingularField(fieldType: ProtobufInt32.self, value: minor, protoFieldNumber: 2, protoFieldName: "minor", jsonFieldName: "minor", swiftFieldName: "minor")
}
if patch != 0 {
try visitor.visitSingularField(fieldType: ProtobufInt32.self, value: patch, protoFieldNumber: 3, protoFieldName: "patch", jsonFieldName: "patch", swiftFieldName: "patch")
}
if suffix != "" {
try visitor.visitSingularField(fieldType: ProtobufString.self, value: suffix, protoFieldNumber: 4, protoFieldName: "suffix", jsonFieldName: "suffix", swiftFieldName: "suffix")
}
}
public func _protoc_generated_isEqualTo(other: Openapi_Plugin_V1_Version) -> Bool {
if major != other.major {return false}
if minor != other.minor {return false}
if patch != other.patch {return false}
if suffix != other.suffix {return false}
return true
}
}
/// A parameter passed to the plugin from (or through) the OpenAPI compiler.
public struct Openapi_Plugin_V1_Parameter: ProtobufGeneratedMessage {
public var swiftClassName: String {return "Openapi_Plugin_V1_Parameter"}
public var protoMessageName: String {return "Parameter"}
public var protoPackageName: String {return "openapi.plugin.v1"}
public var jsonFieldNames: [String: Int] {return [
"name": 1,
"value": 2,
]}
public var protoFieldNames: [String: Int] {return [
"name": 1,
"value": 2,
]}
/// The name of the parameter as specified in the option string
public var name: String = ""
/// The parameter value as specified in the option string
public var value: String = ""
public init() {}
public init(name: String? = nil,
value: String? = nil)
{
if let v = name {
self.name = v
}
if let v = value {
self.value = v
}
}
public mutating func _protoc_generated_decodeField(setter: inout ProtobufFieldDecoder, protoFieldNumber: Int) throws -> Bool {
let handled: Bool
switch protoFieldNumber {
case 1: handled = try setter.decodeSingularField(fieldType: ProtobufString.self, value: &name)
case 2: handled = try setter.decodeSingularField(fieldType: ProtobufString.self, value: &value)
default:
handled = false
}
return handled
}
public func _protoc_generated_traverse(visitor: inout ProtobufVisitor) throws {
if name != "" {
try visitor.visitSingularField(fieldType: ProtobufString.self, value: name, protoFieldNumber: 1, protoFieldName: "name", jsonFieldName: "name", swiftFieldName: "name")
}
if value != "" {
try visitor.visitSingularField(fieldType: ProtobufString.self, value: value, protoFieldNumber: 2, protoFieldName: "value", jsonFieldName: "value", swiftFieldName: "value")
}
}
public func _protoc_generated_isEqualTo(other: Openapi_Plugin_V1_Parameter) -> Bool {
if name != other.name {return false}
if value != other.value {return false}
return true
}
}
/// An encoded Request is written to the plugin's stdin.
public struct Openapi_Plugin_V1_Request: ProtobufGeneratedMessage {
public var swiftClassName: String {return "Openapi_Plugin_V1_Request"}
public var protoMessageName: String {return "Request"}
public var protoPackageName: String {return "openapi.plugin.v1"}
public var jsonFieldNames: [String: Int] {return [
"wrapper": 1,
"outputPath": 2,
"parameters": 3,
"compilerVersion": 4,
]}
public var protoFieldNames: [String: Int] {return [
"wrapper": 1,
"output_path": 2,
"parameters": 3,
"compiler_version": 4,
]}
private class _StorageClass {
typealias ProtobufExtendedMessage = Openapi_Plugin_V1_Request
var _wrapper: Openapi_Plugin_V1_Wrapper? = nil
var _outputPath: String = ""
var _parameters: [Openapi_Plugin_V1_Parameter] = []
var _compilerVersion: Openapi_Plugin_V1_Version? = nil
init() {}
func decodeField(setter: inout ProtobufFieldDecoder, protoFieldNumber: Int) throws -> Bool {
let handled: Bool
switch protoFieldNumber {
case 1: handled = try setter.decodeSingularMessageField(fieldType: Openapi_Plugin_V1_Wrapper.self, value: &_wrapper)
case 2: handled = try setter.decodeSingularField(fieldType: ProtobufString.self, value: &_outputPath)
case 3: handled = try setter.decodeRepeatedMessageField(fieldType: Openapi_Plugin_V1_Parameter.self, value: &_parameters)
case 4: handled = try setter.decodeSingularMessageField(fieldType: Openapi_Plugin_V1_Version.self, value: &_compilerVersion)
default:
handled = false
}
return handled
}
func traverse(visitor: inout ProtobufVisitor) throws {
if let v = _wrapper {
try visitor.visitSingularMessageField(value: v, protoFieldNumber: 1, protoFieldName: "wrapper", jsonFieldName: "wrapper", swiftFieldName: "wrapper")
}
if _outputPath != "" {
try visitor.visitSingularField(fieldType: ProtobufString.self, value: _outputPath, protoFieldNumber: 2, protoFieldName: "output_path", jsonFieldName: "outputPath", swiftFieldName: "outputPath")
}
if !_parameters.isEmpty {
try visitor.visitRepeatedMessageField(value: _parameters, protoFieldNumber: 3, protoFieldName: "parameters", jsonFieldName: "parameters", swiftFieldName: "parameters")
}
if let v = _compilerVersion {
try visitor.visitSingularMessageField(value: v, protoFieldNumber: 4, protoFieldName: "compiler_version", jsonFieldName: "compilerVersion", swiftFieldName: "compilerVersion")
}
}
func isEqualTo(other: _StorageClass) -> Bool {
if _wrapper != other._wrapper {return false}
if _outputPath != other._outputPath {return false}
if _parameters != other._parameters {return false}
if _compilerVersion != other._compilerVersion {return false}
return true
}
func copy() -> _StorageClass {
let clone = _StorageClass()
clone._wrapper = _wrapper
clone._outputPath = _outputPath
clone._parameters = _parameters
clone._compilerVersion = _compilerVersion
return clone
}
}
private var _storage = _StorageClass()
/// A wrapped OpenAPI document to process.
public var wrapper: Openapi_Plugin_V1_Wrapper {
get {return _storage._wrapper ?? Openapi_Plugin_V1_Wrapper()}
set {_uniqueStorage()._wrapper = 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: [Openapi_Plugin_V1_Parameter] {
get {return _storage._parameters}
set {_uniqueStorage()._parameters = newValue}
}
/// The version number of openapi compiler.
public var compilerVersion: Openapi_Plugin_V1_Version {
get {return _storage._compilerVersion ?? Openapi_Plugin_V1_Version()}
set {_uniqueStorage()._compilerVersion = newValue}
}
public init() {}
public init(wrapper: Openapi_Plugin_V1_Wrapper? = nil,
outputPath: String? = nil,
parameters: [Openapi_Plugin_V1_Parameter] = [],
compilerVersion: Openapi_Plugin_V1_Version? = nil)
{
let storage = _uniqueStorage()
storage._wrapper = wrapper
if let v = outputPath {
storage._outputPath = v
}
if !parameters.isEmpty {
storage._parameters = parameters
}
storage._compilerVersion = compilerVersion
}
public mutating func _protoc_generated_decodeField(setter: inout ProtobufFieldDecoder, protoFieldNumber: Int) throws -> Bool {
return try _uniqueStorage().decodeField(setter: &setter, protoFieldNumber: protoFieldNumber)
}
public func _protoc_generated_traverse(visitor: inout ProtobufVisitor) throws {
try _storage.traverse(visitor: &visitor)
}
public func _protoc_generated_isEqualTo(other: Openapi_Plugin_V1_Request) -> Bool {
return _storage === other._storage || _storage.isEqualTo(other: other._storage)
}
private mutating func _uniqueStorage() -> _StorageClass {
if !isKnownUniquelyReferenced(&_storage) {
_storage = _storage.copy()
}
return _storage
}
}
/// The plugin writes an encoded Response to stdout.
public struct Openapi_Plugin_V1_Response: ProtobufGeneratedMessage {
public var swiftClassName: String {return "Openapi_Plugin_V1_Response"}
public var protoMessageName: String {return "Response"}
public var protoPackageName: String {return "openapi.plugin.v1"}
public var jsonFieldNames: [String: Int] {return [
"errors": 1,
"files": 2,
]}
public var protoFieldNames: [String: Int] {return [
"errors": 1,
"files": 2,
]}
/// 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 openapic
/// 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 openapic to an appropriate location.
public var files: [Openapi_Plugin_V1_File] = []
public init() {}
public init(errors: [String] = [],
files: [Openapi_Plugin_V1_File] = [])
{
if !errors.isEmpty {
self.errors = errors
}
if !files.isEmpty {
self.files = files
}
}
public mutating func _protoc_generated_decodeField(setter: inout ProtobufFieldDecoder, protoFieldNumber: Int) throws -> Bool {
let handled: Bool
switch protoFieldNumber {
case 1: handled = try setter.decodeRepeatedField(fieldType: ProtobufString.self, value: &errors)
case 2: handled = try setter.decodeRepeatedMessageField(fieldType: Openapi_Plugin_V1_File.self, value: &files)
default:
handled = false
}
return handled
}
public func _protoc_generated_traverse(visitor: inout ProtobufVisitor) throws {
if !errors.isEmpty {
try visitor.visitRepeatedField(fieldType: ProtobufString.self, value: errors, protoFieldNumber: 1, protoFieldName: "errors", jsonFieldName: "errors", swiftFieldName: "errors")
}
if !files.isEmpty {
try visitor.visitRepeatedMessageField(value: files, protoFieldNumber: 2, protoFieldName: "files", jsonFieldName: "files", swiftFieldName: "files")
}
}
public func _protoc_generated_isEqualTo(other: Openapi_Plugin_V1_Response) -> Bool {
if errors != other.errors {return false}
if files != other.files {return false}
return true
}
}
/// File describes a file generated by a plugin.
public struct Openapi_Plugin_V1_File: ProtobufGeneratedMessage {
public var swiftClassName: String {return "Openapi_Plugin_V1_File"}
public var protoMessageName: String {return "File"}
public var protoPackageName: String {return "openapi.plugin.v1"}
public var jsonFieldNames: [String: Int] {return [
"name": 1,
"data": 2,
]}
public var protoFieldNames: [String: Int] {return [
"name": 1,
"data": 2,
]}
/// name of the file
public var name: String = ""
/// data to be written to the file
public var data: Data = Data()
public init() {}
public init(name: String? = nil,
data: Data? = nil)
{
if let v = name {
self.name = v
}
if let v = data {
self.data = v
}
}
public mutating func _protoc_generated_decodeField(setter: inout ProtobufFieldDecoder, protoFieldNumber: Int) throws -> Bool {
let handled: Bool
switch protoFieldNumber {
case 1: handled = try setter.decodeSingularField(fieldType: ProtobufString.self, value: &name)
case 2: handled = try setter.decodeSingularField(fieldType: ProtobufBytes.self, value: &data)
default:
handled = false
}
return handled
}
public func _protoc_generated_traverse(visitor: inout ProtobufVisitor) throws {
if name != "" {
try visitor.visitSingularField(fieldType: ProtobufString.self, value: name, protoFieldNumber: 1, protoFieldName: "name", jsonFieldName: "name", swiftFieldName: "name")
}
if data != Data() {
try visitor.visitSingularField(fieldType: ProtobufBytes.self, value: data, protoFieldNumber: 2, protoFieldName: "data", jsonFieldName: "data", swiftFieldName: "data")
}
}
public func _protoc_generated_isEqualTo(other: Openapi_Plugin_V1_File) -> Bool {
if name != other.name {return false}
if data != other.data {return false}
return true
}
}
/// Wrapper wraps an OpenAPI document with its version.
public struct Openapi_Plugin_V1_Wrapper: ProtobufGeneratedMessage {
public var swiftClassName: String {return "Openapi_Plugin_V1_Wrapper"}
public var protoMessageName: String {return "Wrapper"}
public var protoPackageName: String {return "openapi.plugin.v1"}
public var jsonFieldNames: [String: Int] {return [
"name": 1,
"version": 2,
"value": 3,
]}
public var protoFieldNames: [String: Int] {return [
"name": 1,
"version": 2,
"value": 3,
]}
/// filename or URL of the wrapped document
public var name: String = ""
/// version of the OpenAPI specification that is used by the wrapped document
public var version: String = ""
/// valid serialized protocol buffer of the named OpenAPI specification version
public var value: Data = Data()
public init() {}
public init(name: String? = nil,
version: String? = nil,
value: Data? = nil)
{
if let v = name {
self.name = v
}
if let v = version {
self.version = v
}
if let v = value {
self.value = v
}
}
public mutating func _protoc_generated_decodeField(setter: inout ProtobufFieldDecoder, protoFieldNumber: Int) throws -> Bool {
let handled: Bool
switch protoFieldNumber {
case 1: handled = try setter.decodeSingularField(fieldType: ProtobufString.self, value: &name)
case 2: handled = try setter.decodeSingularField(fieldType: ProtobufString.self, value: &version)
case 3: handled = try setter.decodeSingularField(fieldType: ProtobufBytes.self, value: &value)
default:
handled = false
}
return handled
}
public func _protoc_generated_traverse(visitor: inout ProtobufVisitor) throws {
if name != "" {
try visitor.visitSingularField(fieldType: ProtobufString.self, value: name, protoFieldNumber: 1, protoFieldName: "name", jsonFieldName: "name", swiftFieldName: "name")
}
if version != "" {
try visitor.visitSingularField(fieldType: ProtobufString.self, value: version, protoFieldNumber: 2, protoFieldName: "version", jsonFieldName: "version", swiftFieldName: "version")
}
if value != Data() {
try visitor.visitSingularField(fieldType: ProtobufBytes.self, value: value, protoFieldNumber: 3, protoFieldName: "value", jsonFieldName: "value", swiftFieldName: "value")
}
}
public func _protoc_generated_isEqualTo(other: Openapi_Plugin_V1_Wrapper) -> Bool {
if name != other.name {return false}
if version != other.version {return false}
if value != other.value {return false}
return true
}
}