Revert "Aggregate OpenAPI specs"
This reverts commit 1a1d9a0394cbdb1d1e2412ae8f0157799eb5329c. Kubernetes-commit: 88868402b863b1f59a339d3a218bf62c264721ee
This commit is contained in:
parent
473678e36a
commit
2de4d08b67
|
@ -27,7 +27,6 @@ import (
|
|||
"github.com/emicklei/go-restful-swagger12"
|
||||
"github.com/golang/glog"
|
||||
|
||||
"github.com/go-openapi/spec"
|
||||
"k8s.io/apimachinery/pkg/apimachinery"
|
||||
"k8s.io/apimachinery/pkg/apimachinery/registered"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
@ -43,7 +42,6 @@ import (
|
|||
apirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
"k8s.io/apiserver/pkg/server/healthz"
|
||||
"k8s.io/apiserver/pkg/server/openapi"
|
||||
"k8s.io/apiserver/pkg/server/routes"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
)
|
||||
|
@ -131,9 +129,6 @@ type GenericAPIServer struct {
|
|||
swaggerConfig *swagger.Config
|
||||
openAPIConfig *openapicommon.Config
|
||||
|
||||
// Enables updating OpenAPI spec using update method.
|
||||
OpenAPIService *openapi.OpenAPIService
|
||||
|
||||
// PostStartHooks are each called after the server has started listening, in a separate go func for each
|
||||
// with no guarantee of ordering between them. The map key is a name used for error reporting.
|
||||
// It may kill the process with a panic if it wishes to by returning an error.
|
||||
|
@ -173,9 +168,6 @@ type DelegationTarget interface {
|
|||
|
||||
// ListedPaths returns the paths for supporting an index
|
||||
ListedPaths() []string
|
||||
|
||||
// OpenAPISpec returns the OpenAPI spec of the delegation target if exists, nil otherwise.
|
||||
OpenAPISpec() *spec.Swagger
|
||||
}
|
||||
|
||||
func (s *GenericAPIServer) UnprotectedHandler() http.Handler {
|
||||
|
@ -191,9 +183,6 @@ func (s *GenericAPIServer) HealthzChecks() []healthz.HealthzChecker {
|
|||
func (s *GenericAPIServer) ListedPaths() []string {
|
||||
return s.listedPathProvider.ListedPaths()
|
||||
}
|
||||
func (s *GenericAPIServer) OpenAPISpec() *spec.Swagger {
|
||||
return s.OpenAPIService.GetSpec()
|
||||
}
|
||||
|
||||
var EmptyDelegate = emptyDelegate{
|
||||
requestContextMapper: apirequest.NewRequestContextMapper(),
|
||||
|
@ -218,9 +207,6 @@ func (s emptyDelegate) ListedPaths() []string {
|
|||
func (s emptyDelegate) RequestContextMapper() apirequest.RequestContextMapper {
|
||||
return s.requestContextMapper
|
||||
}
|
||||
func (s emptyDelegate) OpenAPISpec() *spec.Swagger {
|
||||
return nil
|
||||
}
|
||||
|
||||
// RequestContextMapper is exposed so that third party resource storage can be build in a different location.
|
||||
// TODO refactor third party resource storage
|
||||
|
@ -243,22 +229,17 @@ func (s *GenericAPIServer) PrepareRun() preparedGenericAPIServer {
|
|||
if s.swaggerConfig != nil {
|
||||
routes.Swagger{Config: s.swaggerConfig}.Install(s.Handler.GoRestfulContainer)
|
||||
}
|
||||
s.PrepareOpenAPIService()
|
||||
if s.openAPIConfig != nil {
|
||||
routes.OpenAPI{
|
||||
Config: s.openAPIConfig,
|
||||
}.Install(s.Handler.GoRestfulContainer, s.Handler.NonGoRestfulMux)
|
||||
}
|
||||
|
||||
s.installHealthz()
|
||||
|
||||
return preparedGenericAPIServer{s}
|
||||
}
|
||||
|
||||
// PrepareOpenAPIService installs OpenAPI handler if it does not exists.
|
||||
func (s *GenericAPIServer) PrepareOpenAPIService() {
|
||||
if s.openAPIConfig != nil && s.OpenAPIService == nil {
|
||||
s.OpenAPIService = routes.OpenAPI{
|
||||
Config: s.openAPIConfig,
|
||||
}.Install(s.Handler.GoRestfulContainer, s.Handler.NonGoRestfulMux)
|
||||
}
|
||||
}
|
||||
|
||||
// Run spawns the secure http server. It only returns if stopCh is closed
|
||||
// or the secure port cannot be listened on initially.
|
||||
func (s preparedGenericAPIServer) Run(stopCh <-chan struct{}) error {
|
||||
|
|
|
@ -1,482 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 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 openapi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/go-openapi/spec"
|
||||
|
||||
"k8s.io/apimachinery/pkg/conversion"
|
||||
"k8s.io/apiserver/pkg/util/trie"
|
||||
)
|
||||
|
||||
const (
|
||||
DEFINITION_PREFIX = "#/definitions/"
|
||||
)
|
||||
|
||||
var cloner = conversion.NewCloner()
|
||||
|
||||
// Run a walkRefCallback method on all references of an OpenAPI spec
|
||||
type walkAllRefs struct {
|
||||
// walkRefCallback will be called on each reference and the return value
|
||||
// will replace that reference. This will allow the callers to change
|
||||
// all/some references of an spec (e.g. useful in renaming definitions).
|
||||
walkRefCallback func(ref spec.Ref) spec.Ref
|
||||
|
||||
// The spec to walk through.
|
||||
root *spec.Swagger
|
||||
}
|
||||
|
||||
func newWalkAllRefs(walkRef func(ref spec.Ref) spec.Ref, sp *spec.Swagger) *walkAllRefs {
|
||||
return &walkAllRefs{
|
||||
walkRefCallback: walkRef,
|
||||
root: sp,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *walkAllRefs) walkRef(ref spec.Ref) spec.Ref {
|
||||
if ref.String() != "" {
|
||||
refStr := ref.String()
|
||||
// References that start with #/definitions/ has a definition
|
||||
// inside the same spec file. If that is the case, walk through
|
||||
// those definitions too.
|
||||
// We do not support external references yet.
|
||||
if strings.HasPrefix(refStr, DEFINITION_PREFIX) {
|
||||
def := s.root.Definitions[refStr[len(DEFINITION_PREFIX):]]
|
||||
s.walkSchema(&def)
|
||||
}
|
||||
}
|
||||
return s.walkRefCallback(ref)
|
||||
}
|
||||
|
||||
func (s *walkAllRefs) walkSchema(schema *spec.Schema) {
|
||||
if schema == nil {
|
||||
return
|
||||
}
|
||||
schema.Ref = s.walkRef(schema.Ref)
|
||||
for _, v := range schema.Definitions {
|
||||
s.walkSchema(&v)
|
||||
}
|
||||
for _, v := range schema.Properties {
|
||||
s.walkSchema(&v)
|
||||
}
|
||||
for _, v := range schema.PatternProperties {
|
||||
s.walkSchema(&v)
|
||||
}
|
||||
for _, v := range schema.AllOf {
|
||||
s.walkSchema(&v)
|
||||
}
|
||||
for _, v := range schema.AnyOf {
|
||||
s.walkSchema(&v)
|
||||
}
|
||||
for _, v := range schema.OneOf {
|
||||
s.walkSchema(&v)
|
||||
}
|
||||
if schema.Not != nil {
|
||||
s.walkSchema(schema.Not)
|
||||
}
|
||||
if schema.AdditionalProperties != nil && schema.AdditionalProperties.Schema != nil {
|
||||
s.walkSchema(schema.AdditionalProperties.Schema)
|
||||
}
|
||||
if schema.AdditionalItems != nil && schema.AdditionalItems.Schema != nil {
|
||||
s.walkSchema(schema.AdditionalItems.Schema)
|
||||
}
|
||||
if schema.Items != nil {
|
||||
if schema.Items.Schema != nil {
|
||||
s.walkSchema(schema.Items.Schema)
|
||||
}
|
||||
for _, v := range schema.Items.Schemas {
|
||||
s.walkSchema(&v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *walkAllRefs) walkParams(params []spec.Parameter) {
|
||||
if params == nil {
|
||||
return
|
||||
}
|
||||
for _, param := range params {
|
||||
param.Ref = s.walkRef(param.Ref)
|
||||
s.walkSchema(param.Schema)
|
||||
if param.Items != nil {
|
||||
param.Items.Ref = s.walkRef(param.Items.Ref)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *walkAllRefs) walkResponse(resp *spec.Response) {
|
||||
if resp == nil {
|
||||
return
|
||||
}
|
||||
resp.Ref = s.walkRef(resp.Ref)
|
||||
s.walkSchema(resp.Schema)
|
||||
}
|
||||
|
||||
func (s *walkAllRefs) walkOperation(op *spec.Operation) {
|
||||
if op == nil {
|
||||
return
|
||||
}
|
||||
s.walkParams(op.Parameters)
|
||||
if op.Responses == nil {
|
||||
return
|
||||
}
|
||||
s.walkResponse(op.Responses.Default)
|
||||
for _, r := range op.Responses.StatusCodeResponses {
|
||||
s.walkResponse(&r)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *walkAllRefs) Start() {
|
||||
for _, pathItem := range s.root.Paths.Paths {
|
||||
s.walkParams(pathItem.Parameters)
|
||||
s.walkOperation(pathItem.Delete)
|
||||
s.walkOperation(pathItem.Get)
|
||||
s.walkOperation(pathItem.Head)
|
||||
s.walkOperation(pathItem.Options)
|
||||
s.walkOperation(pathItem.Patch)
|
||||
s.walkOperation(pathItem.Post)
|
||||
s.walkOperation(pathItem.Put)
|
||||
}
|
||||
}
|
||||
|
||||
// FilterSpecByPaths remove unnecessary paths and unused definitions.
|
||||
func FilterSpecByPaths(sp *spec.Swagger, keepPathPrefixes []string) {
|
||||
// First remove unwanted paths
|
||||
prefixes := trie.New(keepPathPrefixes)
|
||||
orgPaths := sp.Paths
|
||||
if orgPaths == nil {
|
||||
return
|
||||
}
|
||||
sp.Paths = &spec.Paths{
|
||||
VendorExtensible: orgPaths.VendorExtensible,
|
||||
Paths: map[string]spec.PathItem{},
|
||||
}
|
||||
for path, pathItem := range orgPaths.Paths {
|
||||
if !prefixes.HasPrefix(path) {
|
||||
continue
|
||||
}
|
||||
sp.Paths.Paths[path] = pathItem
|
||||
}
|
||||
|
||||
// Walk all references to find all definition references.
|
||||
usedDefinitions := map[string]bool{}
|
||||
|
||||
newWalkAllRefs(func(ref spec.Ref) spec.Ref {
|
||||
if ref.String() != "" {
|
||||
refStr := ref.String()
|
||||
if strings.HasPrefix(refStr, DEFINITION_PREFIX) {
|
||||
usedDefinitions[refStr[len(DEFINITION_PREFIX):]] = true
|
||||
}
|
||||
}
|
||||
return ref
|
||||
}, sp).Start()
|
||||
|
||||
// Remove unused definitions
|
||||
orgDefinitions := sp.Definitions
|
||||
sp.Definitions = spec.Definitions{}
|
||||
for k, v := range orgDefinitions {
|
||||
if usedDefinitions[k] {
|
||||
sp.Definitions[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func equalSchemaMap(s1, s2 map[string]spec.Schema) bool {
|
||||
if len(s1) != len(s2) {
|
||||
return false
|
||||
}
|
||||
for k, v := range s1 {
|
||||
v2, found := s2[k]
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
if !EqualSchema(&v, &v2) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func equalSchemaArray(s1, s2 []spec.Schema) bool {
|
||||
if s1 == nil || s2 == nil {
|
||||
return s1 == nil && s2 == nil
|
||||
}
|
||||
if len(s1) != len(s2) {
|
||||
return false
|
||||
}
|
||||
for _, v1 := range s1 {
|
||||
found := false
|
||||
for _, v2 := range s2 {
|
||||
if EqualSchema(&v1, &v2) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for _, v2 := range s2 {
|
||||
found := false
|
||||
for _, v1 := range s1 {
|
||||
if EqualSchema(&v1, &v2) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func equalSchemaOrBool(s1, s2 *spec.SchemaOrBool) bool {
|
||||
if s1 == nil || s2 == nil {
|
||||
return s1 == s2
|
||||
}
|
||||
if s1.Allows != s2.Allows {
|
||||
return false
|
||||
}
|
||||
if !EqualSchema(s1.Schema, s2.Schema) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func equalSchemaOrArray(s1, s2 *spec.SchemaOrArray) bool {
|
||||
if s1 == nil || s2 == nil {
|
||||
return s1 == s2
|
||||
}
|
||||
if !EqualSchema(s1.Schema, s2.Schema) {
|
||||
return false
|
||||
}
|
||||
if !equalSchemaArray(s1.Schemas, s2.Schemas) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func equalStringArray(s1, s2 []string) bool {
|
||||
if len(s1) != len(s2) {
|
||||
return false
|
||||
}
|
||||
for _, v1 := range s1 {
|
||||
found := false
|
||||
for _, v2 := range s2 {
|
||||
if v1 == v2 {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for _, v2 := range s2 {
|
||||
found := false
|
||||
for _, v1 := range s1 {
|
||||
if v1 == v2 {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func equalFloatPointer(s1, s2 *float64) bool {
|
||||
if s1 == nil || s2 == nil {
|
||||
return s1 == s2
|
||||
}
|
||||
return *s1 == *s2
|
||||
}
|
||||
|
||||
func equalIntPointer(s1, s2 *int64) bool {
|
||||
if s1 == nil || s2 == nil {
|
||||
return s1 == s2
|
||||
}
|
||||
return *s1 == *s2
|
||||
}
|
||||
|
||||
// EqualSchema returns true if models have the same properties and references
|
||||
// even if they have different documentation.
|
||||
func EqualSchema(s1, s2 *spec.Schema) bool {
|
||||
if s1 == nil || s2 == nil {
|
||||
return s1 == s2
|
||||
}
|
||||
if s1.Ref.String() != s2.Ref.String() {
|
||||
return false
|
||||
}
|
||||
if !equalSchemaMap(s1.Definitions, s2.Definitions) {
|
||||
return false
|
||||
}
|
||||
if !equalSchemaMap(s1.Properties, s2.Properties) {
|
||||
fmt.Println("Not equal props")
|
||||
return false
|
||||
}
|
||||
if !equalSchemaMap(s1.PatternProperties, s2.PatternProperties) {
|
||||
return false
|
||||
}
|
||||
if !equalSchemaArray(s1.AllOf, s2.AllOf) {
|
||||
return false
|
||||
}
|
||||
if !equalSchemaArray(s1.AnyOf, s2.AnyOf) {
|
||||
return false
|
||||
}
|
||||
if !equalSchemaArray(s1.OneOf, s2.OneOf) {
|
||||
return false
|
||||
}
|
||||
if !EqualSchema(s1.Not, s2.Not) {
|
||||
return false
|
||||
}
|
||||
if !equalSchemaOrBool(s1.AdditionalProperties, s2.AdditionalProperties) {
|
||||
return false
|
||||
}
|
||||
if !equalSchemaOrBool(s1.AdditionalItems, s2.AdditionalItems) {
|
||||
return false
|
||||
}
|
||||
if !equalSchemaOrArray(s1.Items, s2.Items) {
|
||||
return false
|
||||
}
|
||||
if !equalStringArray(s1.Type, s2.Type) {
|
||||
return false
|
||||
}
|
||||
if s1.Format != s2.Format {
|
||||
return false
|
||||
}
|
||||
if !equalFloatPointer(s1.Minimum, s2.Minimum) {
|
||||
return false
|
||||
}
|
||||
if !equalFloatPointer(s1.Maximum, s2.Maximum) {
|
||||
return false
|
||||
}
|
||||
if s1.ExclusiveMaximum != s2.ExclusiveMaximum {
|
||||
return false
|
||||
}
|
||||
if s1.ExclusiveMinimum != s2.ExclusiveMinimum {
|
||||
return false
|
||||
}
|
||||
if !equalFloatPointer(s1.MultipleOf, s2.MultipleOf) {
|
||||
return false
|
||||
}
|
||||
if !equalIntPointer(s1.MaxLength, s2.MaxLength) {
|
||||
return false
|
||||
}
|
||||
if !equalIntPointer(s1.MinLength, s2.MinLength) {
|
||||
return false
|
||||
}
|
||||
if !equalIntPointer(s1.MaxItems, s2.MaxItems) {
|
||||
return false
|
||||
}
|
||||
if !equalIntPointer(s1.MinItems, s2.MinItems) {
|
||||
return false
|
||||
}
|
||||
if s1.Pattern != s2.Pattern {
|
||||
return false
|
||||
}
|
||||
if s1.UniqueItems != s2.UniqueItems {
|
||||
return false
|
||||
}
|
||||
if !equalIntPointer(s1.MaxProperties, s2.MaxProperties) {
|
||||
return false
|
||||
}
|
||||
if !equalIntPointer(s1.MinProperties, s2.MinProperties) {
|
||||
return false
|
||||
}
|
||||
if !equalStringArray(s1.Required, s2.Required) {
|
||||
return false
|
||||
}
|
||||
return len(s1.Enum) == 0 && len(s2.Enum) == 0 && len(s1.Dependencies) == 0 && len(s2.Dependencies) == 0
|
||||
}
|
||||
|
||||
func renameDefinition(s *spec.Swagger, old, new string) {
|
||||
old_ref := DEFINITION_PREFIX + old
|
||||
new_ref := DEFINITION_PREFIX + new
|
||||
newWalkAllRefs(func(ref spec.Ref) spec.Ref {
|
||||
if ref.String() == old_ref {
|
||||
return spec.MustCreateRef(new_ref)
|
||||
}
|
||||
return ref
|
||||
}, s).Start()
|
||||
s.Definitions[new] = s.Definitions[old]
|
||||
delete(s.Definitions, old)
|
||||
}
|
||||
|
||||
// Copy paths and definitions from source to dest, rename definitions if needed.
|
||||
// dest will be mutated, and source will not be changed.
|
||||
func MergeSpecs(dest, source *spec.Swagger) error {
|
||||
source, err := CloneSpec(source)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for k, v := range source.Paths.Paths {
|
||||
if _, found := dest.Paths.Paths[k]; found {
|
||||
return fmt.Errorf("Unable to merge: Duplicated path %s", k)
|
||||
}
|
||||
dest.Paths.Paths[k] = v
|
||||
}
|
||||
usedNames := map[string]bool{}
|
||||
for k := range dest.Definitions {
|
||||
usedNames[k] = true
|
||||
}
|
||||
type Rename struct {
|
||||
from, to string
|
||||
}
|
||||
renames := []Rename{}
|
||||
for k, v := range source.Definitions {
|
||||
v2, found := dest.Definitions[k]
|
||||
if found || usedNames[k] {
|
||||
if found && EqualSchema(&v, &v2) {
|
||||
continue
|
||||
}
|
||||
i := 2
|
||||
newName := fmt.Sprintf("%s_v%d", k, i)
|
||||
for usedNames[newName] {
|
||||
i += 1
|
||||
newName = fmt.Sprintf("%s_v%d", k, i)
|
||||
}
|
||||
renames = append(renames, Rename{from: k, to: newName})
|
||||
usedNames[newName] = true
|
||||
} else {
|
||||
usedNames[k] = true
|
||||
}
|
||||
}
|
||||
for _, r := range renames {
|
||||
renameDefinition(source, r.from, r.to)
|
||||
}
|
||||
for k, v := range source.Definitions {
|
||||
if _, found := dest.Definitions[k]; !found {
|
||||
dest.Definitions[k] = v
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Clone OpenAPI spec
|
||||
func CloneSpec(source *spec.Swagger) (*spec.Swagger, error) {
|
||||
if ret, err := cloner.DeepCopy(source); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return ret.(*spec.Swagger), nil
|
||||
}
|
||||
}
|
|
@ -1,520 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 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 openapi
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/go-openapi/spec"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestFilterSpecs(t *testing.T) {
|
||||
var spec1, spec1_filtered *spec.Swagger
|
||||
yaml.Unmarshal([]byte(`
|
||||
swagger: "2.0"
|
||||
paths:
|
||||
/test:
|
||||
post:
|
||||
tags:
|
||||
- "test"
|
||||
summary: "Test API"
|
||||
operationId: "addTest"
|
||||
parameters:
|
||||
- in: "body"
|
||||
name: "body"
|
||||
description: "test object"
|
||||
required: true
|
||||
schema:
|
||||
$ref: "#/definitions/Test"
|
||||
responses:
|
||||
405:
|
||||
description: "Invalid input"
|
||||
$ref: "#/definitions/InvalidInput"
|
||||
/othertest:
|
||||
post:
|
||||
tags:
|
||||
- "test2"
|
||||
summary: "Test2 API"
|
||||
operationId: "addTest2"
|
||||
consumes:
|
||||
- "application/json"
|
||||
produces:
|
||||
- "application/xml"
|
||||
parameters:
|
||||
- in: "body"
|
||||
name: "body"
|
||||
description: "test2 object"
|
||||
required: true
|
||||
schema:
|
||||
$ref: "#/definitions/Test2"
|
||||
definitions:
|
||||
Test:
|
||||
type: "object"
|
||||
properties:
|
||||
id:
|
||||
type: "integer"
|
||||
format: "int64"
|
||||
status:
|
||||
type: "string"
|
||||
description: "Status"
|
||||
InvalidInput:
|
||||
type: "string"
|
||||
format: "string"
|
||||
Test2:
|
||||
type: "object"
|
||||
properties:
|
||||
other:
|
||||
$ref: "#/definitions/Other"
|
||||
Other:
|
||||
type: "string"
|
||||
`), &spec1)
|
||||
yaml.Unmarshal([]byte(`
|
||||
swagger: "2.0"
|
||||
paths:
|
||||
/test:
|
||||
post:
|
||||
tags:
|
||||
- "test"
|
||||
summary: "Test API"
|
||||
operationId: "addTest"
|
||||
parameters:
|
||||
- in: "body"
|
||||
name: "body"
|
||||
description: "test object"
|
||||
required: true
|
||||
schema:
|
||||
$ref: "#/definitions/Test"
|
||||
responses:
|
||||
405:
|
||||
description: "Invalid input"
|
||||
$ref: "#/definitions/InvalidInput"
|
||||
definitions:
|
||||
Test:
|
||||
type: "object"
|
||||
properties:
|
||||
id:
|
||||
type: "integer"
|
||||
format: "int64"
|
||||
status:
|
||||
type: "string"
|
||||
description: "Status"
|
||||
InvalidInput:
|
||||
type: "string"
|
||||
format: "string"
|
||||
`), &spec1_filtered)
|
||||
assert := assert.New(t)
|
||||
FilterSpecByPaths(spec1, []string{"/test"})
|
||||
assert.Equal(spec1_filtered, spec1)
|
||||
}
|
||||
|
||||
func TestMergeSpecsSimple(t *testing.T) {
|
||||
var spec1, spec2, expected *spec.Swagger
|
||||
yaml.Unmarshal([]byte(`
|
||||
swagger: "2.0"
|
||||
paths:
|
||||
/test:
|
||||
post:
|
||||
tags:
|
||||
- "test"
|
||||
summary: "Test API"
|
||||
operationId: "addTest"
|
||||
parameters:
|
||||
- in: "body"
|
||||
name: "body"
|
||||
description: "test object"
|
||||
required: true
|
||||
schema:
|
||||
$ref: "#/definitions/Test"
|
||||
responses:
|
||||
405:
|
||||
description: "Invalid input"
|
||||
$ref: "#/definitions/InvalidInput"
|
||||
definitions:
|
||||
Test:
|
||||
type: "object"
|
||||
properties:
|
||||
id:
|
||||
type: "integer"
|
||||
format: "int64"
|
||||
status:
|
||||
type: "string"
|
||||
description: "Status"
|
||||
InvalidInput:
|
||||
type: "string"
|
||||
format: "string"
|
||||
`), &spec1)
|
||||
yaml.Unmarshal([]byte(`
|
||||
swagger: "2.0"
|
||||
paths:
|
||||
/othertest:
|
||||
post:
|
||||
tags:
|
||||
- "test2"
|
||||
summary: "Test2 API"
|
||||
operationId: "addTest2"
|
||||
consumes:
|
||||
- "application/json"
|
||||
produces:
|
||||
- "application/xml"
|
||||
parameters:
|
||||
- in: "body"
|
||||
name: "body"
|
||||
description: "test2 object"
|
||||
required: true
|
||||
schema:
|
||||
$ref: "#/definitions/Test2"
|
||||
definitions:
|
||||
Test2:
|
||||
type: "object"
|
||||
properties:
|
||||
other:
|
||||
$ref: "#/definitions/Other"
|
||||
Other:
|
||||
type: "string"
|
||||
`), &spec2)
|
||||
yaml.Unmarshal([]byte(`
|
||||
swagger: "2.0"
|
||||
paths:
|
||||
/test:
|
||||
post:
|
||||
tags:
|
||||
- "test"
|
||||
summary: "Test API"
|
||||
operationId: "addTest"
|
||||
parameters:
|
||||
- in: "body"
|
||||
name: "body"
|
||||
description: "test object"
|
||||
required: true
|
||||
schema:
|
||||
$ref: "#/definitions/Test"
|
||||
responses:
|
||||
405:
|
||||
description: "Invalid input"
|
||||
$ref: "#/definitions/InvalidInput"
|
||||
/othertest:
|
||||
post:
|
||||
tags:
|
||||
- "test2"
|
||||
summary: "Test2 API"
|
||||
operationId: "addTest2"
|
||||
consumes:
|
||||
- "application/json"
|
||||
produces:
|
||||
- "application/xml"
|
||||
parameters:
|
||||
- in: "body"
|
||||
name: "body"
|
||||
description: "test2 object"
|
||||
required: true
|
||||
schema:
|
||||
$ref: "#/definitions/Test2"
|
||||
definitions:
|
||||
Test:
|
||||
type: "object"
|
||||
properties:
|
||||
id:
|
||||
type: "integer"
|
||||
format: "int64"
|
||||
status:
|
||||
type: "string"
|
||||
description: "Status"
|
||||
InvalidInput:
|
||||
type: "string"
|
||||
format: "string"
|
||||
Test2:
|
||||
type: "object"
|
||||
properties:
|
||||
other:
|
||||
$ref: "#/definitions/Other"
|
||||
Other:
|
||||
type: "string"
|
||||
`), &expected)
|
||||
assert := assert.New(t)
|
||||
if !assert.NoError(MergeSpecs(spec1, spec2)) {
|
||||
return
|
||||
}
|
||||
assert.Equal(expected, spec1)
|
||||
}
|
||||
|
||||
func TestMergeSpecsReuseModel(t *testing.T) {
|
||||
var spec1, spec2, expected *spec.Swagger
|
||||
yaml.Unmarshal([]byte(`
|
||||
swagger: "2.0"
|
||||
paths:
|
||||
/test:
|
||||
post:
|
||||
tags:
|
||||
- "test"
|
||||
summary: "Test API"
|
||||
operationId: "addTest"
|
||||
parameters:
|
||||
- in: "body"
|
||||
name: "body"
|
||||
description: "test object"
|
||||
required: true
|
||||
schema:
|
||||
$ref: "#/definitions/Test"
|
||||
responses:
|
||||
405:
|
||||
description: "Invalid input"
|
||||
$ref: "#/definitions/InvalidInput"
|
||||
definitions:
|
||||
Test:
|
||||
type: "object"
|
||||
properties:
|
||||
id:
|
||||
type: "integer"
|
||||
format: "int64"
|
||||
status:
|
||||
type: "string"
|
||||
description: "Status"
|
||||
InvalidInput:
|
||||
type: "string"
|
||||
format: "string"
|
||||
`), &spec1)
|
||||
yaml.Unmarshal([]byte(`
|
||||
swagger: "2.0"
|
||||
paths:
|
||||
/othertest:
|
||||
post:
|
||||
tags:
|
||||
- "test2"
|
||||
summary: "Test2 API"
|
||||
operationId: "addTest2"
|
||||
consumes:
|
||||
- "application/json"
|
||||
produces:
|
||||
- "application/xml"
|
||||
parameters:
|
||||
- in: "body"
|
||||
name: "body"
|
||||
description: "test2 object"
|
||||
required: true
|
||||
schema:
|
||||
$ref: "#/definitions/Test"
|
||||
definitions:
|
||||
Test:
|
||||
description: "This Test has a description"
|
||||
type: "object"
|
||||
properties:
|
||||
id:
|
||||
type: "integer"
|
||||
format: "int64"
|
||||
status:
|
||||
type: "string"
|
||||
description: "This status has another description"
|
||||
InvalidInput:
|
||||
type: "string"
|
||||
format: "string"
|
||||
`), &spec2)
|
||||
yaml.Unmarshal([]byte(`
|
||||
swagger: "2.0"
|
||||
paths:
|
||||
/test:
|
||||
post:
|
||||
tags:
|
||||
- "test"
|
||||
summary: "Test API"
|
||||
operationId: "addTest"
|
||||
parameters:
|
||||
- in: "body"
|
||||
name: "body"
|
||||
description: "test object"
|
||||
required: true
|
||||
schema:
|
||||
$ref: "#/definitions/Test"
|
||||
responses:
|
||||
405:
|
||||
description: "Invalid input"
|
||||
$ref: "#/definitions/InvalidInput"
|
||||
/othertest:
|
||||
post:
|
||||
tags:
|
||||
- "test2"
|
||||
summary: "Test2 API"
|
||||
operationId: "addTest2"
|
||||
consumes:
|
||||
- "application/json"
|
||||
produces:
|
||||
- "application/xml"
|
||||
parameters:
|
||||
- in: "body"
|
||||
name: "body"
|
||||
description: "test2 object"
|
||||
required: true
|
||||
schema:
|
||||
$ref: "#/definitions/Test"
|
||||
definitions:
|
||||
Test:
|
||||
type: "object"
|
||||
properties:
|
||||
id:
|
||||
type: "integer"
|
||||
format: "int64"
|
||||
status:
|
||||
type: "string"
|
||||
description: "Status"
|
||||
InvalidInput:
|
||||
type: "string"
|
||||
format: "string"
|
||||
`), &expected)
|
||||
assert := assert.New(t)
|
||||
if !assert.NoError(MergeSpecs(spec1, spec2)) {
|
||||
return
|
||||
}
|
||||
assert.Equal(expected, spec1)
|
||||
}
|
||||
|
||||
func TestMergeSpecsRenameModel(t *testing.T) {
|
||||
var spec1, spec2, expected *spec.Swagger
|
||||
yaml.Unmarshal([]byte(`
|
||||
swagger: "2.0"
|
||||
paths:
|
||||
/test:
|
||||
post:
|
||||
tags:
|
||||
- "test"
|
||||
summary: "Test API"
|
||||
operationId: "addTest"
|
||||
parameters:
|
||||
- in: "body"
|
||||
name: "body"
|
||||
description: "test object"
|
||||
required: true
|
||||
schema:
|
||||
$ref: "#/definitions/Test"
|
||||
responses:
|
||||
405:
|
||||
description: "Invalid input"
|
||||
$ref: "#/definitions/InvalidInput"
|
||||
definitions:
|
||||
Test:
|
||||
type: "object"
|
||||
properties:
|
||||
id:
|
||||
type: "integer"
|
||||
format: "int64"
|
||||
status:
|
||||
type: "string"
|
||||
description: "Status"
|
||||
InvalidInput:
|
||||
type: "string"
|
||||
format: "string"
|
||||
`), &spec1)
|
||||
yaml.Unmarshal([]byte(`
|
||||
swagger: "2.0"
|
||||
paths:
|
||||
/othertest:
|
||||
post:
|
||||
tags:
|
||||
- "test2"
|
||||
summary: "Test2 API"
|
||||
operationId: "addTest2"
|
||||
consumes:
|
||||
- "application/json"
|
||||
produces:
|
||||
- "application/xml"
|
||||
parameters:
|
||||
- in: "body"
|
||||
name: "body"
|
||||
description: "test2 object"
|
||||
required: true
|
||||
schema:
|
||||
$ref: "#/definitions/Test"
|
||||
definitions:
|
||||
Test:
|
||||
description: "This Test has a description"
|
||||
type: "object"
|
||||
properties:
|
||||
id:
|
||||
type: "integer"
|
||||
format: "int64"
|
||||
InvalidInput:
|
||||
type: "string"
|
||||
format: "string"
|
||||
`), &spec2)
|
||||
yaml.Unmarshal([]byte(`
|
||||
swagger: "2.0"
|
||||
paths:
|
||||
/test:
|
||||
post:
|
||||
tags:
|
||||
- "test"
|
||||
summary: "Test API"
|
||||
operationId: "addTest"
|
||||
parameters:
|
||||
- in: "body"
|
||||
name: "body"
|
||||
description: "test object"
|
||||
required: true
|
||||
schema:
|
||||
$ref: "#/definitions/Test"
|
||||
responses:
|
||||
405:
|
||||
description: "Invalid input"
|
||||
$ref: "#/definitions/InvalidInput"
|
||||
/othertest:
|
||||
post:
|
||||
tags:
|
||||
- "test2"
|
||||
summary: "Test2 API"
|
||||
operationId: "addTest2"
|
||||
consumes:
|
||||
- "application/json"
|
||||
produces:
|
||||
- "application/xml"
|
||||
parameters:
|
||||
- in: "body"
|
||||
name: "body"
|
||||
description: "test2 object"
|
||||
required: true
|
||||
schema:
|
||||
$ref: "#/definitions/Test_v2"
|
||||
definitions:
|
||||
Test:
|
||||
type: "object"
|
||||
properties:
|
||||
id:
|
||||
type: "integer"
|
||||
format: "int64"
|
||||
status:
|
||||
type: "string"
|
||||
description: "Status"
|
||||
Test_v2:
|
||||
description: "This Test has a description"
|
||||
type: "object"
|
||||
properties:
|
||||
id:
|
||||
type: "integer"
|
||||
format: "int64"
|
||||
InvalidInput:
|
||||
type: "string"
|
||||
format: "string"
|
||||
`), &expected)
|
||||
assert := assert.New(t)
|
||||
if !assert.NoError(MergeSpecs(spec1, spec2)) {
|
||||
return
|
||||
}
|
||||
|
||||
expected_yaml, _ := yaml.Marshal(expected)
|
||||
spec1_yaml, _ := yaml.Marshal(spec1)
|
||||
|
||||
assert.Equal(string(expected_yaml), string(spec1_yaml))
|
||||
}
|
Loading…
Reference in New Issue