forked from ebhomengo/niki
1
0
Fork 0
niki/vendor/github.com/go-openapi/spec/normalizer.go

334 lines
6.4 KiB
Go
Raw Normal View History

2024-05-14 13:07:09 +00:00
// Copyright 2015 go-swagger maintainers
2024-05-14 13:07:09 +00:00
//
2024-05-14 13:07:09 +00:00
// Licensed under the Apache License, Version 2.0 (the "License");
2024-05-14 13:07:09 +00:00
// you may not use this file except in compliance with the License.
2024-05-14 13:07:09 +00:00
// You may obtain a copy of the License at
2024-05-14 13:07:09 +00:00
//
2024-05-14 13:07:09 +00:00
// http://www.apache.org/licenses/LICENSE-2.0
2024-05-14 13:07:09 +00:00
//
2024-05-14 13:07:09 +00:00
// Unless required by applicable law or agreed to in writing, software
2024-05-14 13:07:09 +00:00
// distributed under the License is distributed on an "AS IS" BASIS,
2024-05-14 13:07:09 +00:00
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2024-05-14 13:07:09 +00:00
// See the License for the specific language governing permissions and
2024-05-14 13:07:09 +00:00
// limitations under the License.
package spec
import (
"net/url"
"path"
"strings"
)
const fileScheme = "file"
// normalizeURI ensures that all $ref paths used internally by the expander are canonicalized.
2024-05-14 13:07:09 +00:00
//
2024-05-14 13:07:09 +00:00
// NOTE(windows): there is a tolerance over the strict URI format on windows.
2024-05-14 13:07:09 +00:00
//
2024-05-14 13:07:09 +00:00
// The normalizer accepts relative file URLs like 'Path\File.JSON' as well as absolute file URLs like
2024-05-14 13:07:09 +00:00
// 'C:\Path\file.Yaml'.
2024-05-14 13:07:09 +00:00
//
2024-05-14 13:07:09 +00:00
// Both are canonicalized with a "file://" scheme, slashes and a lower-cased path:
2024-05-14 13:07:09 +00:00
// 'file:///c:/path/file.yaml'
2024-05-14 13:07:09 +00:00
//
2024-05-14 13:07:09 +00:00
// URLs can be specified with a file scheme, like in 'file:///folder/file.json' or
2024-05-14 13:07:09 +00:00
// 'file:///c:\folder\File.json'.
2024-05-14 13:07:09 +00:00
//
2024-05-14 13:07:09 +00:00
// URLs like file://C:\folder are considered invalid (i.e. there is no host 'c:\folder') and a "repair"
2024-05-14 13:07:09 +00:00
// is attempted.
2024-05-14 13:07:09 +00:00
//
2024-05-14 13:07:09 +00:00
// The base path argument is assumed to be canonicalized (e.g. using normalizeBase()).
2024-05-14 13:07:09 +00:00
func normalizeURI(refPath, base string) string {
2024-05-14 13:07:09 +00:00
refURL, err := parseURL(refPath)
2024-05-14 13:07:09 +00:00
if err != nil {
2024-05-14 13:07:09 +00:00
specLogger.Printf("warning: invalid URI in $ref %q: %v", refPath, err)
2024-05-14 13:07:09 +00:00
refURL, refPath = repairURI(refPath)
2024-05-14 13:07:09 +00:00
}
fixWindowsURI(refURL, refPath) // noop on non-windows OS
refURL.Path = path.Clean(refURL.Path)
2024-05-14 13:07:09 +00:00
if refURL.Path == "." {
2024-05-14 13:07:09 +00:00
refURL.Path = ""
2024-05-14 13:07:09 +00:00
}
r := MustCreateRef(refURL.String())
2024-05-14 13:07:09 +00:00
if r.IsCanonical() {
2024-05-14 13:07:09 +00:00
return refURL.String()
2024-05-14 13:07:09 +00:00
}
baseURL, _ := parseURL(base)
2024-05-14 13:07:09 +00:00
if path.IsAbs(refURL.Path) {
2024-05-14 13:07:09 +00:00
baseURL.Path = refURL.Path
2024-05-14 13:07:09 +00:00
} else if refURL.Path != "" {
2024-05-14 13:07:09 +00:00
baseURL.Path = path.Join(path.Dir(baseURL.Path), refURL.Path)
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
// copying fragment from ref to base
2024-05-14 13:07:09 +00:00
baseURL.Fragment = refURL.Fragment
return baseURL.String()
2024-05-14 13:07:09 +00:00
}
// denormalizeRef returns the simplest notation for a normalized $ref, given the path of the original root document.
2024-05-14 13:07:09 +00:00
//
2024-05-14 13:07:09 +00:00
// When calling this, we assume that:
2024-05-14 13:07:09 +00:00
// * $ref is a canonical URI
2024-05-14 13:07:09 +00:00
// * originalRelativeBase is a canonical URI
2024-05-14 13:07:09 +00:00
//
2024-05-14 13:07:09 +00:00
// denormalizeRef is currently used when we rewrite a $ref after a circular $ref has been detected.
2024-05-14 13:07:09 +00:00
// In this case, expansion stops and normally renders the internal canonical $ref.
2024-05-14 13:07:09 +00:00
//
2024-05-14 13:07:09 +00:00
// This internal $ref is eventually rebased to the original RelativeBase used for the expansion.
2024-05-14 13:07:09 +00:00
//
2024-05-14 13:07:09 +00:00
// There is a special case for schemas that are anchored with an "id":
2024-05-14 13:07:09 +00:00
// in that case, the rebasing is performed // against the id only if this is an anchor for the initial root document.
2024-05-14 13:07:09 +00:00
// All other intermediate "id"'s found along the way are ignored for the purpose of rebasing.
2024-05-14 13:07:09 +00:00
func denormalizeRef(ref *Ref, originalRelativeBase, id string) Ref {
2024-05-14 13:07:09 +00:00
debugLog("denormalizeRef called:\n$ref: %q\noriginal: %s\nroot ID:%s", ref.String(), originalRelativeBase, id)
if ref.String() == "" || ref.IsRoot() || ref.HasFragmentOnly {
2024-05-14 13:07:09 +00:00
// short circuit: $ref to current doc
2024-05-14 13:07:09 +00:00
return *ref
2024-05-14 13:07:09 +00:00
}
if id != "" {
2024-05-14 13:07:09 +00:00
idBaseURL, err := parseURL(id)
2024-05-14 13:07:09 +00:00
if err == nil { // if the schema id is not usable as a URI, ignore it
2024-05-14 13:07:09 +00:00
if ref, ok := rebase(ref, idBaseURL, true); ok { // rebase, but keep references to root unchaged (do not want $ref: "")
2024-05-14 13:07:09 +00:00
// $ref relative to the ID of the schema in the root document
2024-05-14 13:07:09 +00:00
return ref
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
originalRelativeBaseURL, _ := parseURL(originalRelativeBase)
r, _ := rebase(ref, originalRelativeBaseURL, false)
return r
2024-05-14 13:07:09 +00:00
}
func rebase(ref *Ref, v *url.URL, notEqual bool) (Ref, bool) {
2024-05-14 13:07:09 +00:00
var newBase url.URL
u := ref.GetURL()
if u.Scheme != v.Scheme || u.Host != v.Host {
2024-05-14 13:07:09 +00:00
return *ref, false
2024-05-14 13:07:09 +00:00
}
docPath := v.Path
2024-05-14 13:07:09 +00:00
v.Path = path.Dir(v.Path)
if v.Path == "." {
2024-05-14 13:07:09 +00:00
v.Path = ""
2024-05-14 13:07:09 +00:00
} else if !strings.HasSuffix(v.Path, "/") {
2024-05-14 13:07:09 +00:00
v.Path += "/"
2024-05-14 13:07:09 +00:00
}
newBase.Fragment = u.Fragment
if strings.HasPrefix(u.Path, docPath) {
2024-05-14 13:07:09 +00:00
newBase.Path = strings.TrimPrefix(u.Path, docPath)
2024-05-14 13:07:09 +00:00
} else {
2024-05-14 13:07:09 +00:00
newBase.Path = strings.TrimPrefix(u.Path, v.Path)
2024-05-14 13:07:09 +00:00
}
if notEqual && newBase.Path == "" && newBase.Fragment == "" {
2024-05-14 13:07:09 +00:00
// do not want rebasing to end up in an empty $ref
2024-05-14 13:07:09 +00:00
return *ref, false
2024-05-14 13:07:09 +00:00
}
if path.IsAbs(newBase.Path) {
2024-05-14 13:07:09 +00:00
// whenever we end up with an absolute path, specify the scheme and host
2024-05-14 13:07:09 +00:00
newBase.Scheme = v.Scheme
2024-05-14 13:07:09 +00:00
newBase.Host = v.Host
2024-05-14 13:07:09 +00:00
}
return MustCreateRef(newBase.String()), true
2024-05-14 13:07:09 +00:00
}
// normalizeRef canonicalize a Ref, using a canonical relativeBase as its absolute anchor
2024-05-14 13:07:09 +00:00
func normalizeRef(ref *Ref, relativeBase string) *Ref {
2024-05-14 13:07:09 +00:00
r := MustCreateRef(normalizeURI(ref.String(), relativeBase))
2024-05-14 13:07:09 +00:00
return &r
2024-05-14 13:07:09 +00:00
}
// normalizeBase performs a normalization of the input base path.
2024-05-14 13:07:09 +00:00
//
2024-05-14 13:07:09 +00:00
// This always yields a canonical URI (absolute), usable for the document cache.
2024-05-14 13:07:09 +00:00
//
2024-05-14 13:07:09 +00:00
// It ensures that all further internal work on basePath may safely assume
2024-05-14 13:07:09 +00:00
// a non-empty, cross-platform, canonical URI (i.e. absolute).
2024-05-14 13:07:09 +00:00
//
2024-05-14 13:07:09 +00:00
// This normalization tolerates windows paths (e.g. C:\x\y\File.dat) and transform this
2024-05-14 13:07:09 +00:00
// in a file:// URL with lower cased drive letter and path.
2024-05-14 13:07:09 +00:00
//
2024-05-14 13:07:09 +00:00
// See also: https://en.wikipedia.org/wiki/File_URI_scheme
2024-05-14 13:07:09 +00:00
func normalizeBase(in string) string {
2024-05-14 13:07:09 +00:00
u, err := parseURL(in)
2024-05-14 13:07:09 +00:00
if err != nil {
2024-05-14 13:07:09 +00:00
specLogger.Printf("warning: invalid URI in RelativeBase %q: %v", in, err)
2024-05-14 13:07:09 +00:00
u, in = repairURI(in)
2024-05-14 13:07:09 +00:00
}
u.Fragment = "" // any fragment in the base is irrelevant
fixWindowsURI(u, in) // noop on non-windows OS
u.Path = path.Clean(u.Path)
2024-05-14 13:07:09 +00:00
if u.Path == "." { // empty after Clean()
2024-05-14 13:07:09 +00:00
u.Path = ""
2024-05-14 13:07:09 +00:00
}
if u.Scheme != "" {
2024-05-14 13:07:09 +00:00
if path.IsAbs(u.Path) || u.Scheme != fileScheme {
2024-05-14 13:07:09 +00:00
// this is absolute or explicitly not a local file: we're good
2024-05-14 13:07:09 +00:00
return u.String()
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
// no scheme or file scheme with relative path: assume file and make it absolute
2024-05-14 13:07:09 +00:00
// enforce scheme file://... with absolute path.
2024-05-14 13:07:09 +00:00
//
2024-05-14 13:07:09 +00:00
// If the input path is relative, we anchor the path to the current working directory.
2024-05-14 13:07:09 +00:00
// NOTE: we may end up with a host component. Leave it unchanged: e.g. file://host/folder/file.json
u.Scheme = fileScheme
2024-05-14 13:07:09 +00:00
u.Path = absPath(u.Path) // platform-dependent
u.RawQuery = "" // any query component is irrelevant for a base
2024-05-14 13:07:09 +00:00
return u.String()
2024-05-14 13:07:09 +00:00
}