Updated vednor files

This commit is contained in:
Serguei Bezverkhi
2018-02-15 08:50:31 -05:00
parent 18a4ce4439
commit 1f1e8cea37
3299 changed files with 834 additions and 1051200 deletions

View File

@ -1,23 +0,0 @@
# 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

@ -1,82 +0,0 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// gnostic_analyze is a tool for analyzing OpenAPI descriptions.
//
// It scans an API description and evaluates properties
// that influence the ease and quality of code generation.
// - The number of HTTP operations of each method (GET, POST, etc).
// - The number of HTTP operations with no OperationId value.
// - The parameter types used and their frequencies.
// - The response types used and their frequencies.
// - The types used in definition objects and arrays and their frequencies.
// Results are returned in a JSON structure.
package main
import (
"encoding/json"
"os"
"path"
"strings"
"github.com/golang/protobuf/proto"
plugins "github.com/googleapis/gnostic/plugins"
"github.com/googleapis/gnostic/plugins/gnostic-analyze/statistics"
)
// Record an error, then serialize and return a response.
func sendAndExitIfError(err error, response *plugins.Response) {
if err != nil {
response.Errors = append(response.Errors, err.Error())
sendAndExit(response)
}
}
// Serialize and return a response.
func sendAndExit(response *plugins.Response) {
responseBytes, _ := proto.Marshal(response)
os.Stdout.Write(responseBytes)
os.Exit(0)
}
// This is the main function for the plugin.
func main() {
env, err := plugins.NewEnvironment()
env.RespondAndExitIfError(err)
var stats *statistics.DocumentStatistics
if env.Request.Openapi2 != nil {
// Analyze the API document.
stats = statistics.NewDocumentStatistics(env.Request.SourceName, env.Request.Openapi2)
}
if env.Request.Openapi3 != nil {
// Analyze the API document.
stats = statistics.NewDocumentStatisticsV3(env.Request.SourceName, env.Request.Openapi3)
}
if stats != nil {
// Return the analysis results with an appropriate filename.
// Results are in files named "summary.json" in the same relative
// locations as the description source files.
file := &plugins.File{}
file.Name = strings.Replace(stats.Name, path.Base(stats.Name), "summary.json", -1)
file.Data, err = json.MarshalIndent(stats, "", " ")
file.Data = append(file.Data, []byte("\n")...)
env.RespondAndExitIfError(err)
env.Response.Files = append(env.Response.Files, file)
}
env.RespondAndExit()
}

View File

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

View File

@ -1,127 +0,0 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package statistics
import (
openapi "github.com/googleapis/gnostic/OpenAPIv3"
)
// NewDocumentStatistics builds a new DocumentStatistics object.
func NewDocumentStatisticsV3(source string, document *openapi.Document) *DocumentStatistics {
s := &DocumentStatistics{}
s.Operations = make(map[string]int, 0)
s.ParameterTypes = make(map[string]int, 0)
s.ResultTypes = make(map[string]int, 0)
s.DefinitionFieldTypes = make(map[string]int, 0)
s.DefinitionArrayTypes = make(map[string]int, 0)
s.DefinitionPrimitiveTypes = make(map[string]int, 0)
s.AnonymousOperations = make([]string, 0)
s.AnonymousObjects = make([]string, 0)
// TODO
//s.analyzeDocumentV3(source, document)
return s
}
/*
func (s *DocumentStatistics) analyzeOperationV3(method string, path string, operation *openapi.Operation) {
s.addOperation(method)
s.addOperation("total")
if operation.OperationId == "" {
s.addOperation("anonymous")
s.AnonymousOperations = append(s.AnonymousOperations, path)
}
for _, parametersItem := range operation.Parameters {
p := parametersItem.GetParameter()
if p != nil {
typeName := typeNameForSchemaOrReferenceV3(p.Schema)
s.addParameterType(path+"/"+p.Name, typeName)
}
}
for _, pair := range *(operation.Responses.Responses) {
value := pair.Value
response := value.GetResponse()
if response != nil {
responseSchema := response.Schema
responseSchemaSchema := responseSchema.GetSchema()
if responseSchemaSchema != nil {
s.addResultType(path+"/responses/"+pair.Name, typeForSchema(responseSchemaSchema))
}
responseFileSchema := responseSchema.GetFileSchema()
if responseFileSchema != nil {
s.addResultType(path+"/responses/"+pair.Name, typeForFileSchema(responseFileSchema))
}
}
ref := value.GetJsonReference()
if ref != nil {
}
}
}
// Analyze a definition in an OpenAPI description.
// Collect information about the definition type and any subsidiary types,
// such as the types of object fields or array elements.
func (s *DocumentStatistics) analyzeDefinitionV3(path string, definition *openapi.Schema) {
s.DefinitionCount++
typeName := typeNameForSchemaV3(definition)
switch typeName {
case "object":
if definition.Properties != nil {
for _, pair := range definition.Properties.AdditionalProperties {
propertySchema := pair.Value
propertyType := typeForSchemaV3(propertySchema)
s.addDefinitionFieldType(path+"/"+pair.Name, propertyType)
}
}
case "array":
s.addDefinitionArrayType(path+"/", typeForSchemaV3(definition))
default: // string, boolean, integer, number, null...
s.addDefinitionPrimitiveType(path+"/", typeName)
}
}
// Analyze an OpenAPI description.
// Collect information about types used in the API.
// This should be called exactly once per DocumentStatistics object.
func (s *DocumentStatistics) analyzeDocumentV3(source string, document *openapi.Document) {
s.Name = source
s.Title = document.Info.Title
for _, pair := range document.Paths.Path {
path := pair.Value
if path.Get != nil {
s.analyzeOperation("get", "paths"+pair.Name+"/get", path.Get)
}
if path.Post != nil {
s.analyzeOperation("post", "paths"+pair.Name+"/post", path.Post)
}
if path.Put != nil {
s.analyzeOperation("put", "paths"+pair.Name+"/put", path.Put)
}
if path.Delete != nil {
s.analyzeOperation("delete", "paths"+pair.Name+"/delete", path.Delete)
}
}
if document.Components.Schemas != nil {
for _, pair := range document.Components.Schemas.AdditionalProperties {
definition := pair.Value
if definition.GetSchema() != nil {
s.analyzeDefinition("definitions/"+pair.Name, definition.GetSchema())
}
}
}
}
*/

