vendor files

This commit is contained in:
Serguei Bezverkhi
2018-01-09 13:57:14 -05:00
parent 558bc6c02a
commit 7b24313bd6
16547 changed files with 4527373 additions and 0 deletions

46
vendor/k8s.io/kube-openapi/pkg/generators/README generated vendored Normal file
View File

@ -0,0 +1,46 @@
# Generate OpenAPI definitions
- To generate definition for a specific type or package add "+k8s:openapi-gen=true" tag to the type/package comment lines.
- To exclude a type or a member from a tagged package/type, add "+k8s:openapi-gen=false" tag to the comment lines.
# OpenAPI Extensions
OpenAPI spec can have extensions on types. To define one or more extensions on a type or its member
add `+k8s:openapi-gen=x-kubernetes-$NAME:`$VALUE`` to the comment lines before type/member. A type/member can
have multiple extensions. The rest of the line in the comment will be used as $VALUE so there is no need to
escape or quote the value string. Extensions can be used to pass more information to client generators or
documentation generators. For example a type might have a friendly name to be displayed in documentation or
being used in a client's fluent interface.
# Custom OpenAPI type definitions
Custom types which otherwise don't map directly to OpenAPI can override their
OpenAPI definition by implementing a function named "OpenAPIDefinition" with
the following signature:
import openapi "k8s.io/kube-openapi/pkg/common"
// ...
type Time struct {
time.Time
}
func (_ Time) OpenAPIDefinition() openapi.OpenAPIDefinition {
return openapi.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "date-time",
},
},
}
}
Alternatively, the type can avoid the "openapi" import by defining the following
methods. The following example produces the same OpenAPI definition as the
example above:
func (_ Time) OpenAPISchemaType() []string { return []string{"string"} }
func (_ Time) OpenAPISchemaFormat() string { return "date-time" }
TODO(mehdy): Make k8s:openapi-gen a parameter to the generator now that OpenAPI has its own repo.

642
vendor/k8s.io/kube-openapi/pkg/generators/openapi.go generated vendored Normal file
View File

