Vendor Update (#14496)

* update code.gitea.io/sdk/gitea v0.13.1 -> v0.13.2

* update github.com/go-swagger/go-swagger v0.25.0 -> v0.26.0

* update github.com/google/uuid v1.1.2 -> v1.2.0

* update github.com/klauspost/compress v1.11.3 -> v1.11.7

* update github.com/lib/pq 083382b7e6fc -> v1.9.0

* update github.com/markbates/goth v1.65.0 -> v1.66.1

* update github.com/mattn/go-sqlite3 v1.14.4 -> v1.14.6

* update github.com/mgechev/revive 246eac737dc7 -> v1.0.3

* update github.com/minio/minio-go/v7 v7.0.6 -> v7.0.7

* update github.com/niklasfasching/go-org v1.3.2 -> v1.4.0

* update github.com/olivere/elastic/v7 v7.0.21 -> v7.0.22

* update github.com/pquerna/otp v1.2.0 -> v1.3.0

* update github.com/xanzy/go-gitlab v0.39.0 -> v0.42.0

* update github.com/yuin/goldmark v1.2.1 -> v1.3.1
This commit is contained in:
6543 2021-01-28 17:56:38 +01:00 committed by GitHub
parent e45bf12a34
commit d1353e1f7c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
403 changed files with 29737 additions and 14357 deletions

View file

@ -26,3 +26,14 @@ linters:
- gocognit
- whitespace
- wsl
- wrapcheck
- testpackage
- nlreturn
- gomnd
- exhaustivestruct
- goerr113
- errorlint
- nestif
- godot
- gofumpt
- paralleltest

View file

@ -1,8 +1,26 @@
after_success:
- bash <(curl -s https://codecov.io/bash)
go:
- 1.13.x
- 1.14.x
- 1.x
arch:
- amd64
jobs:
include:
# only run fast tests on ppc64le
- go: 1.x
arch: ppc64le
script:
- gotestsum -f short-verbose -- ./...
# include linting job, but only for latest go version and amd64 arch
- go: 1.x
arch: amd64
install:
go get github.com/golangci/golangci-lint/cmd/golangci-lint
script:
- golangci-lint run --new-from-rev master
install:
- GO111MODULE=off go get -u gotest.tools/gotestsum
language: go

View file

@ -2,9 +2,29 @@
[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/spec/master/LICENSE)
[![GoDoc](https://godoc.org/github.com/go-openapi/spec?status.svg)](http://godoc.org/github.com/go-openapi/spec)
[![GolangCI](https://golangci.com/badges/github.com/go-openapi/spec.svg)](https://golangci.com)
[![Go Report Card](https://goreportcard.com/badge/github.com/go-openapi/spec)](https://goreportcard.com/report/github.com/go-openapi/spec)
The object model for OpenAPI specification documents.
Currently supports Swagger 2.0.
### FAQ
* What does this do?
> 1. This package knows how to marshal and unmarshal Swagger API specifications into a golang object model
> 2. It knows how to resolve $ref and expand them to make a single root documment
* How does it play with the rest of the go-openapi packages ?
> 1. This package is at the core of the go-openapi suite of packages and [code generator](https://github.com/go-swagger/go-swagger)
> 2. There is a [spec loading package](https://github.com/go-openapi/loads) to fetch specs as JSON or YAML from local or remote locations
> 3. There is a [spec validation package](https://github.com/go-openapi/validate) built on top of it
> 4. There is a [spec analysis package](https://github.com/go-openapi/analysis) built on top of it, to analyze, flatten, fix and merge spec documents
* Does this library support OpenAPI 3?
> No.
> This package currently only supports OpenAPI 2.0 (aka Swagger 2.0).
> There is no plan to make it evolve toward supporting OpenAPI 3.x.
> This [discussion thread](https://github.com/go-openapi/spec/issues/21) relates the full story.
>
> An early attempt to support Swagger 3 may be found at: https://github.com/go-openapi/spec3

34
vendor/github.com/go-openapi/spec/appveyor.yml generated vendored Normal file
View file

@ -0,0 +1,34 @@
version: "0.1.{build}"
clone_folder: C:\go-openapi\spec
shallow_clone: true # for startup speed
pull_requests:
do_not_increment_build_number: true
#skip_tags: true
#skip_branch_with_pr: true
# appveyor.yml
build: off
environment:
GOPATH: c:\gopath
stack: go 1.12
test_script:
- echo "test disabled for now"
#- go test -v -timeout 20m ./...
#artifacts:
# - path: '%GOPATH%\bin\*.exe'
deploy: off
notifications:
- provider: Slack
incoming_webhook: https://hooks.slack.com/services/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
auth_token:
secure: Sf7kZf7ZGbnwWUMpffHwMu5A0cHkLK2MYY32LNTPj4+/3qC3Ghl7+9v4TSLOqOlCwdRNjOGblAq7s+GDJed6/xgRQl1JtCi1klzZNrYX4q01pgTPvvGcwbBkIYgeMaPeIRcK9OZnud7sRXdttozgTOpytps2U6Js32ip7uj5mHSg2ub0FwoSJwlS6dbezZ8+eDhoha0F/guY99BEwx8Bd+zROrT2TFGsSGOFGN6wFc7moCqTHO/YkWib13a2QNXqOxCCVBy/lt76Wp+JkeFppjHlzs/2lP3EAk13RIUAaesdEUHvIHrzCyNJEd3/+KO2DzsWOYfpktd+KBCvgaYOsoo7ubdT3IROeAegZdCgo/6xgCEsmFc9ZcqCfN5yNx2A+BZ2Vwmpws+bQ1E1+B5HDzzaiLcYfG4X2O210QVGVDLWsv1jqD+uPYeHY2WRfh5ZsIUFvaqgUEnwHwrK44/8REAhQavt1QAj5uJpsRd7CkRVPWRNK+yIky+wgbVUFEchRNmS55E7QWf+W4+4QZkQi7vUTMc9nbTUu2Es9NfvfudOpM2wZbn98fjpb/qq/nRv6Bk+ca+7XD5/IgNLMbWp2ouDdzbiHLCOfDUiHiDJhLfFZx9Bwo7ZwfzeOlbrQX66bx7xRKYmOe4DLrXhNcpbsMa8qbfxlZRCmYbubB/Y8h4=
channel: bots
on_build_success: false
on_build_failure: true
on_build_status_changed: true

View file

@ -247,9 +247,9 @@ type bintree struct {
}
var _bintree = &bintree{nil, map[string]*bintree{
"jsonschema-draft-04.json": &bintree{jsonschemaDraft04Json, map[string]*bintree{}},
"v2": &bintree{nil, map[string]*bintree{
"schema.json": &bintree{v2SchemaJson, map[string]*bintree{}},
"jsonschema-draft-04.json": {jsonschemaDraft04Json, map[string]*bintree{}},
"v2": {nil, map[string]*bintree{
"schema.json": {v2SchemaJson, map[string]*bintree{}},
}},
}}

View file

@ -14,7 +14,9 @@
package spec
import "sync"
import (
"sync"
)
// ResolutionCache a cache for resolving urls
type ResolutionCache interface {
@ -27,12 +29,23 @@ type simpleCache struct {
store map[string]interface{}
}
func (s *simpleCache) ShallowClone() ResolutionCache {
store := make(map[string]interface{}, len(s.store))
s.lock.RLock()
for k, v := range s.store {
store[k] = v
}
s.lock.RUnlock()
return &simpleCache{
store: store,
}
}
// Get retrieves a cached URI
func (s *simpleCache) Get(uri string) (interface{}, bool) {
debugLog("getting %q from resolution cache", uri)
s.lock.RLock()
v, ok := s.store[uri]
debugLog("got %q from resolution cache: %t", uri, ok)
s.lock.RUnlock()
return v, ok
@ -45,16 +58,41 @@ func (s *simpleCache) Set(uri string, data interface{}) {
s.lock.Unlock()
}
var resCache ResolutionCache
var (
// resCache is a package level cache for $ref resolution and expansion.
// It is initialized lazily by methods that have the need for it: no
// memory is allocated unless some expander methods are called.
//
// It is initialized with JSON schema and swagger schema,
// which do not mutate during normal operations.
//
// All subsequent utilizations of this cache are produced from a shallow
// clone of this initial version.
resCache *simpleCache
onceCache sync.Once
func init() {
resCache = initResolutionCache()
_ ResolutionCache = &simpleCache{}
)
// initResolutionCache initializes the URI resolution cache. To be wrapped in a sync.Once.Do call.
func initResolutionCache() {
resCache = defaultResolutionCache()
}
// initResolutionCache initializes the URI resolution cache
func initResolutionCache() ResolutionCache {
func defaultResolutionCache() *simpleCache {
return &simpleCache{store: map[string]interface{}{
"http://swagger.io/v2/schema.json": MustLoadSwagger20Schema(),
"http://json-schema.org/draft-04/schema": MustLoadJSONSchemaDraft04(),
}}
}
func cacheOrDefault(cache ResolutionCache) ResolutionCache {
onceCache.Do(initResolutionCache)
if cache != nil {
return cache
}
// get a shallow clone of the base cache with swagger and json schema
return resCache.ShallowClone()
}

View file

@ -28,12 +28,14 @@ type ContactInfo struct {
VendorExtensible
}
// ContactInfoProps hold the properties of a ContactInfo object
type ContactInfoProps struct {
Name string `json:"name,omitempty"`
URL string `json:"url,omitempty"`
Email string `json:"email,omitempty"`
}
// UnmarshalJSON hydrates ContactInfo from json
func (c *ContactInfo) UnmarshalJSON(data []byte) error {
if err := json.Unmarshal(data, &c.ContactInfoProps); err != nil {
return err
@ -41,6 +43,7 @@ func (c *ContactInfo) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, &c.VendorExtensible)
}
// MarshalJSON produces ContactInfo as json
func (c ContactInfo) MarshalJSON() ([]byte, error) {
b1, err := json.Marshal(c.ContactInfoProps)
if err != nil {

18
vendor/github.com/go-openapi/spec/errors.go generated vendored Normal file
View file

@ -0,0 +1,18 @@
package spec
import "errors"
var (
// ErrUnknownTypeForReference indicates that a resolved reference was found in an unsupported container type
ErrUnknownTypeForReference = errors.New("unknown type for the resolved reference")
// ErrResolveRefNeedsAPointer indicates that a $ref target must be a valid JSON pointer
ErrResolveRefNeedsAPointer = errors.New("resolve ref: target needs to be a pointer")
// ErrDerefUnsupportedType indicates that a resolved reference was found in an unsupported container type.
// At the moment, $ref are supported only inside: schemas, parameters, responses, path items
ErrDerefUnsupportedType = errors.New("deref: unsupported type")
// ErrExpandUnsupportedType indicates that $ref expansion is attempted on some invalid type
ErrExpandUnsupportedType = errors.New("expand: unsupported type. Input should be of type *Parameter or *Response")
)

View file

@ -17,140 +17,21 @@ package spec
import (
"encoding/json"
"fmt"
"strings"
)
// ExpandOptions provides options for spec expand
// ExpandOptions provides options for the spec expander.
type ExpandOptions struct {
RelativeBase string
SkipSchemas bool
ContinueOnError bool
RelativeBase string
SkipSchemas bool
ContinueOnError bool
PathLoader func(string) (json.RawMessage, error) `json:"-"`
AbsoluteCircularRef bool
}
// ResolveRefWithBase resolves a reference against a context root with preservation of base path
func ResolveRefWithBase(root interface{}, ref *Ref, opts *ExpandOptions) (*Schema, error) {
resolver, err := defaultSchemaLoader(root, opts, nil, nil)
if err != nil {
return nil, err
}
specBasePath := ""
if opts != nil && opts.RelativeBase != "" {
specBasePath, _ = absPath(opts.RelativeBase)
}
result := new(Schema)
if err := resolver.Resolve(ref, result, specBasePath); err != nil {
return nil, err
}
return result, nil
}
// ResolveRef resolves a reference against a context root
// ref is guaranteed to be in root (no need to go to external files)
// ResolveRef is ONLY called from the code generation module
func ResolveRef(root interface{}, ref *Ref) (*Schema, error) {
res, _, err := ref.GetPointer().Get(root)
if err != nil {
panic(err)
}
switch sch := res.(type) {
case Schema:
return &sch, nil
case *Schema:
return sch, nil
case map[string]interface{}:
b, _ := json.Marshal(sch)
newSch := new(Schema)
_ = json.Unmarshal(b, newSch)
return newSch, nil
default:
return nil, fmt.Errorf("unknown type for the resolved reference")
}
}
// ResolveParameter resolves a parameter reference against a context root
func ResolveParameter(root interface{}, ref Ref) (*Parameter, error) {
return ResolveParameterWithBase(root, ref, nil)
}
// ResolveParameterWithBase resolves a parameter reference against a context root and base path
func ResolveParameterWithBase(root interface{}, ref Ref, opts *ExpandOptions) (*Parameter, error) {
resolver, err := defaultSchemaLoader(root, opts, nil, nil)
if err != nil {
return nil, err
}
result := new(Parameter)
if err := resolver.Resolve(&ref, result, ""); err != nil {
return nil, err
}
return result, nil
}
// ResolveResponse resolves response a reference against a context root
func ResolveResponse(root interface{}, ref Ref) (*Response, error) {
return ResolveResponseWithBase(root, ref, nil)
}
// ResolveResponseWithBase resolves response a reference against a context root and base path
func ResolveResponseWithBase(root interface{}, ref Ref, opts *ExpandOptions) (*Response, error) {
resolver, err := defaultSchemaLoader(root, opts, nil, nil)
if err != nil {
return nil, err
}
result := new(Response)
if err := resolver.Resolve(&ref, result, ""); err != nil {
return nil, err
}
return result, nil
}
// ResolveItems resolves parameter items reference against a context root and base path.
//
// NOTE: stricly speaking, this construct is not supported by Swagger 2.0.
// Similarly, $ref are forbidden in response headers.
func ResolveItems(root interface{}, ref Ref, opts *ExpandOptions) (*Items, error) {
resolver, err := defaultSchemaLoader(root, opts, nil, nil)
if err != nil {
return nil, err
}
basePath := ""
if opts.RelativeBase != "" {
basePath = opts.RelativeBase
}
result := new(Items)
if err := resolver.Resolve(&ref, result, basePath); err != nil {
return nil, err
}
return result, nil
}
// ResolvePathItem resolves response a path item against a context root and base path
func ResolvePathItem(root interface{}, ref Ref, opts *ExpandOptions) (*PathItem, error) {
resolver, err := defaultSchemaLoader(root, opts, nil, nil)
if err != nil {
return nil, err
}
basePath := ""
if opts.RelativeBase != "" {
basePath = opts.RelativeBase
}
result := new(PathItem)
if err := resolver.Resolve(&ref, result, basePath); err != nil {
return nil, err
}
return result, nil
}
// ExpandSpec expands the references in a swagger spec
func ExpandSpec(spec *Swagger, options *ExpandOptions) error {
resolver, err := defaultSchemaLoader(spec, options, nil, nil)
// Just in case this ever returns an error.
if resolver.shouldStopOnError(err) {
return err
}
resolver := defaultSchemaLoader(spec, options, nil, nil)
// getting the base path of the spec to adjust all subsequent reference resolutions
specBasePath := ""
@ -160,9 +41,10 @@ func ExpandSpec(spec *Swagger, options *ExpandOptions) error {
if options == nil || !options.SkipSchemas {
for key, definition := range spec.Definitions {
var def *Schema
var err error
if def, err = expandSchema(definition, []string{fmt.Sprintf("#/definitions/%s", key)}, resolver, specBasePath); resolver.shouldStopOnError(err) {
parentRefs := make([]string, 0, 10)
parentRefs = append(parentRefs, fmt.Sprintf("#/definitions/%s", key))
def, err := expandSchema(definition, parentRefs, resolver, specBasePath)
if resolver.shouldStopOnError(err) {
return err
}
if def != nil {
@ -189,157 +71,140 @@ func ExpandSpec(spec *Swagger, options *ExpandOptions) error {
if spec.Paths != nil {
for key := range spec.Paths.Paths {
path := spec.Paths.Paths[key]
if err := expandPathItem(&path, resolver, specBasePath); resolver.shouldStopOnError(err) {
pth := spec.Paths.Paths[key]
if err := expandPathItem(&pth, resolver, specBasePath); resolver.shouldStopOnError(err) {
return err
}
spec.Paths.Paths[key] = path
spec.Paths.Paths[key] = pth
}
}
return nil
}
const rootBase = "root"
// baseForRoot loads in the cache the root document and produces a fake "root" base path entry
const rootBase = ".root"
// baseForRoot loads in the cache the root document and produces a fake ".root" base path entry
// for further $ref resolution
//
// Setting the cache is optional and this parameter may safely be left to nil.
func baseForRoot(root interface{}, cache ResolutionCache) string {
// cache the root document to resolve $ref's
if root != nil {
base, _ := absPath(rootBase)
normalizedBase := normalizeAbsPath(base)
debugLog("setting root doc in cache at: %s", normalizedBase)
if cache == nil {
cache = resCache
}
cache.Set(normalizedBase, root)
return rootBase
if root == nil {
return ""
}
return ""
// cache the root document to resolve $ref's
base, _ := absPath(rootBase)
normalizedBase := normalizeAbsPath(base)
cache.Set(normalizedBase, root)
return normalizedBase
}
// ExpandSchema expands the refs in the schema object with reference to the root object
// go-openapi/validate uses this function
// notice that it is impossible to reference a json schema in a different file other than root
// ExpandSchema expands the refs in the schema object with reference to the root object.
//
// go-openapi/validate uses this function.
//
// Notice that it is impossible to reference a json schema in a different document other than root
// (use ExpandSchemaWithBasePath to resolve external references).
//
// Setting the cache is optional and this parameter may safely be left to nil.
func ExpandSchema(schema *Schema, root interface{}, cache ResolutionCache) error {
cache = cacheOrDefault(cache)
if root == nil {
root = schema
}
opts := &ExpandOptions{
// when a root is specified, cache the root as an in-memory document for $ref retrieval
RelativeBase: baseForRoot(root, cache),
SkipSchemas: false,
ContinueOnError: false,
// when no base path is specified, remaining $ref (circular) are rendered with an absolute path
AbsoluteCircularRef: true,
}
return ExpandSchemaWithBasePath(schema, cache, opts)
}
// ExpandSchemaWithBasePath expands the refs in the schema object, base path configured through expand options
// ExpandSchemaWithBasePath expands the refs in the schema object, base path configured through expand options.
//
// Setting the cache is optional and this parameter may safely be left to nil.
func ExpandSchemaWithBasePath(schema *Schema, cache ResolutionCache, opts *ExpandOptions) error {
if schema == nil {
return nil
}
cache = cacheOrDefault(cache)
var basePath string
if opts.RelativeBase != "" {
basePath, _ = absPath(opts.RelativeBase)
}
resolver, err := defaultSchemaLoader(nil, opts, cache, nil)
resolver := defaultSchemaLoader(nil, opts, cache, nil)
parentRefs := make([]string, 0, 10)
s, err := expandSchema(*schema, parentRefs, resolver, basePath)
if err != nil {
return err
}
refs := []string{""}
var s *Schema
if s, err = expandSchema(*schema, refs, resolver, basePath); err != nil {
return err
if s != nil {
// guard for when continuing on error
*schema = *s
}
*schema = *s
return nil
}
func expandItems(target Schema, parentRefs []string, resolver *schemaLoader, basePath string) (*Schema, error) {
if target.Items != nil {
if target.Items.Schema != nil {
t, err := expandSchema(*target.Items.Schema, parentRefs, resolver, basePath)
if err != nil {
return nil, err
}
*target.Items.Schema = *t
}
for i := range target.Items.Schemas {
t, err := expandSchema(target.Items.Schemas[i], parentRefs, resolver, basePath)
if err != nil {
return nil, err
}
target.Items.Schemas[i] = *t
}
if target.Items == nil {
return &target, nil
}
// array
if target.Items.Schema != nil {
t, err := expandSchema(*target.Items.Schema, parentRefs, resolver, basePath)
if err != nil {
return nil, err
}
*target.Items.Schema = *t
}
// tuple
for i := range target.Items.Schemas {
t, err := expandSchema(target.Items.Schemas[i], parentRefs, resolver, basePath)
if err != nil {
return nil, err
}
target.Items.Schemas[i] = *t
}
return &target, nil
}
func expandSchema(target Schema, parentRefs []string, resolver *schemaLoader, basePath string) (*Schema, error) {
if target.Ref.String() == "" && target.Ref.IsRoot() {
// normalizing is important
newRef := normalizeFileRef(&target.Ref, basePath)
target.Ref = *newRef
return &target, nil
}
// change the base path of resolution when an ID is encountered
// otherwise the basePath should inherit the parent's
// important: ID can be relative path
if target.ID != "" {
debugLog("schema has ID: %s", target.ID)
// handling the case when id is a folder
// remember that basePath has to be a file
refPath := target.ID
if strings.HasSuffix(target.ID, "/") {
// path.Clean here would not work correctly if basepath is http
refPath = fmt.Sprintf("%s%s", refPath, "placeholder.json")
}
basePath = normalizePaths(refPath, basePath)
basePath, _ = resolver.setSchemaID(target, target.ID, basePath)
}
var t *Schema
// if Ref is found, everything else doesn't matter
// Ref also changes the resolution scope of children expandSchema
if target.Ref.String() != "" {
// here the resolution scope is changed because a $ref was encountered
normalizedRef := normalizeFileRef(&target.Ref, basePath)
normalizedBasePath := normalizedRef.RemoteURI()
return expandSchemaRef(target, parentRefs, resolver, basePath)
}
if resolver.isCircular(normalizedRef, basePath, parentRefs...) {
// this means there is a cycle in the recursion tree: return the Ref
// - circular refs cannot be expanded. We leave them as ref.
// - denormalization means that a new local file ref is set relative to the original basePath
debugLog("shortcut circular ref: basePath: %s, normalizedPath: %s, normalized ref: %s",
basePath, normalizedBasePath, normalizedRef.String())
if !resolver.options.AbsoluteCircularRef {
target.Ref = *denormalizeFileRef(normalizedRef, normalizedBasePath, resolver.context.basePath)
} else {
target.Ref = *normalizedRef
}
return &target, nil
for k := range target.Definitions {
tt, err := expandSchema(target.Definitions[k], parentRefs, resolver, basePath)
if resolver.shouldStopOnError(err) {
return &target, err
}
debugLog("basePath: %s: calling Resolve with target: %#v", basePath, target)
if err := resolver.Resolve(&target.Ref, &t, basePath); resolver.shouldStopOnError(err) {
return nil, err
}
if t != nil {
parentRefs = append(parentRefs, normalizedRef.String())
var err error
transitiveResolver, err := resolver.transitiveResolver(basePath, target.Ref)
if transitiveResolver.shouldStopOnError(err) {
return nil, err
}
basePath = resolver.updateBasePath(transitiveResolver, normalizedBasePath)
return expandSchema(*t, parentRefs, transitiveResolver, basePath)
if tt != nil {
target.Definitions[k] = *tt
}
}
@ -356,15 +221,21 @@ func expandSchema(target Schema, parentRefs []string, resolver *schemaLoader, ba
if resolver.shouldStopOnError(err) {
return &target, err
}
target.AllOf[i] = *t
if t != nil {
target.AllOf[i] = *t
}
}
for i := range target.AnyOf {
t, err := expandSchema(target.AnyOf[i], parentRefs, resolver, basePath)
if resolver.shouldStopOnError(err) {
return &target, err
}
target.AnyOf[i] = *t
if t != nil {
target.AnyOf[i] = *t
}
}
for i := range target.OneOf {
t, err := expandSchema(target.OneOf[i], parentRefs, resolver, basePath)
if resolver.shouldStopOnError(err) {
@ -374,6 +245,7 @@ func expandSchema(target Schema, parentRefs []string, resolver *schemaLoader, ba
target.OneOf[i] = *t
}
}
if target.Not != nil {
t, err := expandSchema(*target.Not, parentRefs, resolver, basePath)
if resolver.shouldStopOnError(err) {
@ -383,6 +255,7 @@ func expandSchema(target Schema, parentRefs []string, resolver *schemaLoader, ba
*target.Not = *t
}
}
for k := range target.Properties {
t, err := expandSchema(target.Properties[k], parentRefs, resolver, basePath)
if resolver.shouldStopOnError(err) {
@ -392,6 +265,7 @@ func expandSchema(target Schema, parentRefs []string, resolver *schemaLoader, ba
target.Properties[k] = *t
}
}
if target.AdditionalProperties != nil && target.AdditionalProperties.Schema != nil {
t, err := expandSchema(*target.AdditionalProperties.Schema, parentRefs, resolver, basePath)
if resolver.shouldStopOnError(err) {
@ -401,6 +275,7 @@ func expandSchema(target Schema, parentRefs []string, resolver *schemaLoader, ba
*target.AdditionalProperties.Schema = *t
}
}
for k := range target.PatternProperties {
t, err := expandSchema(target.PatternProperties[k], parentRefs, resolver, basePath)
if resolver.shouldStopOnError(err) {
@ -410,6 +285,7 @@ func expandSchema(target Schema, parentRefs []string, resolver *schemaLoader, ba
target.PatternProperties[k] = *t
}
}
for k := range target.Dependencies {
if target.Dependencies[k].Schema != nil {
t, err := expandSchema(*target.Dependencies[k].Schema, parentRefs, resolver, basePath)
@ -421,6 +297,7 @@ func expandSchema(target Schema, parentRefs []string, resolver *schemaLoader, ba
}
}
}
if target.AdditionalItems != nil && target.AdditionalItems.Schema != nil {
t, err := expandSchema(*target.AdditionalItems.Schema, parentRefs, resolver, basePath)
if resolver.shouldStopOnError(err) {
@ -430,42 +307,73 @@ func expandSchema(target Schema, parentRefs []string, resolver *schemaLoader, ba
*target.AdditionalItems.Schema = *t
}
}
for k := range target.Definitions {
t, err := expandSchema(target.Definitions[k], parentRefs, resolver, basePath)
if resolver.shouldStopOnError(err) {
return &target, err
}
if t != nil {
target.Definitions[k] = *t
}
}
return &target, nil
}
func expandSchemaRef(target Schema, parentRefs []string, resolver *schemaLoader, basePath string) (*Schema, error) {
// if a Ref is found, all sibling fields are skipped
// Ref also changes the resolution scope of children expandSchema
// here the resolution scope is changed because a $ref was encountered
normalizedRef := normalizeFileRef(&target.Ref, basePath)
normalizedBasePath := normalizedRef.RemoteURI()
if resolver.isCircular(normalizedRef, basePath, parentRefs...) {
// this means there is a cycle in the recursion tree: return the Ref
// - circular refs cannot be expanded. We leave them as ref.
// - denormalization means that a new local file ref is set relative to the original basePath
debugLog("short circuit circular ref: basePath: %s, normalizedPath: %s, normalized ref: %s",
basePath, normalizedBasePath, normalizedRef.String())
if !resolver.options.AbsoluteCircularRef {
target.Ref = *denormalizeFileRef(normalizedRef, normalizedBasePath, resolver.context.basePath)
} else {
target.Ref = *normalizedRef
}
return &target, nil
}
var t *Schema
err := resolver.Resolve(&target.Ref, &t, basePath)
if resolver.shouldStopOnError(err) {
return nil, err
}
if t == nil {
// guard for when continuing on error
return &target, nil
}
parentRefs = append(parentRefs, normalizedRef.String())
transitiveResolver := resolver.transitiveResolver(basePath, target.Ref)
basePath = resolver.updateBasePath(transitiveResolver, normalizedBasePath)
return expandSchema(*t, parentRefs, transitiveResolver, basePath)
}
func expandPathItem(pathItem *PathItem, resolver *schemaLoader, basePath string) error {
if pathItem == nil {
return nil
}
parentRefs := []string{}
parentRefs := make([]string, 0, 10)
if err := resolver.deref(pathItem, parentRefs, basePath); resolver.shouldStopOnError(err) {
return err
}
if pathItem.Ref.String() != "" {
transitiveResolver, err := resolver.transitiveResolver(basePath, pathItem.Ref)
if transitiveResolver.shouldStopOnError(err) {
return err
}
transitiveResolver := resolver.transitiveResolver(basePath, pathItem.Ref)
basePath = transitiveResolver.updateBasePath(resolver, basePath)
resolver = transitiveResolver
}
pathItem.Ref = Ref{}
for idx := range pathItem.Parameters {
if err := expandParameterOrResponse(&(pathItem.Parameters[idx]), resolver, basePath); resolver.shouldStopOnError(err) {
pathItem.Ref = Ref{}
for i := range pathItem.Parameters {
if err := expandParameterOrResponse(&(pathItem.Parameters[i]), resolver, basePath); resolver.shouldStopOnError(err) {
return err
}
}
ops := []*Operation{
pathItem.Get,
pathItem.Head,
@ -480,6 +388,7 @@ func expandPathItem(pathItem *PathItem, resolver *schemaLoader, basePath string)
return err
}
}
return nil
}
@ -496,42 +405,47 @@ func expandOperation(op *Operation, resolver *schemaLoader, basePath string) err
op.Parameters[i] = param
}
if op.Responses != nil {
responses := op.Responses
if err := expandParameterOrResponse(responses.Default, resolver, basePath); resolver.shouldStopOnError(err) {
if op.Responses == nil {
return nil
}
responses := op.Responses
if err := expandParameterOrResponse(responses.Default, resolver, basePath); resolver.shouldStopOnError(err) {
return err
}
for code := range responses.StatusCodeResponses {
response := responses.StatusCodeResponses[code]
if err := expandParameterOrResponse(&response, resolver, basePath); resolver.shouldStopOnError(err) {
return err
}
for code := range responses.StatusCodeResponses {
response := responses.StatusCodeResponses[code]
if err := expandParameterOrResponse(&response, resolver, basePath); resolver.shouldStopOnError(err) {
return err
}
responses.StatusCodeResponses[code] = response
}
responses.StatusCodeResponses[code] = response
}
return nil
}
// ExpandResponseWithRoot expands a response based on a root document, not a fetchable document
//
// Notice that it is impossible to reference a json schema in a different document other than root
// (use ExpandResponse to resolve external references).
//
// Setting the cache is optional and this parameter may safely be left to nil.
func ExpandResponseWithRoot(response *Response, root interface{}, cache ResolutionCache) error {
cache = cacheOrDefault(cache)
opts := &ExpandOptions{
RelativeBase: baseForRoot(root, cache),
SkipSchemas: false,
ContinueOnError: false,
// when no base path is specified, remaining $ref (circular) are rendered with an absolute path
AbsoluteCircularRef: true,
}
resolver, err := defaultSchemaLoader(root, opts, nil, nil)
if err != nil {
return err
}
resolver := defaultSchemaLoader(root, opts, cache, nil)
return expandParameterOrResponse(response, resolver, opts.RelativeBase)
}
// ExpandResponse expands a response based on a basepath
// This is the exported version of expandResponse
// all refs inside response will be resolved relative to basePath
//
// All refs inside response will be resolved relative to basePath
func ExpandResponse(response *Response, basePath string) error {
var specBasePath string
if basePath != "" {
@ -540,27 +454,23 @@ func ExpandResponse(response *Response, basePath string) error {
opts := &ExpandOptions{
RelativeBase: specBasePath,
}
resolver, err := defaultSchemaLoader(nil, opts, nil, nil)
if err != nil {
return err
}
resolver := defaultSchemaLoader(nil, opts, nil, nil)
return expandParameterOrResponse(response, resolver, opts.RelativeBase)
}
// ExpandParameterWithRoot expands a parameter based on a root document, not a fetchable document
// ExpandParameterWithRoot expands a parameter based on a root document, not a fetchable document.
//
// Notice that it is impossible to reference a json schema in a different document other than root
// (use ExpandParameter to resolve external references).
func ExpandParameterWithRoot(parameter *Parameter, root interface{}, cache ResolutionCache) error {
cache = cacheOrDefault(cache)
opts := &ExpandOptions{
RelativeBase: baseForRoot(root, cache),
SkipSchemas: false,
ContinueOnError: false,
// when no base path is specified, remaining $ref (circular) are rendered with an absolute path
AbsoluteCircularRef: true,
}
resolver, err := defaultSchemaLoader(root, opts, nil, nil)
if err != nil {
return err
}
resolver := defaultSchemaLoader(root, opts, cache, nil)
return expandParameterOrResponse(parameter, resolver, opts.RelativeBase)
}
@ -576,17 +486,17 @@ func ExpandParameter(parameter *Parameter, basePath string) error {
opts := &ExpandOptions{
RelativeBase: specBasePath,
}
resolver, err := defaultSchemaLoader(nil, opts, nil, nil)
if err != nil {
return err
}
resolver := defaultSchemaLoader(nil, opts, nil, nil)
return expandParameterOrResponse(parameter, resolver, opts.RelativeBase)
}
func getRefAndSchema(input interface{}) (*Ref, *Schema, error) {
var ref *Ref
var sch *Schema
var (
ref *Ref
sch *Schema
)
switch refable := input.(type) {
case *Parameter:
if refable == nil {
@ -601,8 +511,9 @@ func getRefAndSchema(input interface{}) (*Ref, *Schema, error) {
ref = &refable.Ref
sch = refable.Schema
default:
return nil, nil, fmt.Errorf("expand: unsupported type %T. Input should be of type *Parameter or *Response", input)
return nil, nil, fmt.Errorf("unsupported type: %T: %w", input, ErrExpandUnsupportedType)
}
return ref, sch, nil
}
@ -611,41 +522,70 @@ func expandParameterOrResponse(input interface{}, resolver *schemaLoader, basePa
if err != nil {
return err
}
if ref == nil {
return nil
}
parentRefs := []string{}
if err := resolver.deref(input, parentRefs, basePath); resolver.shouldStopOnError(err) {
parentRefs := make([]string, 0, 10)
if err = resolver.deref(input, parentRefs, basePath); resolver.shouldStopOnError(err) {
return err
}
ref, sch, _ := getRefAndSchema(input)
if ref.String() != "" {
transitiveResolver, err := resolver.transitiveResolver(basePath, *ref)
if transitiveResolver.shouldStopOnError(err) {
return err
}
transitiveResolver := resolver.transitiveResolver(basePath, *ref)
basePath = resolver.updateBasePath(transitiveResolver, basePath)
resolver = transitiveResolver
}
if sch != nil && sch.Ref.String() != "" {
// schema expanded to a $ref in another root
var ern error
sch.Ref, ern = NewRef(normalizePaths(sch.Ref.String(), ref.RemoteURI()))
if sch == nil {
// nothing to be expanded
if ref != nil {
*ref = Ref{}
}
return nil
}
if sch.Ref.String() != "" {
rebasedRef, ern := NewRef(normalizePaths(sch.Ref.String(), basePath))
if ern != nil {
return ern
}
switch {
case resolver.isCircular(&rebasedRef, basePath, parentRefs...):
// this is a circular $ref: stop expansion
if !resolver.options.AbsoluteCircularRef {
sch.Ref = *denormalizeFileRef(&rebasedRef, basePath, resolver.context.basePath)
} else {
sch.Ref = rebasedRef
}
case !resolver.options.SkipSchemas:
// schema expanded to a $ref in another root
sch.Ref = rebasedRef
default:
// skip schema expansion but rebase $ref to schema
sch.Ref = *denormalizeFileRef(&rebasedRef, basePath, resolver.context.basePath)
}
}
if ref != nil {
*ref = Ref{}
}
if !resolver.options.SkipSchemas && sch != nil {
// expand schema
if !resolver.options.SkipSchemas {
s, err := expandSchema(*sch, parentRefs, resolver, basePath)
if resolver.shouldStopOnError(err) {
return err
}
if s == nil {
// guard for when continuing on error
return nil
}
*sch = *s
}
return nil
}

View file

@ -1,12 +1,13 @@
module github.com/go-openapi/spec
require (
github.com/go-openapi/jsonpointer v0.19.3
github.com/go-openapi/jsonreference v0.19.2
github.com/go-openapi/swag v0.19.5
github.com/stretchr/testify v1.3.0
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 // indirect
gopkg.in/yaml.v2 v2.2.4
github.com/go-openapi/jsonpointer v0.19.5
github.com/go-openapi/jsonreference v0.19.5
github.com/go-openapi/swag v0.19.12
github.com/stretchr/testify v1.6.1
golang.org/x/net v0.0.0-20201224014010-6772e930b67b // indirect
golang.org/x/text v0.3.4 // indirect
gopkg.in/yaml.v2 v2.4.0
)
go 1.13

View file

@ -2,48 +2,71 @@ github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tN
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-openapi/jsonpointer v0.19.2 h1:A9+F4Dc/MCNB5jibxf6rRvOvR/iFgQdyNx9eIhnGqq0=
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonreference v0.19.2 h1:o20suLFB4Ri0tuzpWtyHlh7E7HnkqTNLq6aR6WVNS1w=
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
github.com/go-openapi/swag v0.19.2 h1:jvO6bCMBEilGwMfHhrd61zIID4oIFdwb76V17SM88dE=
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonreference v0.19.5 h1:1WJP/wi4OjB4iV8KVbH73rQaoialJrqv8gitZLxGLtM=
github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=
github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.19.12 h1:Bc0bnY2c3AoF7Gc+IMIAQQsD8fLHjHpc19wXvYuayQI=
github.com/go-openapi/swag v0.19.12/go.mod h1:eFdyEBkTdoAf/9RXBvj4cr1nH7GD8Kzo5HTt47gr72M=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63 h1:nTT4s92Dgz2HlrB2NaMgvlfqHH39OgMhA7z3PK7PGD4=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980 h1:dfGZHvZk057jK2MCeWus/TowKpJ8y4AmooUzdBSR9GU=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b h1:iFwSg7t5GZmB/Q5TjiEAsdoLDrdJRC1RiF2WhuV29Qw=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View file

@ -141,6 +141,12 @@ func (h *Header) AllowDuplicates() *Header {
return h
}
// WithValidations is a fluent method to set header validations
func (h *Header) WithValidations(val CommonValidations) *Header {
h.SetValidations(SchemaValidations{CommonValidations: val})
return h
}
// MarshalJSON marshal this to JSON
func (h Header) MarshalJSON() ([]byte, error) {
b1, err := json.Marshal(h.CommonValidations)

View file

@ -53,22 +53,6 @@ func (s *SimpleSchema) ItemsTypeName() string {
return s.Items.TypeName()
}
// CommonValidations describe common JSON-schema validations
type CommonValidations struct {
Maximum *float64 `json:"maximum,omitempty"`
ExclusiveMaximum bool `json:"exclusiveMaximum,omitempty"`
Minimum *float64 `json:"minimum,omitempty"`
ExclusiveMinimum bool `json:"exclusiveMinimum,omitempty"`
MaxLength *int64 `json:"maxLength,omitempty"`
MinLength *int64 `json:"minLength,omitempty"`
Pattern string `json:"pattern,omitempty"`
MaxItems *int64 `json:"maxItems,omitempty"`
MinItems *int64 `json:"minItems,omitempty"`
UniqueItems bool `json:"uniqueItems,omitempty"`
MultipleOf *float64 `json:"multipleOf,omitempty"`
Enum []interface{} `json:"enum,omitempty"`
}
// Items a limited subset of JSON-Schema's items object.
// It is used by parameter definitions that are not located in "body".
//
@ -180,6 +164,12 @@ func (i *Items) AllowDuplicates() *Items {
return i
}
// WithValidations is a fluent method to set Items validations
func (i *Items) WithValidations(val CommonValidations) *Items {
i.SetValidations(SchemaValidations{CommonValidations: val})
return i
}
// UnmarshalJSON hydrates this items instance with the data from JSON
func (i *Items) UnmarshalJSON(data []byte) error {
var validations CommonValidations

View file

@ -28,11 +28,13 @@ type License struct {
VendorExtensible
}
// LicenseProps holds the properties of a License object
type LicenseProps struct {
Name string `json:"name,omitempty"`
URL string `json:"url,omitempty"`
}
// UnmarshalJSON hydrates License from json
func (l *License) UnmarshalJSON(data []byte) error {
if err := json.Unmarshal(data, &l.LicenseProps); err != nil {
return err
@ -40,6 +42,7 @@ func (l *License) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, &l.VendorExtensible)
}
// MarshalJSON produces License as json
func (l License) MarshalJSON() ([]byte, error) {
b1, err := json.Marshal(l.LicenseProps)
if err != nil {

View file

@ -20,9 +20,12 @@ import (
"os"
"path"
"path/filepath"
"runtime"
"strings"
)
const windowsOS = "windows"
// normalize absolute path for cache.
// on Windows, drive letters should be converted to lower as scheme in net/url.URL
func normalizeAbsPath(path string) string {
@ -71,27 +74,51 @@ func normalizePaths(refPath, base string) string {
return baseURL.String()
}
// isRoot is a temporary hack to discern windows file ref for ref.IsRoot().
// TODO: a more thorough change is needed to handle windows file refs.
func isRoot(ref *Ref) bool {
if runtime.GOOS != windowsOS {
return ref.IsRoot()
}
return !filepath.IsAbs(ref.String())
}
// isAbs is a temporary hack to discern windows file ref for url IsAbs().
// TODO: a more thorough change is needed to handle windows file refs.
func isAbs(u *url.URL) bool {
if runtime.GOOS != windowsOS {
return u.IsAbs()
}
if len(u.Scheme) <= 1 {
// drive letter got caught as URI scheme
return false
}
return u.IsAbs()
}
// denormalizePaths returns to simplest notation on file $ref,
// i.e. strips the absolute path and sets a path relative to the base path.
//
// This is currently used when we rewrite ref after a circular ref has been detected
func denormalizeFileRef(ref *Ref, relativeBase, originalRelativeBase string) *Ref {
debugLog("denormalizeFileRef for: %s", ref.String())
debugLog("denormalizeFileRef for: %s (relative: %s, original: %s)", ref.String(),
relativeBase, originalRelativeBase)
if ref.String() == "" || ref.IsRoot() || ref.HasFragmentOnly {
// log.Printf("denormalize: %s, IsRoot: %t,HasFragmentOnly: %t, HasFullURL: %t", ref.String(), ref.IsRoot(), ref.HasFragmentOnly, ref.HasFullURL)
if ref.String() == "" || isRoot(ref) || ref.HasFragmentOnly {
return ref
}
// strip relativeBase from URI
relativeBaseURL, _ := url.Parse(relativeBase)
relativeBaseURL.Fragment = ""
if relativeBaseURL.IsAbs() && strings.HasPrefix(ref.String(), relativeBase) {
if isAbs(relativeBaseURL) && strings.HasPrefix(ref.String(), relativeBase) {
// this should work for absolute URI (e.g. http://...): we have an exact match, just trim prefix
r, _ := NewRef(strings.TrimPrefix(ref.String(), relativeBase))
return &r
}
if relativeBaseURL.IsAbs() {
if isAbs(relativeBaseURL) {
// other absolute URL get unchanged (i.e. with a non-empty scheme)
return ref
}
@ -111,7 +138,7 @@ func denormalizeFileRef(ref *Ref, relativeBase, originalRelativeBase string) *Re
// my normalized ref points to: /mypath/item.json#/target
// expected result: item.json#/target
parts := strings.Split(ref.String(), "#")
relativePath, err := filepath.Rel(path.Dir(originalRelativeBaseURL.String()), parts[0])
relativePath, err := filepath.Rel(filepath.Dir(originalRelativeBaseURL.String()), parts[0])
if err != nil {
// there is no common ancestor (e.g. different drives on windows)
// leaves the ref unchanged
@ -132,8 +159,6 @@ func normalizeFileRef(ref *Ref, relativeBase string) *Ref {
return &r
}
debugLog("normalizing %s against %s", ref.String(), relativeBase)
s := normalizePaths(ref.String(), relativeBase)
r, _ := NewRef(s)
return &r
@ -148,5 +173,5 @@ func absPath(fname string) (string, error) {
return fname, nil
}
wd, err := os.Getwd()
return filepath.Join(wd, fname), err
return normalizeAbsPath(filepath.Join(wd, fname)), err
}

View file

@ -25,7 +25,6 @@ import (
)
func init() {
//gob.Register(map[string][]interface{}{})
gob.Register(map[string]interface{}{})
gob.Register([]interface{}{})
}

View file

@ -39,8 +39,7 @@ func PathParam(name string) *Parameter {
// BodyParam creates a body parameter
func BodyParam(name string, schema *Schema) *Parameter {
return &Parameter{ParamProps: ParamProps{Name: name, In: "body", Schema: schema},
SimpleSchema: SimpleSchema{Type: "object"}}
return &Parameter{ParamProps: ParamProps{Name: name, In: "body", Schema: schema}}
}
// FormDataParam creates a body parameter
@ -58,7 +57,7 @@ func FileParam(name string) *Parameter {
func SimpleArrayParam(name, tpe, fmt string) *Parameter {
return &Parameter{ParamProps: ParamProps{Name: name},
SimpleSchema: SimpleSchema{Type: jsonArray, CollectionFormat: "csv",
Items: &Items{SimpleSchema: SimpleSchema{Type: "string", Format: fmt}}}}
Items: &Items{SimpleSchema: SimpleSchema{Type: tpe, Format: fmt}}}}
}
// ParamRef creates a parameter that's a json reference
@ -278,6 +277,12 @@ func (p *Parameter) AllowDuplicates() *Parameter {
return p
}
// WithValidations is a fluent method to set parameter validations
func (p *Parameter) WithValidations(val CommonValidations) *Parameter {
p.SetValidations(SchemaValidations{CommonValidations: val})
return p
}
// UnmarshalJSON hydrates this items instance with the data from JSON
func (p *Parameter) UnmarshalJSON(data []byte) error {
if err := json.Unmarshal(data, &p.CommonValidations); err != nil {

91
vendor/github.com/go-openapi/spec/properties.go generated vendored Normal file
View file

@ -0,0 +1,91 @@
package spec
import (
"bytes"
"encoding/json"
"reflect"
"sort"
)
// OrderSchemaItem holds a named schema (e.g. from a property of an object)
type OrderSchemaItem struct {
Name string
Schema
}
// OrderSchemaItems is a sortable slice of named schemas.
// The ordering is defined by the x-order schema extension.
type OrderSchemaItems []OrderSchemaItem
// MarshalJSON produces a json object with keys defined by the name schemas
// of the OrderSchemaItems slice, keeping the original order of the slice.
func (items OrderSchemaItems) MarshalJSON() ([]byte, error) {
buf := bytes.NewBuffer(nil)
buf.WriteString("{")
for i := range items {
if i > 0 {
buf.WriteString(",")
}
buf.WriteString("\"")
buf.WriteString(items[i].Name)
buf.WriteString("\":")
bs, err := json.Marshal(&items[i].Schema)
if err != nil {
return nil, err
}
buf.Write(bs)
}
buf.WriteString("}")
return buf.Bytes(), nil
}
func (items OrderSchemaItems) Len() int { return len(items) }
func (items OrderSchemaItems) Swap(i, j int) { items[i], items[j] = items[j], items[i] }
func (items OrderSchemaItems) Less(i, j int) (ret bool) {
ii, oki := items[i].Extensions.GetString("x-order")
ij, okj := items[j].Extensions.GetString("x-order")
if oki {
if okj {
defer func() {
if err := recover(); err != nil {
defer func() {
if err = recover(); err != nil {
ret = items[i].Name < items[j].Name
}
}()
ret = reflect.ValueOf(ii).String() < reflect.ValueOf(ij).String()
}
}()
return reflect.ValueOf(ii).Int() < reflect.ValueOf(ij).Int()
}
return true
} else if okj {
return false
}
return items[i].Name < items[j].Name
}
// SchemaProperties is a map representing the properties of a Schema object.
// It knows how to transform its keys into an ordered slice.
type SchemaProperties map[string]Schema
// ToOrderedSchemaItems transforms the map of properties into a sortable slice
func (properties SchemaProperties) ToOrderedSchemaItems() OrderSchemaItems {
items := make(OrderSchemaItems, 0, len(properties))
for k, v := range properties {
items = append(items, OrderSchemaItem{
Name: k,
Schema: v,
})
}
sort.Sort(items)
return items
}
// MarshalJSON produces properties as json, keeping their order.
func (properties SchemaProperties) MarshalJSON() ([]byte, error) {
if properties == nil {
return []byte("null"), nil
}
return json.Marshal(properties.ToOrderedSchemaItems())
}

View file

@ -48,7 +48,7 @@ type Ref struct {
// RemoteURI gets the remote uri part of the ref
func (r *Ref) RemoteURI() string {
if r.String() == "" {
return r.String()
return ""
}
u := *r.GetURL()
@ -68,7 +68,7 @@ func (r *Ref) IsValidURI(basepaths ...string) bool {
}
if r.HasFullURL {
//#nosec
//nolint:noctx,gosec
rr, err := http.Get(v)
if err != nil {
return false

128
vendor/github.com/go-openapi/spec/resolver.go generated vendored Normal file
View file

@ -0,0 +1,128 @@
package spec
import (
"github.com/go-openapi/swag"
)
func resolveAnyWithBase(root interface{}, ref *Ref, result interface{}, options *ExpandOptions) error {
resolver := defaultSchemaLoader(root, options, nil, nil)
basePath := ""
if options != nil && options.RelativeBase != "" {
basePath, _ = absPath(options.RelativeBase)
}
if err := resolver.Resolve(ref, result, basePath); err != nil {
return err
}
return nil
}
// ResolveRefWithBase resolves a reference against a context root with preservation of base path
func ResolveRefWithBase(root interface{}, ref *Ref, options *ExpandOptions) (*Schema, error) {
result := new(Schema)
err := resolveAnyWithBase(root, ref, result, options)
if err != nil {
return nil, err
}
return result, nil
}
// ResolveRef resolves a reference for a schema against a context root
// ref is guaranteed to be in root (no need to go to external files)
//
// ResolveRef is ONLY called from the code generation module
func ResolveRef(root interface{}, ref *Ref) (*Schema, error) {
res, _, err := ref.GetPointer().Get(root)
if err != nil {
return nil, err
}
switch sch := res.(type) {
case Schema:
return &sch, nil
case *Schema:
return sch, nil
case map[string]interface{}:
newSch := new(Schema)
if err = swag.DynamicJSONToStruct(sch, newSch); err != nil {
return nil, err
}
return newSch, nil
default:
return nil, ErrUnknownTypeForReference
}
}
// ResolveParameterWithBase resolves a parameter reference against a context root and base path
func ResolveParameterWithBase(root interface{}, ref Ref, options *ExpandOptions) (*Parameter, error) {
result := new(Parameter)
err := resolveAnyWithBase(root, &ref, result, options)
if err != nil {
return nil, err
}
return result, nil
}
// ResolveParameter resolves a parameter reference against a context root
func ResolveParameter(root interface{}, ref Ref) (*Parameter, error) {
return ResolveParameterWithBase(root, ref, nil)
}
// ResolveResponseWithBase resolves response a reference against a context root and base path
func ResolveResponseWithBase(root interface{}, ref Ref, options *ExpandOptions) (*Response, error) {
result := new(Response)
err := resolveAnyWithBase(root, &ref, result, options)
if err != nil {
return nil, err
}
return result, nil
}
// ResolveResponse resolves response a reference against a context root
func ResolveResponse(root interface{}, ref Ref) (*Response, error) {
return ResolveResponseWithBase(root, ref, nil)
}
// ResolvePathItemWithBase resolves response a path item against a context root and base path
func ResolvePathItemWithBase(root interface{}, ref Ref, options *ExpandOptions) (*PathItem, error) {
result := new(PathItem)
err := resolveAnyWithBase(root, &ref, result, options)
if err != nil {
return nil, err
}
return result, nil
}
// ResolvePathItem resolves response a path item against a context root and base path
//
// Deprecated: use ResolvePathItemWithBase instead
func ResolvePathItem(root interface{}, ref Ref, options *ExpandOptions) (*PathItem, error) {
return ResolvePathItemWithBase(root, ref, options)
}
// ResolveItemsWithBase resolves parameter items reference against a context root and base path.
//
// NOTE: stricly speaking, this construct is not supported by Swagger 2.0.
// Similarly, $ref are forbidden in response headers.
func ResolveItemsWithBase(root interface{}, ref Ref, options *ExpandOptions) (*Items, error) {
result := new(Items)
err := resolveAnyWithBase(root, &ref, result, options)
if err != nil {
return nil, err
}
return result, nil
}
// ResolveItems resolves parameter items reference against a context root and base path.
//
// Deprecated: use ResolveItemsWithBase instead
func ResolveItems(root interface{}, ref Ref, options *ExpandOptions) (*Items, error) {
return ResolveItemsWithBase(root, ref, options)
}

View file

@ -23,7 +23,7 @@ import (
// ResponseProps properties specific to a response
type ResponseProps struct {
Description string `json:"description,omitempty"`
Description string `json:"description"`
Schema *Schema `json:"schema,omitempty"`
Headers map[string]Header `json:"headers,omitempty"`
Examples map[string]interface{} `json:"examples,omitempty"`
@ -63,10 +63,31 @@ func (r *Response) UnmarshalJSON(data []byte) error {
// MarshalJSON converts this items object to JSON
func (r Response) MarshalJSON() ([]byte, error) {
b1, err := json.Marshal(r.ResponseProps)
var (
b1 []byte
err error
)
if r.Ref.String() == "" {
// when there is no $ref, empty description is rendered as an empty string
b1, err = json.Marshal(r.ResponseProps)
} else {
// when there is $ref inside the schema, description should be omitempty-ied
b1, err = json.Marshal(struct {
Description string `json:"description,omitempty"`
Schema *Schema `json:"schema,omitempty"`
Headers map[string]Header `json:"headers,omitempty"`
Examples map[string]interface{} `json:"examples,omitempty"`
}{
Description: r.ResponseProps.Description,
Schema: r.ResponseProps.Schema,
Examples: r.ResponseProps.Examples,
})
}
if err != nil {
return nil, err
}
b2, err := json.Marshal(r.Refable)
if err != nil {
return nil, err

View file

@ -158,41 +158,41 @@ func (r *SchemaURL) fromMap(v map[string]interface{}) error {
// SchemaProps describes a JSON schema (draft 4)
type SchemaProps struct {
ID string `json:"id,omitempty"`
Ref Ref `json:"-"`
Schema SchemaURL `json:"-"`
Description string `json:"description,omitempty"`
Type StringOrArray `json:"type,omitempty"`
Nullable bool `json:"nullable,omitempty"`
Format string `json:"format,omitempty"`
Title string `json:"title,omitempty"`
Default interface{} `json:"default,omitempty"`
Maximum *float64 `json:"maximum,omitempty"`
ExclusiveMaximum bool `json:"exclusiveMaximum,omitempty"`
Minimum *float64 `json:"minimum,omitempty"`
ExclusiveMinimum bool `json:"exclusiveMinimum,omitempty"`
MaxLength *int64 `json:"maxLength,omitempty"`
MinLength *int64 `json:"minLength,omitempty"`
Pattern string `json:"pattern,omitempty"`
MaxItems *int64 `json:"maxItems,omitempty"`
MinItems *int64 `json:"minItems,omitempty"`
UniqueItems bool `json:"uniqueItems,omitempty"`
MultipleOf *float64 `json:"multipleOf,omitempty"`
Enum []interface{} `json:"enum,omitempty"`
MaxProperties *int64 `json:"maxProperties,omitempty"`
MinProperties *int64 `json:"minProperties,omitempty"`
Required []string `json:"required,omitempty"`
Items *SchemaOrArray `json:"items,omitempty"`
AllOf []Schema `json:"allOf,omitempty"`
OneOf []Schema `json:"oneOf,omitempty"`
AnyOf []Schema `json:"anyOf,omitempty"`
Not *Schema `json:"not,omitempty"`
Properties map[string]Schema `json:"properties,omitempty"`
AdditionalProperties *SchemaOrBool `json:"additionalProperties,omitempty"`
PatternProperties map[string]Schema `json:"patternProperties,omitempty"`
Dependencies Dependencies `json:"dependencies,omitempty"`
AdditionalItems *SchemaOrBool `json:"additionalItems,omitempty"`
Definitions Definitions `json:"definitions,omitempty"`
ID string `json:"id,omitempty"`
Ref Ref `json:"-"`
Schema SchemaURL `json:"-"`
Description string `json:"description,omitempty"`
Type StringOrArray `json:"type,omitempty"`
Nullable bool `json:"nullable,omitempty"`
Format string `json:"format,omitempty"`
Title string `json:"title,omitempty"`
Default interface{} `json:"default,omitempty"`
Maximum *float64 `json:"maximum,omitempty"`
ExclusiveMaximum bool `json:"exclusiveMaximum,omitempty"`
Minimum *float64 `json:"minimum,omitempty"`
ExclusiveMinimum bool `json:"exclusiveMinimum,omitempty"`
MaxLength *int64 `json:"maxLength,omitempty"`
MinLength *int64 `json:"minLength,omitempty"`
Pattern string `json:"pattern,omitempty"`
MaxItems *int64 `json:"maxItems,omitempty"`
MinItems *int64 `json:"minItems,omitempty"`
UniqueItems bool `json:"uniqueItems,omitempty"`
MultipleOf *float64 `json:"multipleOf,omitempty"`
Enum []interface{} `json:"enum,omitempty"`
MaxProperties *int64 `json:"maxProperties,omitempty"`
MinProperties *int64 `json:"minProperties,omitempty"`
Required []string `json:"required,omitempty"`
Items *SchemaOrArray `json:"items,omitempty"`
AllOf []Schema `json:"allOf,omitempty"`
OneOf []Schema `json:"oneOf,omitempty"`
AnyOf []Schema `json:"anyOf,omitempty"`
Not *Schema `json:"not,omitempty"`
Properties SchemaProperties `json:"properties,omitempty"`
AdditionalProperties *SchemaOrBool `json:"additionalProperties,omitempty"`
PatternProperties SchemaProperties `json:"patternProperties,omitempty"`
Dependencies Dependencies `json:"dependencies,omitempty"`
AdditionalItems *SchemaOrBool `json:"additionalItems,omitempty"`
Definitions Definitions `json:"definitions,omitempty"`
}
// SwaggerSchemaProps are additional properties supported by swagger schemas, but not JSON-schema (draft 4)
@ -513,6 +513,56 @@ func (s *Schema) AsUnwrappedXML() *Schema {
return s
}
// SetValidations defines all schema validations.
//
// NOTE: Required, ReadOnly, AllOf, AnyOf, OneOf and Not are not considered.
func (s *Schema) SetValidations(val SchemaValidations) {
s.Maximum = val.Maximum
s.ExclusiveMaximum = val.ExclusiveMaximum
s.Minimum = val.Minimum
s.ExclusiveMinimum = val.ExclusiveMinimum
s.MaxLength = val.MaxLength
s.MinLength = val.MinLength
s.Pattern = val.Pattern
s.MaxItems = val.MaxItems
s.MinItems = val.MinItems
s.UniqueItems = val.UniqueItems
s.MultipleOf = val.MultipleOf
s.Enum = val.Enum
s.MinProperties = val.MinProperties
s.MaxProperties = val.MaxProperties
s.PatternProperties = val.PatternProperties
}
// WithValidations is a fluent method to set schema validations
func (s *Schema) WithValidations(val SchemaValidations) *Schema {
s.SetValidations(val)
return s
}
// Validations returns a clone of the validations for this schema
func (s Schema) Validations() SchemaValidations {
return SchemaValidations{
CommonValidations: CommonValidations{
Maximum: s.Maximum,
ExclusiveMaximum: s.ExclusiveMaximum,
Minimum: s.Minimum,
ExclusiveMinimum: s.ExclusiveMinimum,
MaxLength: s.MaxLength,
MinLength: s.MinLength,
Pattern: s.Pattern,
MaxItems: s.MaxItems,
MinItems: s.MinItems,
UniqueItems: s.UniqueItems,
MultipleOf: s.MultipleOf,
Enum: s.Enum,
},
MinProperties: s.MinProperties,
MaxProperties: s.MaxProperties,
PatternProperties: s.PatternProperties,
}
}
// MarshalJSON marshal this to JSON
func (s Schema) MarshalJSON() ([]byte, error) {
b1, err := json.Marshal(s.SchemaProps)

View file

@ -25,7 +25,14 @@ import (
"github.com/go-openapi/swag"
)
// PathLoader function to use when loading remote refs
// PathLoader is a function to use when loading remote refs.
//
// This is a package level default. It may be overridden or bypassed by
// specifying the loader in ExpandOptions.
//
// NOTE: if you are using the go-openapi/loads package, it will override
// this value with its own default (a loader to retrieve YAML documents as
// well as JSON ones).
var PathLoader func(string) (json.RawMessage, error)
func init() {
@ -41,19 +48,30 @@ func init() {
// resolverContext allows to share a context during spec processing.
// At the moment, it just holds the index of circular references found.
type resolverContext struct {
// circulars holds all visited circular references, which allows shortcuts.
// NOTE: this is not just a performance improvement: it is required to figure out
// circular references which participate several cycles.
// circulars holds all visited circular references, to shortcircuit $ref resolution.
//
// This structure is privately instantiated and needs not be locked against
// concurrent access, unless we chose to implement a parallel spec walking.
circulars map[string]bool
basePath string
loadDoc func(string) (json.RawMessage, error)
}
func newResolverContext(originalBasePath string) *resolverContext {
func newResolverContext(expandOptions *ExpandOptions) *resolverContext {
absBase, _ := absPath(expandOptions.RelativeBase)
// path loader may be overridden from option
var loader func(string) (json.RawMessage, error)
if expandOptions.PathLoader == nil {
loader = PathLoader
} else {
loader = expandOptions.PathLoader
}
return &resolverContext{
circulars: make(map[string]bool),
basePath: originalBasePath, // keep the root base path in context
basePath: absBase, // keep the root base path in context
loadDoc: loader,
}
}
@ -62,21 +80,20 @@ type schemaLoader struct {
options *ExpandOptions
cache ResolutionCache
context *resolverContext
loadDoc func(string) (json.RawMessage, error)
}
func (r *schemaLoader) transitiveResolver(basePath string, ref Ref) (*schemaLoader, error) {
func (r *schemaLoader) transitiveResolver(basePath string, ref Ref) *schemaLoader {
if ref.IsRoot() || ref.HasFragmentOnly {
return r, nil
return r
}
baseRef, _ := NewRef(basePath)
baseRef := MustCreateRef(basePath)
currentRef := normalizeFileRef(&ref, basePath)
if strings.HasPrefix(currentRef.String(), baseRef.String()) {
return r, nil
return r
}
// Set a new root to resolve against
// set a new root against which to resolve
rootURL := currentRef.GetURL()
rootURL.Fragment = ""
root, _ := r.cache.Get(rootURL.String())
@ -85,16 +102,13 @@ func (r *schemaLoader) transitiveResolver(basePath string, ref Ref) (*schemaLoad
// traversing multiple documents
newOptions := r.options
newOptions.RelativeBase = rootURL.String()
debugLog("setting new root: %s", newOptions.RelativeBase)
return defaultSchemaLoader(root, newOptions, r.cache, r.context)
}
func (r *schemaLoader) updateBasePath(transitive *schemaLoader, basePath string) string {
if transitive != r {
debugLog("got a new resolver")
if transitive.options != nil && transitive.options.RelativeBase != "" {
basePath, _ = absPath(transitive.options.RelativeBase)
debugLog("new basePath = %s", basePath)
}
}
return basePath
@ -103,17 +117,19 @@ func (r *schemaLoader) updateBasePath(transitive *schemaLoader, basePath string)
func (r *schemaLoader) resolveRef(ref *Ref, target interface{}, basePath string) error {
tgt := reflect.ValueOf(target)
if tgt.Kind() != reflect.Ptr {
return fmt.Errorf("resolve ref: target needs to be a pointer")
return ErrResolveRefNeedsAPointer
}
refURL := ref.GetURL()
if refURL == nil {
if ref.GetURL() == nil {
return nil
}
var res interface{}
var data interface{}
var err error
var (
res interface{}
data interface{}
err error
)
// Resolve against the root if it isn't nil, and if ref is pointing at the root, or has a fragment only which means
// it is pointing somewhere in the root.
root := r.root
@ -122,12 +138,11 @@ func (r *schemaLoader) resolveRef(ref *Ref, target interface{}, basePath string)
root, _, _, _ = r.load(baseRef.GetURL())
}
}
if (ref.IsRoot() || ref.HasFragmentOnly) && root != nil {
data = root
} else {
baseRef := normalizeFileRef(ref, basePath)
debugLog("current ref is: %s", ref.String())
debugLog("current ref normalized file: %s", baseRef.String())
data, _, _, err = r.load(baseRef.GetURL())
if err != nil {
return err
@ -150,27 +165,29 @@ func (r *schemaLoader) load(refURL *url.URL) (interface{}, url.URL, bool, error)
toFetch.Fragment = ""
var err error
path := toFetch.String()
if path == rootBase {
path, err = absPath(rootBase)
pth := toFetch.String()
if pth == rootBase {
pth, err = absPath(rootBase)
if err != nil {
return nil, url.URL{}, false, err
}
}
normalized := normalizeAbsPath(path)
normalized := normalizeAbsPath(pth)
data, fromCache := r.cache.Get(normalized)
if !fromCache {
b, err := r.loadDoc(normalized)
b, err := r.context.loadDoc(normalized)
if err != nil {
debugLog("unable to load the document: %v", err)
return nil, url.URL{}, false, err
return nil, url.URL{}, false, fmt.Errorf("%s [%s]: %w", pth, normalized, err)
}
if err := json.Unmarshal(b, &data); err != nil {
var doc interface{}
if err := json.Unmarshal(b, &doc); err != nil {
return nil, url.URL{}, false, err
}
r.cache.Set(normalized, data)
r.cache.Set(normalized, doc)
return doc, toFetch, fromCache, nil
}
return data, toFetch, fromCache, nil
@ -185,17 +202,20 @@ func (r *schemaLoader) isCircular(ref *Ref, basePath string, parentRefs ...strin
foundCycle = true
return
}
foundCycle = swag.ContainsStringsCI(parentRefs, normalizedRef)
foundCycle = swag.ContainsStringsCI(parentRefs, normalizedRef) // TODO(fred): normalize windows url and remove CI equality
if foundCycle {
r.context.circulars[normalizedRef] = true
}
return
}
// Resolve resolves a reference against basePath and stores the result in target
// Resolve is not in charge of following references, it only resolves ref by following its URL
// if the schema that ref is referring to has more refs in it. Resolve doesn't resolve them
// if basePath is an empty string, ref is resolved against the root schema stored in the schemaLoader struct
// Resolve resolves a reference against basePath and stores the result in target.
//
// Resolve is not in charge of following references: it only resolves ref by following its URL.
//
// If the schema the ref is referring to holds nested refs, Resolve doesn't resolve them.
//
// If basePath is an empty string, ref is resolved against the root schema stored in the schemaLoader struct
func (r *schemaLoader) Resolve(ref *Ref, target interface{}, basePath string) error {
return r.resolveRef(ref, target, basePath)
}
@ -212,30 +232,32 @@ func (r *schemaLoader) deref(input interface{}, parentRefs []string, basePath st
case *PathItem:
ref = &refable.Ref
default:
return fmt.Errorf("deref: unsupported type %T", input)
return fmt.Errorf("unsupported type: %T: %w", input, ErrDerefUnsupportedType)
}
curRef := ref.String()
if curRef != "" {
normalizedRef := normalizeFileRef(ref, basePath)
normalizedBasePath := normalizedRef.RemoteURI()
if r.isCircular(normalizedRef, basePath, parentRefs...) {
return nil
}
if err := r.resolveRef(ref, input, basePath); r.shouldStopOnError(err) {
return err
}
// NOTE(fredbi): removed basePath check => needs more testing
if ref.String() != "" && ref.String() != curRef {
parentRefs = append(parentRefs, normalizedRef.String())
return r.deref(input, parentRefs, normalizedBasePath)
}
if curRef == "" {
return nil
}
return nil
normalizedRef := normalizeFileRef(ref, basePath)
normalizedBasePath := normalizedRef.RemoteURI()
if r.isCircular(normalizedRef, basePath, parentRefs...) {
return nil
}
if err := r.resolveRef(ref, input, basePath); r.shouldStopOnError(err) {
return err
}
if ref.String() == "" || ref.String() == curRef {
// done with rereferencing
return nil
}
parentRefs = append(parentRefs, normalizedRef.String())
return r.deref(input, parentRefs, normalizedBasePath)
}
func (r *schemaLoader) shouldStopOnError(err error) bool {
@ -250,30 +272,48 @@ func (r *schemaLoader) shouldStopOnError(err error) bool {
return false
}
func (r *schemaLoader) setSchemaID(target interface{}, id, basePath string) (string, string) {
debugLog("schema has ID: %s", id)
// handling the case when id is a folder
// remember that basePath has to point to a file
var refPath string
if strings.HasSuffix(id, "/") {
// path.Clean here would not work correctly if there is a scheme (e.g. https://...)
refPath = fmt.Sprintf("%s%s", id, "placeholder.json")
} else {
refPath = id
}
// updates the current base path
// * important: ID can be a relative path
// * registers target to be fetchable from the new base proposed by this id
newBasePath := normalizePaths(refPath, basePath)
// store found IDs for possible future reuse in $ref
r.cache.Set(newBasePath, target)
return newBasePath, refPath
}
func defaultSchemaLoader(
root interface{},
expandOptions *ExpandOptions,
cache ResolutionCache,
context *resolverContext) (*schemaLoader, error) {
context *resolverContext) *schemaLoader {
if cache == nil {
cache = resCache
}
if expandOptions == nil {
expandOptions = &ExpandOptions{}
}
absBase, _ := absPath(expandOptions.RelativeBase)
if context == nil {
context = newResolverContext(absBase)
context = newResolverContext(expandOptions)
}
return &schemaLoader{
root: root,
options: expandOptions,
cache: cache,
cache: cacheOrDefault(cache),
context: context,
loadDoc: func(path string) (json.RawMessage, error) {
debugLog("fetching document at %q", path)
return PathLoader(path)
},
}, nil
}
}

View file

@ -82,12 +82,12 @@ func OAuth2AccessToken(authorizationURL, tokenURL string) *SecurityScheme {
type SecuritySchemeProps struct {
Description string `json:"description,omitempty"`
Type string `json:"type"`
Name string `json:"name,omitempty"` // api key
In string `json:"in,omitempty"` // api key
Flow string `json:"flow,omitempty"` // oauth2
AuthorizationURL string `json:"authorizationUrl,omitempty"` // oauth2
TokenURL string `json:"tokenUrl,omitempty"` // oauth2
Scopes map[string]string `json:"scopes,omitempty"` // oauth2
Name string `json:"name,omitempty"` // api key
In string `json:"in,omitempty"` // api key
Flow string `json:"flow,omitempty"` // oauth2
AuthorizationURL string `json:"authorizationUrl"` // oauth2
TokenURL string `json:"tokenUrl,omitempty"` // oauth2
Scopes map[string]string `json:"scopes,omitempty"` // oauth2
}
// AddScope adds a scope to this security scheme
@ -120,10 +120,40 @@ func (s SecurityScheme) JSONLookup(token string) (interface{}, error) {
// MarshalJSON marshal this to JSON
func (s SecurityScheme) MarshalJSON() ([]byte, error) {
b1, err := json.Marshal(s.SecuritySchemeProps)
var (
b1 []byte
err error
)
if s.Type == oauth2 {
// when oauth2, empty AuthorizationURL is added as empty string
b1, err = json.Marshal(s.SecuritySchemeProps)
} else {
// when not oauth2, empty AuthorizationURL should be omitted
b1, err = json.Marshal(struct {
Description string `json:"description,omitempty"`
Type string `json:"type"`
Name string `json:"name,omitempty"` // api key
In string `json:"in,omitempty"` // api key
Flow string `json:"flow,omitempty"` // oauth2
AuthorizationURL string `json:"authorizationUrl,omitempty"` // oauth2
TokenURL string `json:"tokenUrl,omitempty"` // oauth2
Scopes map[string]string `json:"scopes,omitempty"` // oauth2
}{
Description: s.Description,
Type: s.Type,
Name: s.Name,
In: s.In,
Flow: s.Flow,
AuthorizationURL: s.AuthorizationURL,
TokenURL: s.TokenURL,
Scopes: s.Scopes,
})
}
if err != nil {
return nil, err
}
b2, err := json.Marshal(s.VendorExtensible)
if err != nil {
return nil, err

View file

@ -14,7 +14,9 @@
package spec
import "encoding/json"
import (
"encoding/json"
)
//go:generate curl -L --progress -o ./schemas/v2/schema.json http://swagger.io/v2/schema.json
//go:generate curl -L --progress -o ./schemas/jsonschema-draft-04.json http://json-schema.org/draft-04/schema
@ -28,16 +30,6 @@ const (
JSONSchemaURL = "http://json-schema.org/draft-04/schema#"
)
var (
jsonSchema *Schema
swaggerSchema *Schema
)
func init() {
jsonSchema = MustLoadJSONSchemaDraft04()
swaggerSchema = MustLoadSwagger20Schema()
}
// MustLoadJSONSchemaDraft04 panics when Swagger20Schema returns an error
func MustLoadJSONSchemaDraft04() *Schema {
d, e := JSONSchemaDraft04()

215
vendor/github.com/go-openapi/spec/validations.go generated vendored Normal file
View file

@ -0,0 +1,215 @@
package spec
// CommonValidations describe common JSON-schema validations
type CommonValidations struct {
Maximum *float64 `json:"maximum,omitempty"`
ExclusiveMaximum bool `json:"exclusiveMaximum,omitempty"`
Minimum *float64 `json:"minimum,omitempty"`
ExclusiveMinimum bool `json:"exclusiveMinimum,omitempty"`
MaxLength *int64 `json:"maxLength,omitempty"`
MinLength *int64 `json:"minLength,omitempty"`
Pattern string `json:"pattern,omitempty"`
MaxItems *int64 `json:"maxItems,omitempty"`
MinItems *int64 `json:"minItems,omitempty"`
UniqueItems bool `json:"uniqueItems,omitempty"`
MultipleOf *float64 `json:"multipleOf,omitempty"`
Enum []interface{} `json:"enum,omitempty"`
}
// SetValidations defines all validations for a simple schema.
//
// NOTE: the input is the larger set of validations available for schemas.
// For simple schemas, MinProperties and MaxProperties are ignored.
func (v *CommonValidations) SetValidations(val SchemaValidations) {
v.Maximum = val.Maximum
v.ExclusiveMaximum = val.ExclusiveMaximum
v.Minimum = val.Minimum
v.ExclusiveMinimum = val.ExclusiveMinimum
v.MaxLength = val.MaxLength
v.MinLength = val.MinLength
v.Pattern = val.Pattern
v.MaxItems = val.MaxItems
v.MinItems = val.MinItems
v.UniqueItems = val.UniqueItems
v.MultipleOf = val.MultipleOf
v.Enum = val.Enum
}
type clearedValidation struct {
Validation string
Value interface{}
}
type clearedValidations []clearedValidation
func (c clearedValidations) apply(cbs []func(string, interface{})) {
for _, cb := range cbs {
for _, cleared := range c {
cb(cleared.Validation, cleared.Value)
}
}
}
// ClearNumberValidations clears all number validations.
//
// Some callbacks may be set by the caller to capture changed values.
func (v *CommonValidations) ClearNumberValidations(cbs ...func(string, interface{})) {
done := make(clearedValidations, 0, 5)
defer func() {
done.apply(cbs)
}()
if v.Minimum != nil {
done = append(done, clearedValidation{Validation: "minimum", Value: v.Minimum})
v.Minimum = nil
}
if v.Maximum != nil {
done = append(done, clearedValidation{Validation: "maximum", Value: v.Maximum})
v.Maximum = nil
}
if v.ExclusiveMaximum {
done = append(done, clearedValidation{Validation: "exclusiveMaximum", Value: v.ExclusiveMaximum})
v.ExclusiveMaximum = false
}
if v.ExclusiveMinimum {
done = append(done, clearedValidation{Validation: "exclusiveMinimum", Value: v.ExclusiveMinimum})
v.ExclusiveMinimum = false
}
if v.MultipleOf != nil {
done = append(done, clearedValidation{Validation: "multipleOf", Value: v.MultipleOf})
v.MultipleOf = nil
}
}
// ClearStringValidations clears all string validations.
//
// Some callbacks may be set by the caller to capture changed values.
func (v *CommonValidations) ClearStringValidations(cbs ...func(string, interface{})) {
done := make(clearedValidations, 0, 3)
defer func() {
done.apply(cbs)
}()
if v.Pattern != "" {
done = append(done, clearedValidation{Validation: "pattern", Value: v.Pattern})
v.Pattern = ""
}
if v.MinLength != nil {
done = append(done, clearedValidation{Validation: "minLength", Value: v.MinLength})
v.MinLength = nil
}
if v.MaxLength != nil {
done = append(done, clearedValidation{Validation: "maxLength", Value: v.MaxLength})
v.MaxLength = nil
}
}
// ClearArrayValidations clears all array validations.
//
// Some callbacks may be set by the caller to capture changed values.
func (v *CommonValidations) ClearArrayValidations(cbs ...func(string, interface{})) {
done := make(clearedValidations, 0, 3)
defer func() {
done.apply(cbs)
}()
if v.MaxItems != nil {
done = append(done, clearedValidation{Validation: "maxItems", Value: v.MaxItems})
v.MaxItems = nil
}
if v.MinItems != nil {
done = append(done, clearedValidation{Validation: "minItems", Value: v.MinItems})
v.MinItems = nil
}
if v.UniqueItems {
done = append(done, clearedValidation{Validation: "uniqueItems", Value: v.UniqueItems})
v.UniqueItems = false
}
}
// Validations returns a clone of the validations for a simple schema.
//
// NOTE: in the context of simple schema objects, MinProperties, MaxProperties
// and PatternProperties remain unset.
func (v CommonValidations) Validations() SchemaValidations {
return SchemaValidations{
CommonValidations: v,
}
}
// HasNumberValidations indicates if the validations are for numbers or integers
func (v CommonValidations) HasNumberValidations() bool {
return v.Maximum != nil || v.Minimum != nil || v.MultipleOf != nil
}
// HasStringValidations indicates if the validations are for strings
func (v CommonValidations) HasStringValidations() bool {
return v.MaxLength != nil || v.MinLength != nil || v.Pattern != ""
}
// HasArrayValidations indicates if the validations are for arrays
func (v CommonValidations) HasArrayValidations() bool {
return v.MaxItems != nil || v.MinItems != nil || v.UniqueItems
}
// HasEnum indicates if the validation includes some enum constraint
func (v CommonValidations) HasEnum() bool {
return len(v.Enum) > 0
}
// SchemaValidations describes the validation properties of a schema
//
// NOTE: at this moment, this is not embedded in SchemaProps because this would induce a breaking change
// in the exported members: all initializers using litterals would fail.
type SchemaValidations struct {
CommonValidations
PatternProperties SchemaProperties `json:"patternProperties,omitempty"`
MaxProperties *int64 `json:"maxProperties,omitempty"`
MinProperties *int64 `json:"minProperties,omitempty"`
}
// HasObjectValidations indicates if the validations are for objects
func (v SchemaValidations) HasObjectValidations() bool {
return v.MaxProperties != nil || v.MinProperties != nil || v.PatternProperties != nil
}
// SetValidations for schema validations
func (v *SchemaValidations) SetValidations(val SchemaValidations) {
v.CommonValidations.SetValidations(val)
v.PatternProperties = val.PatternProperties
v.MaxProperties = val.MaxProperties
v.MinProperties = val.MinProperties
}
// Validations for a schema
func (v SchemaValidations) Validations() SchemaValidations {
val := v.CommonValidations.Validations()
val.PatternProperties = v.PatternProperties
val.MinProperties = v.MinProperties
val.MaxProperties = v.MaxProperties
return val
}
// ClearObjectValidations returns a clone of the validations with all object validations cleared.
//
// Some callbacks may be set by the caller to capture changed values.
func (v *SchemaValidations) ClearObjectValidations(cbs ...func(string, interface{})) {
done := make(clearedValidations, 0, 3)
defer func() {
done.apply(cbs)
}()
if v.MaxProperties != nil {
done = append(done, clearedValidation{Validation: "maxProperties", Value: v.MaxProperties})
v.MaxProperties = nil
}
if v.MinProperties != nil {
done = append(done, clearedValidation{Validation: "minProperties", Value: v.MinProperties})
v.MinProperties = nil
}
if v.PatternProperties != nil {
done = append(done, clearedValidation{Validation: "patternProperties", Value: v.PatternProperties})
v.PatternProperties = nil
}
}