View File

@ -1,156 +0,0 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// summarize is a tool for summarizing the results of gnostic_analyze runs.
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"sort"
"github.com/googleapis/gnostic/plugins/gnostic-analyze/statistics"
)
// Results are collected in this global slice.
var stats []statistics.DocumentStatistics
// walker is called for each summary file found.
func walker(p string, info os.FileInfo, err error) error {
basename := path.Base(p)
if basename != "summary.json" {
return nil
}
data, err := ioutil.ReadFile(p)
if err != nil {
return err
}
var s statistics.DocumentStatistics
err = json.Unmarshal(data, &s)
if err != nil {
return err
}
stats = append(stats, s)
return nil
}
func printFrequencies(m map[string]int) {
for _, pair := range rankByCount(m) {
fmt.Printf("%6d %s\n", pair.Value, pair.Key)
}
}
func rankByCount(frequencies map[string]int) pairList {
pl := make(pairList, len(frequencies))
i := 0
for k, v := range frequencies {
pl[i] = pair{k, v}
i++
}
sort.Sort(sort.Reverse(pl))
return pl
}
type pair struct {
Key string
Value int
}
type pairList []pair
func (p pairList) Len() int { return len(p) }
func (p pairList) Less(i, j int) bool { return p[i].Value < p[j].Value }
func (p pairList) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func main() {
// Collect all statistics in the current directory and its subdirectories.
stats = make([]statistics.DocumentStatistics, 0)
filepath.Walk(".", walker)
// Compute some interesting properties.
apisWithAnonymousOperations := 0
apisWithAnonymousObjects := 0
apisWithAnonymousAnything := 0
opFrequencies := make(map[string]int, 0)
parameterTypeFrequencies := make(map[string]int, 0)
resultTypeFrequencies := make(map[string]int, 0)
definitionFieldTypeFrequencies := make(map[string]int, 0)
definitionArrayTypeFrequencies := make(map[string]int, 0)
definitionPrimitiveTypeFrequencies := make(map[string]int, 0)
for _, api := range stats {
if api.Operations["anonymous"] != 0 {
apisWithAnonymousOperations++
}
if len(api.AnonymousObjects) > 0 {
apisWithAnonymousObjects++
}
if len(api.AnonymousOperations) > 0 {
apisWithAnonymousAnything++
if len(api.AnonymousObjects) > 0 {
fmt.Printf("%s has anonymous operations and objects\n", api.Name)
} else {
fmt.Printf("%s has anonymous operations\n", api.Name)
}
} else {
if len(api.AnonymousObjects) > 0 {
apisWithAnonymousAnything++
fmt.Printf("%s has anonymous objects\n", api.Name)
} else {
fmt.Printf("%s has no anonymous operations or objects\n", api.Name)
}
}
for k, v := range api.Operations {
opFrequencies[k] += v
}
for k, v := range api.ParameterTypes {
parameterTypeFrequencies[k] += v
}
for k, v := range api.ResultTypes {
resultTypeFrequencies[k] += v
}
for k, v := range api.DefinitionFieldTypes {
definitionFieldTypeFrequencies[k] += v
}
for k, v := range api.DefinitionArrayTypes {
definitionArrayTypeFrequencies[k] += v
}
for k, v := range api.DefinitionPrimitiveTypes {
definitionPrimitiveTypeFrequencies[k] += v
}
}
// Report the results.
fmt.Printf("\n")
fmt.Printf("Collected information on %d APIs.\n\n", len(stats))
fmt.Printf("APIs with anonymous operations: %d\n", apisWithAnonymousOperations)
fmt.Printf("APIs with anonymous objects: %d\n", apisWithAnonymousObjects)
fmt.Printf("APIs with anonymous anything: %d\n", apisWithAnonymousAnything)
fmt.Printf("\nOperation frequencies:\n")
printFrequencies(opFrequencies)
fmt.Printf("\nParameter type frequencies:\n")
printFrequencies(parameterTypeFrequencies)
fmt.Printf("\nResult type frequencies:\n")
printFrequencies(resultTypeFrequencies)
fmt.Printf("\nDefinition object field type frequencies:\n")
printFrequencies(definitionFieldTypeFrequencies)
fmt.Printf("\nDefinition array type frequencies:\n")
printFrequencies(definitionArrayTypeFrequencies)
fmt.Printf("\nDefinition primitive type frequencies:\n")
printFrequencies(definitionPrimitiveTypeFrequencies)
}