@ -0,0 +1,642 @@
/*
Copyright 2016 The Kubernetes Authors.
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 generators
import (
"bytes"
"fmt"
"io"
"path/filepath"
"reflect"
"sort"
"strings"
"k8s.io/gengo/args"
"k8s.io/gengo/generator"
"k8s.io/gengo/namer"
"k8s.io/gengo/types"
openapi "k8s.io/kube-openapi/pkg/common"
"github.com/golang/glog"
)
// This is the comment tag that carries parameters for open API generation.
const tagName = "k8s:openapi-gen"
const tagOptional = "optional"
// Known values for the tag.
const (
tagValueTrue = "true"
tagValueFalse = "false"
tagExtensionPrefix = "x-kubernetes-"
tagPatchStrategy = "patchStrategy"
tagPatchMergeKey = "patchMergeKey"
patchStrategyExtensionName = "patch-strategy"
patchMergeKeyExtensionName = "patch-merge-key"
)
func getOpenAPITagValue(comments []string) []string {
return types.ExtractCommentTags("+", comments)[tagName]
}
func getSingleTagsValue(comments []string, tag string) (string, error) {
tags, ok := types.ExtractCommentTags("+", comments)[tag]
if !ok || len(tags) == 0 {
return "", nil
}
if len(tags) > 1 {
return "", fmt.Errorf("multiple values are not allowed for tag %s", tag)
}
return tags[0], nil
}
func hasOpenAPITagValue(comments []string, value string) bool {
tagValues := getOpenAPITagValue(comments)
for _, val := range tagValues {
if val == value {
return true
}
}
return false
}
// hasOptionalTag returns true if the member has +optional in its comments or
// omitempty in its json tags.
func hasOptionalTag(m *types.Member) bool {
hasOptionalCommentTag := types.ExtractCommentTags(
"+", m.CommentLines)[tagOptional] != nil
hasOptionalJsonTag := strings.Contains(
reflect.StructTag(m.Tags).Get("json"), "omitempty")
return hasOptionalCommentTag || hasOptionalJsonTag
}
type identityNamer struct{}
func (_ identityNamer) Name(t *types.Type) string {
return t.Name.String()
}
var _ namer.Namer = identityNamer{}
// NameSystems returns the name system used by the generators in this package.
func NameSystems() namer.NameSystems {
return namer.NameSystems{
"raw": namer.NewRawNamer("", nil),
"sorting_namer": identityNamer{},
}
}
// DefaultNameSystem returns the default name system for ordering the types to be
// processed by the generators in this package.
func DefaultNameSystem() string {
return "sorting_namer"
}
func Packages(context *generator.Context, arguments *args.GeneratorArgs) generator.Packages {
boilerplate, err := arguments.LoadGoBoilerplate()
if err != nil {
glog.Fatalf("Failed loading boilerplate: %v", err)
}
header := append([]byte(fmt.Sprintf("// +build !%s\n\n", arguments.GeneratedBuildTag)), boilerplate...)
header = append(header, []byte(
`
// This file was autogenerated by openapi-gen. Do not edit it manually!
`)...)
return generator.Packages{
&generator.DefaultPackage{
PackageName: filepath.Base(arguments.OutputPackagePath),
PackagePath: arguments.OutputPackagePath,
HeaderText: header,
GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) {
return []generator.Generator{NewOpenAPIGen(arguments.OutputFileBaseName, arguments.OutputPackagePath, context)}
},
FilterFunc: func(c *generator.Context, t *types.Type) bool {
// There is a conflict between this codegen and codecgen, we should avoid types generated for codecgen
if strings.HasPrefix(t.Name.Name, "codecSelfer") {
return false
}
pkg := context.Universe.Package(t.Name.Package)
if hasOpenAPITagValue(pkg.Comments, tagValueTrue) {
return !hasOpenAPITagValue(t.CommentLines, tagValueFalse)
}
if hasOpenAPITagValue(t.CommentLines, tagValueTrue) {
return true
}
return false
},
},
}
}
const (
specPackagePath = "github.com/go-openapi/spec"
openAPICommonPackagePath = "k8s.io/kube-openapi/pkg/common"
)
// openApiGen produces a file with auto-generated OpenAPI functions.
type openAPIGen struct {
generator.DefaultGen
// TargetPackage is the package that will get GetOpenAPIDefinitions function returns all open API definitions.
targetPackage string
imports namer.ImportTracker
context *generator.Context
}
func NewOpenAPIGen(sanitizedName string, targetPackage string, context *generator.Context) generator.Generator {
return &openAPIGen{
DefaultGen: generator.DefaultGen{
OptionalName: sanitizedName,
},
imports: generator.NewImportTracker(),
targetPackage: targetPackage,
context: context,
}
}
func (g *openAPIGen) Namers(c *generator.Context) namer.NameSystems {
// Have the raw namer for this file track what it imports.
return namer.NameSystems{
"raw": namer.NewRawNamer(g.targetPackage, g.imports),
}
}
func (g *openAPIGen) Filter(c *generator.Context, t *types.Type) bool {
// There is a conflict between this codegen and codecgen, we should avoid types generated for codecgen
if strings.HasPrefix(t.Name.Name, "codecSelfer") {
return false
}
return true
}
func (g *openAPIGen) isOtherPackage(pkg string) bool {
if pkg == g.targetPackage {
return false
}
if strings.HasSuffix(pkg, "\""+g.targetPackage+"\"") {
return false
}
return true
}
func (g *openAPIGen) Imports(c *generator.Context) []string {
importLines := []string{}
for _, singleImport := range g.imports.ImportLines() {
importLines = append(importLines, singleImport)
}
return importLines
}
func argsFromType(t *types.Type) generator.Args {
return generator.Args{
"type": t,
"ReferenceCallback": types.Ref(openAPICommonPackagePath, "ReferenceCallback"),
"OpenAPIDefinition": types.Ref(openAPICommonPackagePath, "OpenAPIDefinition"),
"SpecSchemaType": types.Ref(specPackagePath, "Schema"),
}
}
func (g *openAPIGen) Init(c *generator.Context, w io.Writer) error {
sw := generator.NewSnippetWriter(w, c, "$", "$")
sw.Do("func GetOpenAPIDefinitions(ref $.ReferenceCallback|raw$) map[string]$.OpenAPIDefinition|raw$ {\n", argsFromType(nil))
sw.Do("return map[string]$.OpenAPIDefinition|raw${\n", argsFromType(nil))
return sw.Error()
}
func (g *openAPIGen) Finalize(c *generator.Context, w io.Writer) error {
sw := generator.NewSnippetWriter(w, c, "$", "$")
sw.Do("}\n", nil)
sw.Do("}\n", nil)
return sw.Error()
}
func (g *openAPIGen) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error {
glog.V(5).Infof("generating for type %v", t)
sw := generator.NewSnippetWriter(w, c, "$", "$")
err := newOpenAPITypeWriter(sw).generate(t)
if err != nil {
return err
}
return sw.Error()
}
func getJsonTags(m *types.Member) []string {
jsonTag := reflect.StructTag(m.Tags).Get("json")
if jsonTag == "" {
return []string{}
}
return strings.Split(jsonTag, ",")
}
func getPatchTags(m *types.Member) (string, string) {
return reflect.StructTag(m.Tags).Get(tagPatchMergeKey), reflect.StructTag(m.Tags).Get(tagPatchStrategy)
}
func getReferableName(m *types.Member) string {
jsonTags := getJsonTags(m)
if len(jsonTags) > 0 {
if jsonTags[0] == "-" {
return ""
} else {
return jsonTags[0]
}
} else {
return m.Name
}
}
func shouldInlineMembers(m *types.Member) bool {
jsonTags := getJsonTags(m)
return len(jsonTags) > 1 && jsonTags[1] == "inline"
}
type openAPITypeWriter struct {
*generator.SnippetWriter
refTypes map[string]*types.Type
GetDefinitionInterface *types.Type
}
func newOpenAPITypeWriter(sw *generator.SnippetWriter) openAPITypeWriter {
return openAPITypeWriter{
SnippetWriter: sw,
refTypes: map[string]*types.Type{},
}
}
func methodReturnsValue(mt *types.Type, pkg, name string) bool {
if len(mt.Signature.Parameters) != 0 || len(mt.Signature.Results) != 1 {
return false
}
r := mt.Signature.Results[0]
return r.Name.Name == name && r.Name.Package == pkg
}
func hasOpenAPIDefinitionMethod(t *types.Type) bool {
for mn, mt := range t.Methods {
if mn != "OpenAPIDefinition" {
continue
}
return methodReturnsValue(mt, openAPICommonPackagePath, "OpenAPIDefinition")
}
return false
}
func hasOpenAPIDefinitionMethods(t *types.Type) bool {
var hasSchemaTypeMethod, hasOpenAPISchemaFormat bool
for mn, mt := range t.Methods {
switch mn {
case "OpenAPISchemaType":
hasSchemaTypeMethod = methodReturnsValue(mt, "", "[]string")
case "OpenAPISchemaFormat":
hasOpenAPISchemaFormat = methodReturnsValue(mt, "", "string")
}
}
return hasSchemaTypeMethod && hasOpenAPISchemaFormat
}
// typeShortName returns short package name (e.g. the name x appears in package x definition) dot type name.
func typeShortName(t *types.Type) string {
return filepath.Base(t.Name.Package) + "." + t.Name.Name
}
func (g openAPITypeWriter) generateMembers(t *types.Type, required []string) ([]string, error) {
var err error
for _, m := range t.Members {
if hasOpenAPITagValue(m.CommentLines, tagValueFalse) {
continue
}
if shouldInlineMembers(&m) {
required, err = g.generateMembers(m.Type, required)
if err != nil {
return required, err
}
continue
}
name := getReferableName(&m)
if name == "" {
continue
}
if !hasOptionalTag(&m) {
required = append(required, name)
}
if err = g.generateProperty(&m, t); err != nil {
glog.Errorf("Error when generating: %v, %v\n", name, m)
return required, err
}
}
return required, nil
}
func (g openAPITypeWriter) generate(t *types.Type) error {
// Only generate for struct type and ignore the rest
switch t.Kind {
case types.Struct:
args := argsFromType(t)
g.Do("\"$.$\": ", t.Name)
if hasOpenAPIDefinitionMethod(t) {
g.Do("$.type|raw${}.OpenAPIDefinition(),\n", args)
return nil
}
if hasOpenAPIDefinitionMethods(t) {
// Since this generated snippet is part of a map:
//
// map[string]common.OpenAPIDefinition: {
// "TYPE_NAME": {
// Schema: spec.Schema{ ... },
// },
// }
//
// For compliance with gofmt -s it's important we elide the
// struct type. The type is implied by the map and will be
// removed otherwise.
g.Do("{\n"+
"Schema: spec.Schema{\n"+
"SchemaProps: spec.SchemaProps{\n"+
"Type:$.type|raw${}.OpenAPISchemaType(),\n"+
"Format:$.type|raw${}.OpenAPISchemaFormat(),\n"+
"},\n"+
"},\n"+
"},\n", args)
return nil
}
g.Do("{\nSchema: spec.Schema{\nSchemaProps: spec.SchemaProps{\n", nil)
g.generateDescription(t.CommentLines)
g.Do("Properties: map[string]$.SpecSchemaType|raw${\n", args)
required, err := g.generateMembers(t, []string{})
if err != nil {
return err
}
g.Do("},\n", nil)
if len(required) > 0 {
g.Do("Required: []string{\"$.$\"},\n", strings.Join(required, "\",\""))
}
g.Do("},\n", nil)
if err := g.generateExtensions(t.CommentLines); err != nil {
return err
}
g.Do("},\n", nil)
g.Do("Dependencies: []string{\n", args)
// Map order is undefined, sort them or we may get a different file generated each time.
keys := []string{}
for k := range g.refTypes {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
v := g.refTypes[k]
if t, _ := openapi.GetOpenAPITypeFormat(v.String()); t != "" {
// This is a known type, we do not need a reference to it
// Will eliminate special case of time.Time
continue
}
g.Do("\"$.$\",", k)
}
g.Do("},\n},\n", nil)
}
return nil
}
func (g openAPITypeWriter) generateExtensions(CommentLines []string) error {
tagValues := getOpenAPITagValue(CommentLines)
type NameValue struct {
Name, Value string
}
extensions := []NameValue{}
for _, val := range tagValues {
if strings.HasPrefix(val, tagExtensionPrefix) {
parts := strings.SplitN(val, ":", 2)
if len(parts) != 2 {
return fmt.Errorf("invalid extension value: %v", val)
}
extensions = append(extensions, NameValue{parts[0], parts[1]})
}
}
patchMergeKeyTag, err := getSingleTagsValue(CommentLines, tagPatchMergeKey)
if err != nil {
return err
}
if len(patchMergeKeyTag) > 0 {
extensions = append(extensions, NameValue{tagExtensionPrefix + patchMergeKeyExtensionName, patchMergeKeyTag})
}
patchStrategyTag, err := getSingleTagsValue(CommentLines, tagPatchStrategy)
if err != nil {
return err
}
if len(patchStrategyTag) > 0 {
extensions = append(extensions, NameValue{tagExtensionPrefix + patchStrategyExtensionName, patchStrategyTag})
}
if len(extensions) == 0 {
return nil
}
g.Do("VendorExtensible: spec.VendorExtensible{\nExtensions: spec.Extensions{\n", nil)
for _, extension := range extensions {
g.Do("\"$.$\": ", extension.Name)
g.Do("\"$.$\",\n", extension.Value)
}
g.Do("},\n},\n", nil)
return nil
}
// TODO(#44005): Move this validation outside of this generator (probably to policy verifier)
func (g openAPITypeWriter) validatePatchTags(m *types.Member, parent *types.Type) error {
patchMergeKeyStructTag, patchStrategyStructTag := getPatchTags(m)
patchMergeKeyCommentTag, err := getSingleTagsValue(m.CommentLines, tagPatchMergeKey)
if err != nil {
return err
}
patchStrategyCommentTag, err := getSingleTagsValue(m.CommentLines, tagPatchStrategy)
if err != nil {
return err
}
if patchMergeKeyStructTag != patchMergeKeyCommentTag {
return fmt.Errorf("patchMergeKey in comment and struct tags should match for member (%s) of (%s)",
m.Name, parent.Name.String())
}
if patchStrategyStructTag != patchStrategyCommentTag {
return fmt.Errorf("patchStrategy in comment and struct tags should match for member (%s) of (%s)",
m.Name, parent.Name.String())
}
return nil
}
func (g openAPITypeWriter) generateDescription(CommentLines []string) {
var buffer bytes.Buffer
delPrevChar := func() {
if buffer.Len() > 0 {
buffer.Truncate(buffer.Len() - 1) // Delete the last " " or "\n"
}
}
for _, line := range CommentLines {
// Ignore all lines after ---
if line == "---" {
break
}
line = strings.TrimRight(line, " ")
leading := strings.TrimLeft(line, " ")
switch {
case len(line) == 0: // Keep paragraphs
delPrevChar()
buffer.WriteString("\n\n")
case strings.HasPrefix(leading, "TODO"): // Ignore one line TODOs
case strings.HasPrefix(leading, "+"): // Ignore instructions to go2idl
default:
if strings.HasPrefix(line, " ") || strings.HasPrefix(line, "\t") {
delPrevChar()
line = "\n" + line + "\n" // Replace it with newline. This is useful when we have a line with: "Example:\n\tJSON-someting..."
} else {
line += " "
}
buffer.WriteString(line)
}
}
postDoc := strings.TrimRight(buffer.String(), "\n")
postDoc = strings.Replace(postDoc, "\\\"", "\"", -1) // replace user's \" to "
postDoc = strings.Replace(postDoc, "\"", "\\\"", -1) // Escape "
postDoc = strings.Replace(postDoc, "\n", "\\n", -1)
postDoc = strings.Replace(postDoc, "\t", "\\t", -1)
postDoc = strings.Trim(postDoc, " ")
if postDoc != "" {
g.Do("Description: \"$.$\",\n", postDoc)
}
}
func (g openAPITypeWriter) generateProperty(m *types.Member, parent *types.Type) error {
name := getReferableName(m)
if name == "" {
return nil
}
if err := g.validatePatchTags(m, parent); err != nil {
return err
}
g.Do("\"$.$\": {\n", name)
if err := g.generateExtensions(m.CommentLines); err != nil {
return err
}
g.Do("SchemaProps: spec.SchemaProps{\n", nil)
g.generateDescription(m.CommentLines)
jsonTags := getJsonTags(m)
if len(jsonTags) > 1 && jsonTags[1] == "string" {
g.generateSimpleProperty("string", "")
g.Do("},\n},\n", nil)
return nil
}
t := resolveAliasAndPtrType(m.Type)
// If we can get a openAPI type and format for this type, we consider it to be simple property
typeString, format := openapi.GetOpenAPITypeFormat(t.String())
if typeString != "" {
g.generateSimpleProperty(typeString, format)
g.Do("},\n},\n", nil)
return nil
}
switch t.Kind {
case types.Builtin:
return fmt.Errorf("please add type %v to getOpenAPITypeFormat function", t)
case types.Map:
if err := g.generateMapProperty(t); err != nil {
return err
}
case types.Slice, types.Array:
if err := g.generateSliceProperty(t); err != nil {
return err
}
case types.Struct, types.Interface:
g.generateReferenceProperty(t)
default:
return fmt.Errorf("cannot generate spec for type %v", t)
}
g.Do("},\n},\n", nil)
return g.Error()
}
func (g openAPITypeWriter) generateSimpleProperty(typeString, format string) {
g.Do("Type: []string{\"$.$\"},\n", typeString)
g.Do("Format: \"$.$\",\n", format)
}
func (g openAPITypeWriter) generateReferenceProperty(t *types.Type) {
g.refTypes[t.Name.String()] = t
g.Do("Ref: ref(\"$.$\"),\n", t.Name.String())
}
func resolveAliasAndPtrType(t *types.Type) *types.Type {
var prev *types.Type
for prev != t {
prev = t
if t.Kind == types.Alias {
t = t.Underlying
}
if t.Kind == types.Pointer {
t = t.Elem
}
}
return t
}
func (g openAPITypeWriter) generateMapProperty(t *types.Type) error {
keyType := resolveAliasAndPtrType(t.Key)
elemType := resolveAliasAndPtrType(t.Elem)
// According to OpenAPI examples, only map from string is supported
if keyType.Name.Name != "string" {
return fmt.Errorf("map with non-string keys are not supported by OpenAPI in %v", t)
}
g.Do("Type: []string{\"object\"},\n", nil)
g.Do("AdditionalProperties: &spec.SchemaOrBool{\nSchema: &spec.Schema{\nSchemaProps: spec.SchemaProps{\n", nil)
typeString, format := openapi.GetOpenAPITypeFormat(elemType.String())
if typeString != "" {
g.generateSimpleProperty(typeString, format)
g.Do("},\n},\n},\n", nil)
return nil
}
switch elemType.Kind {
case types.Builtin:
return fmt.Errorf("please add type %v to getOpenAPITypeFormat function", elemType)
case types.Struct:
g.generateReferenceProperty(elemType)
case types.Slice, types.Array:
g.generateSliceProperty(elemType)
default:
return fmt.Errorf("map Element kind %v is not supported in %v", elemType.Kind, t.Name)
}
g.Do("},\n},\n},\n", nil)
return nil
}
func (g openAPITypeWriter) generateSliceProperty(t *types.Type) error {
elemType := resolveAliasAndPtrType(t.Elem)
g.Do("Type: []string{\"array\"},\n", nil)
g.Do("Items: &spec.SchemaOrArray{\nSchema: &spec.Schema{\nSchemaProps: spec.SchemaProps{\n", nil)
typeString, format := openapi.GetOpenAPITypeFormat(elemType.String())
if typeString != "" {
g.generateSimpleProperty(typeString, format)
g.Do("},\n},\n},\n", nil)
return nil
}
switch elemType.Kind {
case types.Builtin:
return fmt.Errorf("please add type %v to getOpenAPITypeFormat function", elemType)
case types.Struct:
g.generateReferenceProperty(elemType)
default:
return fmt.Errorf("slice Element kind %v is not supported in %v", elemType.Kind, t)
}
g.Do("},\n},\n},\n", nil)
return nil
}

View File

@ -0,0 +1,426 @@
/*
Copyright 2016 The Kubernetes Authors.
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 generators
import (
"bytes"
"fmt"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"k8s.io/gengo/generator"
"k8s.io/gengo/namer"
"k8s.io/gengo/parser"
"k8s.io/gengo/types"
)
func construct(t *testing.T, files map[string]string, testNamer namer.Namer) (*parser.Builder, types.Universe, []*types.Type) {
b := parser.New()
for name, src := range files {
if err := b.AddFileForTest(filepath.Dir(name), name, []byte(src)); err != nil {
t.Fatal(err)
}
}
u, err := b.FindTypes()
if err != nil {
t.Fatal(err)
}
orderer := namer.Orderer{Namer: testNamer}
o := orderer.OrderUniverse(u)
return b, u, o
}
func testOpenAPITypeWritter(t *testing.T, code string) (error, *assert.Assertions, *bytes.Buffer) {
assert := assert.New(t)
var testFiles = map[string]string{
"base/foo/bar.go": code,
}
rawNamer := namer.NewRawNamer("o", nil)
namers := namer.NameSystems{
"raw": namer.NewRawNamer("", nil),
}
builder, universe, _ := construct(t, testFiles, rawNamer)
context, err := generator.NewContext(builder, namers, "raw")
if err != nil {
t.Fatal(err)
}
buffer := &bytes.Buffer{}
sw := generator.NewSnippetWriter(buffer, context, "$", "$")
blahT := universe.Type(types.Name{Package: "base/foo", Name: "Blah"})
return newOpenAPITypeWriter(sw).generate(blahT), assert, buffer
}
func TestSimple(t *testing.T) {
err, assert, buffer := testOpenAPITypeWritter(t, `
package foo
// Blah is a test.
// +k8s:openapi-gen=true
// +k8s:openapi-gen=x-kubernetes-type-tag:type_test
type Blah struct {
// A simple string
String string
// A simple int
Int int `+"`"+`json:",omitempty"`+"`"+`
// An int considered string simple int
IntString int `+"`"+`json:",string"`+"`"+`
// A simple int64
Int64 int64
// A simple int32
Int32 int32
// A simple int16
Int16 int16
// A simple int8
Int8 int8
// A simple int
Uint uint
// A simple int64
Uint64 uint64
// A simple int32
Uint32 uint32
// A simple int16
Uint16 uint16
// A simple int8
Uint8 uint8
// A simple byte
Byte byte
// A simple boolean
Bool bool
// A simple float64
Float64 float64
// A simple float32
Float32 float32
// a base64 encoded characters
ByteArray []byte
// a member with an extension
// +k8s:openapi-gen=x-kubernetes-member-tag:member_test
WithExtension string
// a member with struct tag as extension
// +patchStrategy=ps
// +patchMergeKey=pmk
WithStructTagExtension string `+"`"+`patchStrategy:"ps" patchMergeKey:"pmk"`+"`"+`
}
`)
if err != nil {
t.Fatal(err)
}
assert.Equal(`"base/foo.Blah": {
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "Blah is a test.",
Properties: map[string]spec.Schema{
"String": {
SchemaProps: spec.SchemaProps{
Description: "A simple string",
Type: []string{"string"},
Format: "",
},
},
"Int64": {
SchemaProps: spec.SchemaProps{
Description: "A simple int64",
Type: []string{"integer"},
Format: "int64",
},
},
"Int32": {
SchemaProps: spec.SchemaProps{
Description: "A simple int32",
Type: []string{"integer"},
Format: "int32",
},
},
"Int16": {
SchemaProps: spec.SchemaProps{
Description: "A simple int16",
Type: []string{"integer"},
Format: "int32",
},
},
"Int8": {
SchemaProps: spec.SchemaProps{
Description: "A simple int8",
Type: []string{"integer"},
Format: "byte",
},
},
"Uint": {
SchemaProps: spec.SchemaProps{
Description: "A simple int",
Type: []string{"integer"},
Format: "int32",
},
},
"Uint64": {
SchemaProps: spec.SchemaProps{
Description: "A simple int64",
Type: []string{"integer"},
Format: "int64",
},
},
"Uint32": {
SchemaProps: spec.SchemaProps{
Description: "A simple int32",
Type: []string{"integer"},
Format: "int64",
},
},
"Uint16": {
SchemaProps: spec.SchemaProps{
Description: "A simple int16",
Type: []string{"integer"},
Format: "int32",
},
},
"Uint8": {
SchemaProps: spec.SchemaProps{
Description: "A simple int8",
Type: []string{"integer"},
Format: "byte",
},
},
"Byte": {
SchemaProps: spec.SchemaProps{
Description: "A simple byte",
Type: []string{"integer"},
Format: "byte",
},
},
"Bool": {
SchemaProps: spec.SchemaProps{
Description: "A simple boolean",
Type: []string{"boolean"},
Format: "",
},
},
"Float64": {
SchemaProps: spec.SchemaProps{
Description: "A simple float64",
Type: []string{"number"},
Format: "double",
},
},
"Float32": {
SchemaProps: spec.SchemaProps{
Description: "A simple float32",
Type: []string{"number"},
Format: "float",
},
},
"ByteArray": {
SchemaProps: spec.SchemaProps{
Description: "a base64 encoded characters",
Type: []string{"string"},
Format: "byte",
},
},
"WithExtension": {
VendorExtensible: spec.VendorExtensible{
Extensions: spec.Extensions{
"x-kubernetes-member-tag": "member_test",
},
},
SchemaProps: spec.SchemaProps{
Description: "a member with an extension",
Type: []string{"string"},
Format: "",
},
},
"WithStructTagExtension": {
VendorExtensible: spec.VendorExtensible{
Extensions: spec.Extensions{
"x-kubernetes-patch-merge-key": "pmk",
"x-kubernetes-patch-strategy": "ps",
},
},
SchemaProps: spec.SchemaProps{
Description: "a member with struct tag as extension",
Type: []string{"string"},
Format: "",
},
},
},
Required: []string{"String","Int64","Int32","Int16","Int8","Uint","Uint64","Uint32","Uint16","Uint8","Byte","Bool","Float64","Float32","ByteArray","WithExtension","WithStructTagExtension"},
},
VendorExtensible: spec.VendorExtensible{
Extensions: spec.Extensions{
"x-kubernetes-type-tag": "type_test",
},
},
},
Dependencies: []string{
},
},
`, buffer.String())
}
func TestFailingSample1(t *testing.T) {
err, assert, _ := testOpenAPITypeWritter(t, `
package foo
// Map sample tests openAPIGen.generateMapProperty method.
type Blah struct {
// A sample String to String map
StringToArray map[string]map[string]string
}
`)
if assert.Error(err, "An error was expected") {
assert.Equal(err, fmt.Errorf("map Element kind Map is not supported in map[string]map[string]string"))
}
}
func TestFailingSample2(t *testing.T) {
err, assert, _ := testOpenAPITypeWritter(t, `
package foo
// Map sample tests openAPIGen.generateMapProperty method.
type Blah struct {
// A sample String to String map
StringToArray map[int]string
} `)
if assert.Error(err, "An error was expected") {
assert.Equal(err, fmt.Errorf("map with non-string keys are not supported by OpenAPI in map[int]string"))
}
}
func TestCustomDef(t *testing.T) {
err, assert, buffer := testOpenAPITypeWritter(t, `
package foo
import openapi "k8s.io/kube-openapi/pkg/common"
type Blah struct {
}
func (_ Blah) OpenAPIDefinition() openapi.OpenAPIDefinition {
return openapi.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "date-time",
},
},
}
}
`)
if err != nil {
t.Fatal(err)
}
assert.Equal(`"base/foo.Blah": foo.Blah{}.OpenAPIDefinition(),
`, buffer.String())
}
func TestCustomDefs(t *testing.T) {
err, assert, buffer := testOpenAPITypeWritter(t, `
package foo
type Blah struct {
}
func (_ Blah) OpenAPISchemaType() []string { return []string{"string"} }
func (_ Blah) OpenAPISchemaFormat() string { return "date-time" }
`)
if err != nil {
t.Fatal(err)
}
assert.Equal(`"base/foo.Blah": {
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Type:foo.Blah{}.OpenAPISchemaType(),
Format:foo.Blah{}.OpenAPISchemaFormat(),
},
},
},
`, buffer.String())
}
func TestPointer(t *testing.T) {
err, assert, buffer := testOpenAPITypeWritter(t, `
package foo
// PointerSample demonstrate pointer's properties
type Blah struct {
// A string pointer
StringPointer *string
// A struct pointer
StructPointer *Blah
// A slice pointer
SlicePointer *[]string
// A map pointer
MapPointer *map[string]string
}
`)
if err != nil {
t.Fatal(err)
}
assert.Equal(`"base/foo.Blah": {
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "PointerSample demonstrate pointer's properties",
Properties: map[string]spec.Schema{
"StringPointer": {
SchemaProps: spec.SchemaProps{
Description: "A string pointer",
Type: []string{"string"},
Format: "",
},
},
"StructPointer": {
SchemaProps: spec.SchemaProps{
Description: "A struct pointer",
Ref: ref("base/foo.Blah"),
},
},
"SlicePointer": {
SchemaProps: spec.SchemaProps{
Description: "A slice pointer",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "",
},
},
},
},
},
"MapPointer": {
SchemaProps: spec.SchemaProps{
Description: "A map pointer",
Type: []string{"object"},
AdditionalProperties: &spec.SchemaOrBool{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "",
},
},
},
},
},
},
Required: []string{"StringPointer","StructPointer","SlicePointer","MapPointer"},
},
},
Dependencies: []string{
"base/foo.Blah",},
},
`, buffer.String())
}