mirror of
https://github.com/kubernetes/sample-controller.git
synced 2026-04-12 00:00:26 +08:00
Merge pull request #70663 from wenjiaswe/gonet110
Upgrade golang.org/x/net image to release-branch.go1.10 Kubernetes-commit: 471aff6673ebe4ac9c9219a7579d23831e1146be
This commit is contained in:
+5
-5
@@ -24,19 +24,19 @@
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/go-openapi/jsonpointer",
|
||||
"Rev": "46af16f9f7b149af66e5d1bd010e3574dc06de98"
|
||||
"Rev": "ef5f0afec364d3b9396b7b77b43dbe26bf1f8004"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/go-openapi/jsonreference",
|
||||
"Rev": "13c6e3589ad90f49bd3e3bbe2c2cb3d7a4142272"
|
||||
"Rev": "8483a886a90412cd6858df4ea3483dce9c8e35a3"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/go-openapi/spec",
|
||||
"Rev": "1de3e0542de65ad8d75452a595886fdd0befb363"
|
||||
"Rev": "5bae59e25b21498baea7f9d46e9c147ec106a42e"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/go-openapi/swag",
|
||||
"Rev": "f3f9494671f93fcff853e3c6e9e948b3eb71e590"
|
||||
"Rev": "5899d5c5e619fda5fa86e14795a835f473ca284c"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/gogo/protobuf/gogoproto",
|
||||
@@ -160,7 +160,7 @@
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/idna",
|
||||
"Rev": "1c05540f6879653db88113bc4a2b70aec4bd491f"
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/text/cases",
|
||||
|
||||
-1
@@ -1 +0,0 @@
|
||||
eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.pDqezepze0YqRx4u6M8GFaWtnVR-utTWZic-GX-RvMATAoYpG4H2sc9tlnGNCxa44dbRY0vY10qfBU7Sno8vkp21fsK42ofGLfen_suum_0ilm0sFS0X-kAwk7TIq5L5lPPKiChPMUiGp5oJW-g5MqMFX1jNiI-4fP-vSM3B3-eyZtJD_O517TgfIRLnblCzqwIkyRmAfPNopi-Fe8Y31TmO2Vd0nFc1Aqro_VaJSACzEVxOHTNpjETcMjlYzwgMXLeiAfLV-5hM0f6DXgHMlLSuMkB_Ndnw25dkB7hreGk4x0tHQ3X9mUfTgLq1hIDoyeeKDIM83Tqw4LBRph20BQ.qd_pNuyi23B0PlWz.JtpO7kqOm0SWOGzWDalkWheHuNd-eDpVbqI9WPAEFDOIBvz7TbsYMBlIYVWEGWbat4mkx_ejxnMn1L1l996NJnyP7eY-QE82cfPJbjx94d0Ob70KZ4DCm_UxcY2t-OKFiPJqxW7MA5jKyDuGD16bdxpjLEoe_cMSEr8FNu-MVG6wcchPcyYyRkqTQSl4mb09KikkAzHjwjo-DcO0f8ps4Uzsoc0aqAAWdE-ocG0YqierLoemjusYMiLH-eLF6MvaLRvHSte-cLzPuYCeZURnBDgxu3i3UApgddnX7g1c7tdGGBGvgCl-tEEDW58Vxgdjksim2S7y3lfoJ8FFzSWeRH2y7Kq04hgew3b2J_RiDB9ejzIopzG8ZGjJa3EO1-i9ORTl12nXK1RdlLGqu604ENaeVOPCIHL-0C8e6_wHdUGHydLZImSxKYSrNvy8resP1D_9t4B-3q2mkS9mhnMONrXbPDVw5QY5mvXlWs0Db99ARwzsl-Qlu0A_tsZwMjWT2I1QMvWPyTRScmMm0FJSv9zStjzxWa_q2GL7Naz1fI4Dd6ZgNJWYYq-mHN5chEeBdIcwb_zMPHczMQXXNL5nmfRGM1aPffkToFWCDpIlI8IXec83ZC6_POxZegS6n9Drrvc.6Nz8EXxs1lWX3ASaCeNElA
|
||||
-32
@@ -1,32 +0,0 @@
|
||||
clone:
|
||||
path: github.com/go-openapi/jsonpointer
|
||||
|
||||
matrix:
|
||||
GO_VERSION:
|
||||
- "1.6"
|
||||
|
||||
build:
|
||||
integration:
|
||||
image: golang:$$GO_VERSION
|
||||
pull: true
|
||||
commands:
|
||||
- go get -u github.com/stretchr/testify/assert
|
||||
- go get -u github.com/go-openapi/swag
|
||||
- go test -race
|
||||
- go test -v -cover -coverprofile=coverage.out -covermode=count ./...
|
||||
|
||||
notify:
|
||||
slack:
|
||||
channel: bots
|
||||
webhook_url: $$SLACK_URL
|
||||
username: drone
|
||||
|
||||
publish:
|
||||
coverage:
|
||||
server: https://coverage.vmware.run
|
||||
token: $$GITHUB_TOKEN
|
||||
# threshold: 70
|
||||
# must_increase: true
|
||||
when:
|
||||
matrix:
|
||||
GO_VERSION: "1.6"
|
||||
Generated
Vendored
+26
@@ -0,0 +1,26 @@
|
||||
# top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
# Unix-style newlines with a newline ending every file
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
# Set default charset
|
||||
[*.{js,py,go,scala,rb,java,html,css,less,sass,md}]
|
||||
charset = utf-8
|
||||
|
||||
# Tab indentation (no size specified)
|
||||
[*.go]
|
||||
indent_style = tab
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
# Matches the exact files either package.json or .travis.yml
|
||||
[{package.json,.travis.yml}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
Generated
Vendored
-13
@@ -1,13 +0,0 @@
|
||||
approve_by_comment: true
|
||||
approve_regex: '^(:shipit:|:\+1:|\+1|LGTM|lgtm|Approved)'
|
||||
reject_regex: ^[Rr]ejected
|
||||
reset_on_push: false
|
||||
reviewers:
|
||||
members:
|
||||
- casualjim
|
||||
- chancez
|
||||
- frapposelli
|
||||
- vburenin
|
||||
- pytlesk4
|
||||
name: pullapprove
|
||||
required: 1
|
||||
Generated
Vendored
+15
@@ -0,0 +1,15 @@
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
go:
|
||||
- '1.9'
|
||||
- 1.10.x
|
||||
- 1.11.x
|
||||
install:
|
||||
- go get -u github.com/stretchr/testify/assert
|
||||
- go get -u github.com/go-openapi/swag
|
||||
language: go
|
||||
notifications:
|
||||
slack:
|
||||
secure: a5VgoiwB1G/AZqzmephPZIhEB9avMlsWSlVnM1dSAtYAwdrQHGTQxAmpOxYIoSPDhWNN5bfZmjd29++UlTwLcHSR+e0kJhH6IfDlsHj/HplNCJ9tyI0zYc7XchtdKgeMxMzBKCzgwFXGSbQGydXTliDNBo0HOzmY3cou/daMFTP60K+offcjS+3LRAYb1EroSRXZqrk1nuF/xDL3792DZUdPMiFR/L/Df6y74D6/QP4sTkTDFQitz4Wy/7jbsfj8dG6qK2zivgV6/l+w4OVjFkxVpPXogDWY10vVXNVynqxfJ7to2d1I9lNCHE2ilBCkWMIPdyJF7hjF8pKW+82yP4EzRh0vu8Xn0HT5MZpQxdRY/YMxNrWaG7SxsoEaO4q5uhgdzAqLYY3TRa7MjIK+7Ur+aqOeTXn6OKwVi0CjvZ6mIU3WUKSwiwkFZMbjRAkSb5CYwMEfGFO/z964xz83qGt6WAtBXNotqCQpTIiKtDHQeLOMfksHImCg6JLhQcWBVxamVgu0G3Pdh8Y6DyPnxraXY95+QDavbjqv7TeYT9T/FNnrkXaTTK0s4iWE5H4ACU0Qvz0wUYgfQrZv0/Hp7V17+rabUwnzYySHCy9SWX/7OV9Cfh31iMp9ZIffr76xmmThtOEqs8TrTtU6BWI3rWwvA9cXQipZTVtL0oswrGw=
|
||||
script:
|
||||
- go test -v -race -cover -coverprofile=coverage.txt -covermode=atomic ./...
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
# gojsonpointer [](https://ci.vmware.run/go-openapi/jsonpointer) [](https://coverage.vmware.run/go-openapi/jsonpointer) [](https://slackin.goswagger.io)
|
||||
# gojsonpointer [](https://travis-ci.org/go-openapi/jsonpointer) [](https://codecov.io/gh/go-openapi/jsonpointer) [](https://slackin.goswagger.io)
|
||||
|
||||
[](https://raw.githubusercontent.com/go-openapi/jsonpointer/master/LICENSE) [](http://godoc.org/github.com/go-openapi/jsonpointer)
|
||||
An implementation of JSON Pointer - Go language
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
module github.com/go-openapi/jsonpointer
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/go-openapi/swag v0.17.0
|
||||
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/stretchr/testify v1.2.2
|
||||
gopkg.in/yaml.v2 v2.2.1 // indirect
|
||||
)
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
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/swag v0.17.0 h1:7wu+dZ5k83kvUWeAb+WUkFiUhDzwGqzTR/NhWzeo1JU=
|
||||
github.com/go-openapi/swag v0.17.0/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
|
||||
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 h1:2gxZ0XQIU/5z3Z3bUBu+FXuk2pFbkN6tcwi/pjyaDic=
|
||||
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
+165
-13
@@ -43,6 +43,7 @@ const (
|
||||
)
|
||||
|
||||
var jsonPointableType = reflect.TypeOf(new(JSONPointable)).Elem()
|
||||
var jsonSetableType = reflect.TypeOf(new(JSONSetable)).Elem()
|
||||
|
||||
// JSONPointable is an interface for structs to implement when they need to customize the
|
||||
// json pointer process
|
||||
@@ -50,16 +51,10 @@ type JSONPointable interface {
|
||||
JSONLookup(string) (interface{}, error)
|
||||
}
|
||||
|
||||
type implStruct struct {
|
||||
mode string // "SET" or "GET"
|
||||
|
||||
inDocument interface{}
|
||||
|
||||
setInValue interface{}
|
||||
|
||||
getOutNode interface{}
|
||||
getOutKind reflect.Kind
|
||||
outError error
|
||||
// JSONSetable is an interface for structs to implement when they need to customize the
|
||||
// json pointer process
|
||||
type JSONSetable interface {
|
||||
JSONSet(string, interface{}) error
|
||||
}
|
||||
|
||||
// New creates a new json pointer for the given string
|
||||
@@ -100,15 +95,25 @@ func (p *Pointer) Get(document interface{}) (interface{}, reflect.Kind, error) {
|
||||
return p.get(document, swag.DefaultJSONNameProvider)
|
||||
}
|
||||
|
||||
// Set uses the pointer to set a value from a JSON document
|
||||
func (p *Pointer) Set(document interface{}, value interface{}) (interface{}, error) {
|
||||
return document, p.set(document, value, swag.DefaultJSONNameProvider)
|
||||
}
|
||||
|
||||
// GetForToken gets a value for a json pointer token 1 level deep
|
||||
func GetForToken(document interface{}, decodedToken string) (interface{}, reflect.Kind, error) {
|
||||
return getSingleImpl(document, decodedToken, swag.DefaultJSONNameProvider)
|
||||
}
|
||||
|
||||
// SetForToken gets a value for a json pointer token 1 level deep
|
||||
func SetForToken(document interface{}, decodedToken string, value interface{}) (interface{}, error) {
|
||||
return document, setSingleImpl(document, value, decodedToken, swag.DefaultJSONNameProvider)
|
||||
}
|
||||
|
||||
func getSingleImpl(node interface{}, decodedToken string, nameProvider *swag.NameProvider) (interface{}, reflect.Kind, error) {
|
||||
kind := reflect.Invalid
|
||||
rValue := reflect.Indirect(reflect.ValueOf(node))
|
||||
kind = rValue.Kind()
|
||||
kind := rValue.Kind()
|
||||
|
||||
switch kind {
|
||||
|
||||
case reflect.Struct:
|
||||
@@ -129,6 +134,7 @@ func getSingleImpl(node interface{}, decodedToken string, nameProvider *swag.Nam
|
||||
case reflect.Map:
|
||||
kv := reflect.ValueOf(decodedToken)
|
||||
mv := rValue.MapIndex(kv)
|
||||
|
||||
if mv.IsValid() && !swag.IsZero(mv) {
|
||||
return mv.Interface(), kind, nil
|
||||
}
|
||||
@@ -141,7 +147,7 @@ func getSingleImpl(node interface{}, decodedToken string, nameProvider *swag.Nam
|
||||
}
|
||||
sLength := rValue.Len()
|
||||
if tokenIndex < 0 || tokenIndex >= sLength {
|
||||
return nil, kind, fmt.Errorf("index out of bounds array[0,%d] index '%d'", sLength, tokenIndex)
|
||||
return nil, kind, fmt.Errorf("index out of bounds array[0,%d] index '%d'", sLength-1, tokenIndex)
|
||||
}
|
||||
|
||||
elem := rValue.Index(tokenIndex)
|
||||
@@ -153,6 +159,57 @@ func getSingleImpl(node interface{}, decodedToken string, nameProvider *swag.Nam
|
||||
|
||||
}
|
||||
|
||||
func setSingleImpl(node, data interface{}, decodedToken string, nameProvider *swag.NameProvider) error {
|
||||
rValue := reflect.Indirect(reflect.ValueOf(node))
|
||||
switch rValue.Kind() {
|
||||
|
||||
case reflect.Struct:
|
||||
if ns, ok := node.(JSONSetable); ok { // pointer impl
|
||||
return ns.JSONSet(decodedToken, data)
|
||||
}
|
||||
|
||||
if rValue.Type().Implements(jsonSetableType) {
|
||||
return node.(JSONSetable).JSONSet(decodedToken, data)
|
||||
}
|
||||
|
||||
nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken)
|
||||
if !ok {
|
||||
return fmt.Errorf("object has no field %q", decodedToken)
|
||||
}
|
||||
fld := rValue.FieldByName(nm)
|
||||
if fld.IsValid() {
|
||||
fld.Set(reflect.ValueOf(data))
|
||||
}
|
||||
return nil
|
||||
|
||||
case reflect.Map:
|
||||
kv := reflect.ValueOf(decodedToken)
|
||||
rValue.SetMapIndex(kv, reflect.ValueOf(data))
|
||||
return nil
|
||||
|
||||
case reflect.Slice:
|
||||
tokenIndex, err := strconv.Atoi(decodedToken)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sLength := rValue.Len()
|
||||
if tokenIndex < 0 || tokenIndex >= sLength {
|
||||
return fmt.Errorf("index out of bounds array[0,%d] index '%d'", sLength, tokenIndex)
|
||||
}
|
||||
|
||||
elem := rValue.Index(tokenIndex)
|
||||
if !elem.CanSet() {
|
||||
return fmt.Errorf("can't set slice index %s to %v", decodedToken, data)
|
||||
}
|
||||
elem.Set(reflect.ValueOf(data))
|
||||
return nil
|
||||
|
||||
default:
|
||||
return fmt.Errorf("invalid token reference %q", decodedToken)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (p *Pointer) get(node interface{}, nameProvider *swag.NameProvider) (interface{}, reflect.Kind, error) {
|
||||
|
||||
if nameProvider == nil {
|
||||
@@ -184,6 +241,101 @@ func (p *Pointer) get(node interface{}, nameProvider *swag.NameProvider) (interf
|
||||
return node, kind, nil
|
||||
}
|
||||
|
||||
func (p *Pointer) set(node, data interface{}, nameProvider *swag.NameProvider) error {
|
||||
knd := reflect.ValueOf(node).Kind()
|
||||
|
||||
if knd != reflect.Ptr && knd != reflect.Struct && knd != reflect.Map && knd != reflect.Slice && knd != reflect.Array {
|
||||
return fmt.Errorf("only structs, pointers, maps and slices are supported for setting values")
|
||||
}
|
||||
|
||||
if nameProvider == nil {
|
||||
nameProvider = swag.DefaultJSONNameProvider
|
||||
}
|
||||
|
||||
// Full document when empty
|
||||
if len(p.referenceTokens) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
lastI := len(p.referenceTokens) - 1
|
||||
for i, token := range p.referenceTokens {
|
||||
isLastToken := i == lastI
|
||||
decodedToken := Unescape(token)
|
||||
|
||||
if isLastToken {
|
||||
|
||||
return setSingleImpl(node, data, decodedToken, nameProvider)
|
||||
}
|
||||
|
||||
rValue := reflect.Indirect(reflect.ValueOf(node))
|
||||
kind := rValue.Kind()
|
||||
|
||||
switch kind {
|
||||
|
||||
case reflect.Struct:
|
||||
if rValue.Type().Implements(jsonPointableType) {
|
||||
r, err := node.(JSONPointable).JSONLookup(decodedToken)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fld := reflect.ValueOf(r)
|
||||
if fld.CanAddr() && fld.Kind() != reflect.Interface && fld.Kind() != reflect.Map && fld.Kind() != reflect.Slice && fld.Kind() != reflect.Ptr {
|
||||
node = fld.Addr().Interface()
|
||||
continue
|
||||
}
|
||||
node = r
|
||||
continue
|
||||
}
|
||||
nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken)
|
||||
if !ok {
|
||||
return fmt.Errorf("object has no field %q", decodedToken)
|
||||
}
|
||||
fld := rValue.FieldByName(nm)
|
||||
if fld.CanAddr() && fld.Kind() != reflect.Interface && fld.Kind() != reflect.Map && fld.Kind() != reflect.Slice && fld.Kind() != reflect.Ptr {
|
||||
node = fld.Addr().Interface()
|
||||
continue
|
||||
}
|
||||
node = fld.Interface()
|
||||
|
||||
case reflect.Map:
|
||||
kv := reflect.ValueOf(decodedToken)
|
||||
mv := rValue.MapIndex(kv)
|
||||
|
||||
if !mv.IsValid() {
|
||||
return fmt.Errorf("object has no key %q", decodedToken)
|
||||
}
|
||||
if mv.CanAddr() && mv.Kind() != reflect.Interface && mv.Kind() != reflect.Map && mv.Kind() != reflect.Slice && mv.Kind() != reflect.Ptr {
|
||||
node = mv.Addr().Interface()
|
||||
continue
|
||||
}
|
||||
node = mv.Interface()
|
||||
|
||||
case reflect.Slice:
|
||||
tokenIndex, err := strconv.Atoi(decodedToken)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sLength := rValue.Len()
|
||||
if tokenIndex < 0 || tokenIndex >= sLength {
|
||||
return fmt.Errorf("index out of bounds array[0,%d] index '%d'", sLength, tokenIndex)
|
||||
}
|
||||
|
||||
elem := rValue.Index(tokenIndex)
|
||||
if elem.CanAddr() && elem.Kind() != reflect.Interface && elem.Kind() != reflect.Map && elem.Kind() != reflect.Slice && elem.Kind() != reflect.Ptr {
|
||||
node = elem.Addr().Interface()
|
||||
continue
|
||||
}
|
||||
node = elem.Interface()
|
||||
|
||||
default:
|
||||
return fmt.Errorf("invalid token reference %q", decodedToken)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DecodedTokens returns the decoded tokens
|
||||
func (p *Pointer) DecodedTokens() []string {
|
||||
result := make([]string, 0, len(p.referenceTokens))
|
||||
|
||||
Generated
Vendored
-1
@@ -1 +0,0 @@
|
||||
eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.Xe40Wx6g5Y-iN0JVMhKyFfubtOId3zAVE564szw_yYGzFNhc_cGZO9F3BtAcJ55CfHG9C_ozn9dpnUDl_zYZoy_6cPCq13Ekb95z8NAC3ekDtbAATsc9HZwRNwI7UfkhstdwxljEouGB01qoLcUn6lFutrou-Ho21COHeDb2caemnPSA-rEAnXkOiBFu0RQ1MIwMygzvHXIHHYNpNwAtXqmiggM10miSjqBM3JmRPxCi7VK6_Rxij5p6LlhmK1BDi8Y6oBh-9BX3--5GAJeWZ6Vof5TnP-Enioia18j8c8KFtfY4q0y6Ednjb-AarLZ12gj695ppkBNJUdTJQmwGwA.fVcz_RiLrUB5fgMS.rjWllDYC6m_NB-ket_LizNEy9mlJ27odBTZQcMKaUqqXZBtWUCmPrOoMXGq-_cc-c7chg7D-WMh9SPQ23pV0P-DY-jsDpbOqHG2STOMEfW9ZREoaOLJXQaWcuBldLjRyWFcq0HGj97LgE6szD1Zlou3bmdHS_Q-U9Up9YQ_8_YnDcESD_cj1w5FZom7HjchKJFeGjQjfDQpoCKCQNMJaavUqy9jHQEeQ_uVocSrETg3GpewDcUF2tuv8uGq7ZZWu7Vl8zmnY1MFTynaGBWzTCSRmCkAXjcsaUheDP_NT5D7k-xUS6LwtqEUiXAXV07SNFraorFj5lnBQZRDlZMYcA3NWR6zHiOxekR9LBYPofst6w1rIqUchj_5m1tDpVTBMPir1eAaFcnJtPgo4ch17OF-kmcmQGLhJI3U7n8wv4sTrmP1dewtRRKrvlJe5r3_6eDiK4xZ8K0rnK1D4g6zuQqU1gA8KaU7pmZkKpFx3Bew4v-6DH32YwQBvAI7Lbb8afou9WsCNB_iswz5XGimP4bifiJRwpWBEz9VGhZFdiw-hZpYWgbxzVb5gtqfTDLIvpbLDmFz1vge16uUQHHVFpo1pSozyr7A60X8qsh9pmmO3RcJ-ZGZBWqiRC-Kl5ejz7WQ.LFoK4Ibi11B2lWQ5WcPSag
|
||||
Generated
Vendored
-33
@@ -1,33 +0,0 @@
|
||||
clone:
|
||||
path: github.com/go-openapi/jsonreference
|
||||
|
||||
matrix:
|
||||
GO_VERSION:
|
||||
- "1.6"
|
||||
|
||||
build:
|
||||
integration:
|
||||
image: golang:$$GO_VERSION
|
||||
pull: true
|
||||
commands:
|
||||
- go get -u github.com/stretchr/testify/assert
|
||||
- go get -u github.com/PuerkitoBio/purell
|
||||
- go get -u github.com/go-openapi/jsonpointer
|
||||
- go test -race
|
||||
- go test -v -cover -coverprofile=coverage.out -covermode=count ./...
|
||||
|
||||
notify:
|
||||
slack:
|
||||
channel: bots
|
||||
webhook_url: $$SLACK_URL
|
||||
username: drone
|
||||
|
||||
publish:
|
||||
coverage:
|
||||
server: https://coverage.vmware.run
|
||||
token: $$GITHUB_TOKEN
|
||||
# threshold: 70
|
||||
# must_increase: true
|
||||
when:
|
||||
matrix:
|
||||
GO_VERSION: "1.6"
|
||||
Generated
Vendored
-13
@@ -1,13 +0,0 @@
|
||||
approve_by_comment: true
|
||||
approve_regex: '^(:shipit:|:\+1:|\+1|LGTM|lgtm|Approved)'
|
||||
reject_regex: ^[Rr]ejected
|
||||
reset_on_push: false
|
||||
reviewers:
|
||||
members:
|
||||
- casualjim
|
||||
- chancez
|
||||
- frapposelli
|
||||
- vburenin
|
||||
- pytlesk4
|
||||
name: pullapprove
|
||||
required: 1
|
||||
Generated
Vendored
+16
@@ -0,0 +1,16 @@
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
go:
|
||||
- '1.9'
|
||||
- 1.10.x
|
||||
- 1.11.x
|
||||
install:
|
||||
- go get -u github.com/stretchr/testify/assert
|
||||
- go get -u github.com/PuerkitoBio/purell
|
||||
- go get -u github.com/go-openapi/jsonpointer
|
||||
language: go
|
||||
notifications:
|
||||
slack:
|
||||
secure: OpQG/36F7DSF00HLm9WZMhyqFCYYyYTsVDObW226cWiR8PWYiNfLZiSEvIzT1Gx4dDjhigKTIqcLhG34CkL5iNXDjm9Yyo2RYhQPlK8NErNqUEXuBqn4RqYHW48VGhEhOyDd4Ei0E2FN5ZbgpvHgtpkdZ6XDi64r3Ac89isP9aPHXQTuv2Jog6b4/OKKiUTftLcTIst0p4Cp3gqOJWf1wnoj+IadWiECNVQT6zb47IYjtyw6+uV8iUjTzdKcRB6Zc6b4Dq7JAg1Zd7Jfxkql3hlKp4PNlRf9Cy7y5iA3G7MLyg3FcPX5z2kmcyPt2jOTRMBWUJ5zIQpOxizAcN8WsT3WWBL5KbuYK6k0PzujrIDLqdxGpNmjkkMfDBT9cKmZpm2FdW+oZgPFJP+oKmAo4u4KJz/vjiPTXgQlN5bmrLuRMCp+AwC5wkIohTqWZVPE2TK6ZSnMYcg/W39s+RP/9mJoyryAvPSpBOLTI+biCgaUCTOAZxNTWpMFc3tPYntc41WWkdKcooZ9JA5DwfcaVFyTGQ3YXz+HvX6G1z/gW0Q/A4dBi9mj2iE1xm7tRTT+4VQ2AXFvSEI1HJpfPgYnwAtwOD1v3Qm2EUHk9sCdtEDR4wVGEPIVn44GnwFMnGKx9JWppMPYwFu3SVDdHt+E+LOlhZUply11Aa+IVrT2KUQ=
|
||||
script:
|
||||
- go test -v -race -cover -coverprofile=coverage.txt -covermode=atomic ./...
|
||||
Generated
Vendored
+2
-2
@@ -1,4 +1,4 @@
|
||||
# gojsonreference [](https://ci.vmware.run/go-openapi/jsonreference) [](https://coverage.vmware.run/go-openapi/jsonreference) [](https://slackin.goswagger.io)
|
||||
# gojsonreference [](https://travis-ci.org/go-openapi/jsonreference) [](https://codecov.io/gh/go-openapi/jsonreference) [](https://slackin.goswagger.io)
|
||||
|
||||
[](https://raw.githubusercontent.com/go-openapi/jsonreference/master/LICENSE) [](http://godoc.org/github.com/go-openapi/jsonreference)
|
||||
An implementation of JSON Reference - Go language
|
||||
@@ -7,7 +7,7 @@ An implementation of JSON Reference - Go language
|
||||
Work in progress ( 90% done )
|
||||
|
||||
## Dependencies
|
||||
https://github.com/xeipuuv/gojsonpointer
|
||||
https://github.com/go-openapi/jsonpointer
|
||||
|
||||
## References
|
||||
http://tools.ietf.org/html/draft-ietf-appsawg-json-pointer-07
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
module github.com/go-openapi/jsonreference
|
||||
|
||||
require (
|
||||
github.com/PuerkitoBio/purell v1.1.0
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.17.0
|
||||
github.com/go-openapi/swag v0.17.0 // indirect
|
||||
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/stretchr/testify v1.2.2
|
||||
golang.org/x/net v0.0.0-20181005035420-146acd28ed58 // indirect
|
||||
golang.org/x/text v0.3.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.2.1 // indirect
|
||||
)
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
github.com/PuerkitoBio/purell v1.1.0 h1:rmGxhojJlM0tuKtfdvliR84CFHljx9ag64t2xmVkjK4=
|
||||
github.com/PuerkitoBio/purell v1.1.0/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/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.17.0 h1:Bpl2DtZ6k7wKqfFs7e+4P08+M9I3FQgn09a1UsRUQbk=
|
||||
github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
|
||||
github.com/go-openapi/swag v0.17.0 h1:7wu+dZ5k83kvUWeAb+WUkFiUhDzwGqzTR/NhWzeo1JU=
|
||||
github.com/go-openapi/swag v0.17.0/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
|
||||
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 h1:2gxZ0XQIU/5z3Z3bUBu+FXuk2pFbkN6tcwi/pjyaDic=
|
||||
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
golang.org/x/net v0.0.0-20181005035420-146acd28ed58 h1:otZG8yDCO4LVps5+9bxOeNiCvgmOyt96J3roHTYs7oE=
|
||||
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
linters-settings:
|
||||
govet:
|
||||
check-shadowing: true
|
||||
golint:
|
||||
min-confidence: 0
|
||||
gocyclo:
|
||||
min-complexity: 25
|
||||
maligned:
|
||||
suggest-new: true
|
||||
dupl:
|
||||
threshold: 100
|
||||
goconst:
|
||||
min-len: 2
|
||||
min-occurrences: 2
|
||||
|
||||
linters:
|
||||
enable-all: true
|
||||
disable:
|
||||
- maligned
|
||||
- unparam
|
||||
- lll
|
||||
+8
-6
@@ -1,16 +1,18 @@
|
||||
language: go
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
go:
|
||||
- 1.7
|
||||
- '1.9'
|
||||
- 1.10.x
|
||||
- 1.11.x
|
||||
install:
|
||||
- go get -u github.com/stretchr/testify
|
||||
- go get -u github.com/go-openapi/swag
|
||||
- go get -u gopkg.in/yaml.v2
|
||||
- go get -u github.com/go-openapi/jsonpointer
|
||||
- go get -u github.com/go-openapi/jsonreference
|
||||
script:
|
||||
- go test -v -race -cover -coverprofile=coverage.txt -covermode=atomic ./...
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
language: go
|
||||
notifications:
|
||||
slack:
|
||||
secure: QUWvCkBBK09GF7YtEvHHVt70JOkdlNBG0nIKu/5qc4/nW5HP8I2w0SEf/XR2je0eED1Qe3L/AfMCWwrEj+IUZc3l4v+ju8X8R3Lomhme0Eb0jd1MTMCuPcBT47YCj0M7RON7vXtbFfm1hFJ/jLe5+9FXz0hpXsR24PJc5ZIi/ogNwkaPqG4BmndzecpSh0vc2FJPZUD9LT0I09REY/vXR0oQAalLkW0asGD5taHZTUZq/kBpsNxaAFrLM23i4mUcf33M5fjLpvx5LRICrX/57XpBrDh2TooBU6Qj3CgoY0uPRYUmSNxbVx1czNzl2JtEpb5yjoxfVPQeg0BvQM00G8LJINISR+ohrjhkZmAqchDupAX+yFrxTtORa78CtnIL6z/aTNlgwwVD8kvL/1pFA/JWYmKDmz93mV/+6wubGzNSQCstzjkFA4/iZEKewKUoRIAi/fxyscP6L/rCpmY/4llZZvrnyTqVbt6URWpopUpH4rwYqreXAtJxJsfBJIeSmUIiDIOMGkCTvyTEW3fWGmGoqWtSHLoaWDyAIGb7azb+KvfpWtEcoPFWfSWU+LGee0A/YsUhBl7ADB9A0CJEuR8q4BPpKpfLwPKSiKSAXL7zDkyjExyhtgqbSl2jS+rKIHOZNL8JkCcTP2MKMVd563C5rC5FMKqu3S9m2b6380E=
|
||||
script:
|
||||
- go test -v -race -cover -coverprofile=coverage.txt -covermode=atomic ./...
|
||||
|
||||
+7
-2
@@ -1,5 +1,10 @@
|
||||
# OAI object model [](https://travis-ci.org/go-openapi/spec) [](https://codecov.io/gh/go-openapi/spec) [](https://slackin.goswagger.io)
|
||||
|
||||
[](https://raw.githubusercontent.com/go-openapi/spec/master/LICENSE) [](http://godoc.org/github.com/go-openapi/spec)
|
||||
[](https://raw.githubusercontent.com/go-openapi/spec/master/LICENSE)
|
||||
[](http://godoc.org/github.com/go-openapi/spec)
|
||||
[](https://golangci.com)
|
||||
[](https://goreportcard.com/report/github.com/go-openapi/spec)
|
||||
|
||||
The object model for OpenAPI specification documents
|
||||
The object model for OpenAPI specification documents.
|
||||
|
||||
Currently supports Swagger 2.0.
|
||||
|
||||
+6
-6
File diff suppressed because one or more lines are too long
+47
@@ -0,0 +1,47 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// 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 spec
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
var (
|
||||
// Debug is true when the SWAGGER_DEBUG env var is not empty.
|
||||
// It enables a more verbose logging of validators.
|
||||
Debug = os.Getenv("SWAGGER_DEBUG") != ""
|
||||
// validateLogger is a debug logger for this package
|
||||
specLogger *log.Logger
|
||||
)
|
||||
|
||||
func init() {
|
||||
debugOptions()
|
||||
}
|
||||
|
||||
func debugOptions() {
|
||||
specLogger = log.New(os.Stdout, "spec:", log.LstdFlags)
|
||||
}
|
||||
|
||||
func debugLog(msg string, args ...interface{}) {
|
||||
// A private, trivial trace logger, based on go-openapi/spec/expander.go:debugLog()
|
||||
if Debug {
|
||||
_, file1, pos1, _ := runtime.Caller(1)
|
||||
specLogger.Printf("%s:%d: %s", filepath.Base(file1), pos1, fmt.Sprintf(msg, args...))
|
||||
}
|
||||
}
|
||||
+314
-79
@@ -30,16 +30,12 @@ import (
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
var (
|
||||
// Debug enables logging when SWAGGER_DEBUG env var is not empty
|
||||
Debug = os.Getenv("SWAGGER_DEBUG") != ""
|
||||
)
|
||||
|
||||
// ExpandOptions provides options for expand.
|
||||
type ExpandOptions struct {
|
||||
RelativeBase string
|
||||
SkipSchemas bool
|
||||
ContinueOnError bool
|
||||
RelativeBase string
|
||||
SkipSchemas bool
|
||||
ContinueOnError bool
|
||||
AbsoluteCircularRef bool
|
||||
}
|
||||
|
||||
// ResolutionCache a cache for resolving urls
|
||||
@@ -49,7 +45,7 @@ type ResolutionCache interface {
|
||||
}
|
||||
|
||||
type simpleCache struct {
|
||||
lock sync.Mutex
|
||||
lock sync.RWMutex
|
||||
store map[string]interface{}
|
||||
}
|
||||
|
||||
@@ -59,6 +55,7 @@ func init() {
|
||||
resCache = initResolutionCache()
|
||||
}
|
||||
|
||||
// initResolutionCache initializes the URI resolution cache
|
||||
func initResolutionCache() ResolutionCache {
|
||||
return &simpleCache{store: map[string]interface{}{
|
||||
"http://swagger.io/v2/schema.json": MustLoadSwagger20Schema(),
|
||||
@@ -66,16 +63,37 @@ func initResolutionCache() ResolutionCache {
|
||||
}}
|
||||
}
|
||||
|
||||
// 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.
|
||||
// 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
|
||||
}
|
||||
|
||||
func newResolverContext(originalBasePath string) *resolverContext {
|
||||
return &resolverContext{
|
||||
circulars: make(map[string]bool),
|
||||
basePath: originalBasePath, // keep the root base path in context
|
||||
}
|
||||
}
|
||||
|
||||
// Get retrieves a cached URI
|
||||
func (s *simpleCache) Get(uri string) (interface{}, bool) {
|
||||
debugLog("getting %q from resolution cache", uri)
|
||||
s.lock.Lock()
|
||||
s.lock.RLock()
|
||||
v, ok := s.store[uri]
|
||||
debugLog("got %q from resolution cache: %t", uri, ok)
|
||||
|
||||
s.lock.Unlock()
|
||||
s.lock.RUnlock()
|
||||
return v, ok
|
||||
}
|
||||
|
||||
// Set caches a URI
|
||||
func (s *simpleCache) Set(uri string, data interface{}) {
|
||||
s.lock.Lock()
|
||||
s.store[uri] = data
|
||||
@@ -84,7 +102,7 @@ func (s *simpleCache) Set(uri string, data interface{}) {
|
||||
|
||||
// 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)
|
||||
resolver, err := defaultSchemaLoader(root, opts, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -116,21 +134,21 @@ func ResolveRef(root interface{}, ref *Ref) (*Schema, error) {
|
||||
case map[string]interface{}:
|
||||
b, _ := json.Marshal(sch)
|
||||
newSch := new(Schema)
|
||||
json.Unmarshal(b, newSch)
|
||||
_ = json.Unmarshal(b, newSch)
|
||||
return newSch, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown type for the resolved reference")
|
||||
}
|
||||
}
|
||||
|
||||
// ResolveParameter resolves a paramter reference against a context root
|
||||
// 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 paramter reference against a context root and base path
|
||||
// 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)
|
||||
resolver, err := defaultSchemaLoader(root, opts, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -149,7 +167,7 @@ func ResolveResponse(root interface{}, ref Ref) (*Response, error) {
|
||||
|
||||
// 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)
|
||||
resolver, err := defaultSchemaLoader(root, opts, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -163,7 +181,7 @@ func ResolveResponseWithBase(root interface{}, ref Ref, opts *ExpandOptions) (*R
|
||||
|
||||
// ResolveItems resolves header and parameter items reference against a context root and base path
|
||||
func ResolveItems(root interface{}, ref Ref, opts *ExpandOptions) (*Items, error) {
|
||||
resolver, err := defaultSchemaLoader(root, opts, nil)
|
||||
resolver, err := defaultSchemaLoader(root, opts, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -180,7 +198,7 @@ func ResolveItems(root interface{}, ref Ref, opts *ExpandOptions) (*Items, error
|
||||
|
||||
// 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)
|
||||
resolver, err := defaultSchemaLoader(root, opts, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -199,6 +217,7 @@ type schemaLoader struct {
|
||||
root interface{}
|
||||
options *ExpandOptions
|
||||
cache ResolutionCache
|
||||
context *resolverContext
|
||||
loadDoc func(string) (json.RawMessage, error)
|
||||
}
|
||||
|
||||
@@ -221,7 +240,8 @@ func init() {
|
||||
func defaultSchemaLoader(
|
||||
root interface{},
|
||||
expandOptions *ExpandOptions,
|
||||
cache ResolutionCache) (*schemaLoader, error) {
|
||||
cache ResolutionCache,
|
||||
context *resolverContext) (*schemaLoader, error) {
|
||||
|
||||
if cache == nil {
|
||||
cache = resCache
|
||||
@@ -229,11 +249,15 @@ func defaultSchemaLoader(
|
||||
if expandOptions == nil {
|
||||
expandOptions = &ExpandOptions{}
|
||||
}
|
||||
|
||||
absBase, _ := absPath(expandOptions.RelativeBase)
|
||||
if context == nil {
|
||||
context = newResolverContext(absBase)
|
||||
}
|
||||
return &schemaLoader{
|
||||
root: root,
|
||||
options: expandOptions,
|
||||
cache: cache,
|
||||
context: context,
|
||||
loadDoc: func(path string) (json.RawMessage, error) {
|
||||
debugLog("fetching document at %q", path)
|
||||
return PathLoader(path)
|
||||
@@ -312,12 +336,6 @@ func nextRef(startingNode interface{}, startingRef *Ref, ptr *jsonpointer.Pointe
|
||||
return ret
|
||||
}
|
||||
|
||||
func debugLog(msg string, args ...interface{}) {
|
||||
if Debug {
|
||||
log.Printf(msg, args...)
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
@@ -336,12 +354,17 @@ func normalizeAbsPath(path string) string {
|
||||
// base could be a directory or a full file path
|
||||
func normalizePaths(refPath, base string) string {
|
||||
refURL, _ := url.Parse(refPath)
|
||||
if path.IsAbs(refURL.Path) {
|
||||
if path.IsAbs(refURL.Path) || filepath.IsAbs(refPath) {
|
||||
// refPath is actually absolute
|
||||
if refURL.Host != "" {
|
||||
return refPath
|
||||
}
|
||||
return filepath.FromSlash(refPath)
|
||||
parts := strings.Split(refPath, "#")
|
||||
result := filepath.FromSlash(parts[0])
|
||||
if len(parts) == 2 {
|
||||
result += "#" + parts[1]
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// relative refPath
|
||||
@@ -361,6 +384,59 @@ func normalizePaths(refPath, base string) string {
|
||||
return baseURL.String()
|
||||
}
|
||||
|
||||
// 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())
|
||||
|
||||
if ref.String() == "" || ref.IsRoot() || ref.HasFragmentOnly {
|
||||
return ref
|
||||
}
|
||||
// strip relativeBase from URI
|
||||
relativeBaseURL, _ := url.Parse(relativeBase)
|
||||
relativeBaseURL.Fragment = ""
|
||||
|
||||
if relativeBaseURL.IsAbs() && 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() {
|
||||
// other absolute URL get unchanged (i.e. with a non-empty scheme)
|
||||
return ref
|
||||
}
|
||||
|
||||
// for relative file URIs:
|
||||
originalRelativeBaseURL, _ := url.Parse(originalRelativeBase)
|
||||
originalRelativeBaseURL.Fragment = ""
|
||||
if strings.HasPrefix(ref.String(), originalRelativeBaseURL.String()) {
|
||||
// the resulting ref is in the expanded spec: return a local ref
|
||||
r, _ := NewRef(strings.TrimPrefix(ref.String(), originalRelativeBaseURL.String()))
|
||||
return &r
|
||||
}
|
||||
|
||||
// check if we may set a relative path, considering the original base path for this spec.
|
||||
// Example:
|
||||
// spec is located at /mypath/spec.json
|
||||
// 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])
|
||||
if err != nil {
|
||||
// there is no common ancestor (e.g. different drives on windows)
|
||||
// leaves the ref unchanged
|
||||
return ref
|
||||
}
|
||||
if len(parts) == 2 {
|
||||
relativePath += "#" + parts[1]
|
||||
}
|
||||
r, _ := NewRef(relativePath)
|
||||
return &r
|
||||
}
|
||||
|
||||
// relativeBase could be an ABSOLUTE file path or an ABSOLUTE URL
|
||||
func normalizeFileRef(ref *Ref, relativeBase string) *Ref {
|
||||
// This is important for when the reference is pointing to the root schema
|
||||
@@ -369,8 +445,7 @@ func normalizeFileRef(ref *Ref, relativeBase string) *Ref {
|
||||
return &r
|
||||
}
|
||||
|
||||
refURL := ref.GetURL()
|
||||
debugLog("normalizing %s against %s (%s)", ref.String(), relativeBase, refURL.String())
|
||||
debugLog("normalizing %s against %s", ref.String(), relativeBase)
|
||||
|
||||
s := normalizePaths(ref.String(), relativeBase)
|
||||
r, _ := NewRef(s)
|
||||
@@ -395,7 +470,7 @@ func (r *schemaLoader) resolveRef(ref *Ref, target interface{}, basePath string)
|
||||
// it is pointing somewhere in the root.
|
||||
root := r.root
|
||||
if (ref.IsRoot() || ref.HasFragmentOnly) && root == nil && basePath != "" {
|
||||
if baseRef, err := NewRef(basePath); err == nil {
|
||||
if baseRef, erb := NewRef(basePath); erb == nil {
|
||||
root, _, _, _ = r.load(baseRef.GetURL())
|
||||
}
|
||||
}
|
||||
@@ -430,9 +505,11 @@ func (r *schemaLoader) load(refURL *url.URL) (interface{}, url.URL, bool, error)
|
||||
toFetch := *refURL
|
||||
toFetch.Fragment = ""
|
||||
|
||||
data, fromCache := r.cache.Get(toFetch.String())
|
||||
normalized := normalizeAbsPath(toFetch.String())
|
||||
|
||||
data, fromCache := r.cache.Get(normalized)
|
||||
if !fromCache {
|
||||
b, err := r.loadDoc(toFetch.String())
|
||||
b, err := r.loadDoc(normalized)
|
||||
if err != nil {
|
||||
return nil, url.URL{}, false, err
|
||||
}
|
||||
@@ -440,7 +517,7 @@ func (r *schemaLoader) load(refURL *url.URL) (interface{}, url.URL, bool, error)
|
||||
if err := json.Unmarshal(b, &data); err != nil {
|
||||
return nil, url.URL{}, false, err
|
||||
}
|
||||
r.cache.Set(toFetch.String(), data)
|
||||
r.cache.Set(normalized, data)
|
||||
}
|
||||
|
||||
return data, toFetch, fromCache, nil
|
||||
@@ -468,7 +545,7 @@ func absPath(fname string) (string, error) {
|
||||
|
||||
// ExpandSpec expands the references in a swagger spec
|
||||
func ExpandSpec(spec *Swagger, options *ExpandOptions) error {
|
||||
resolver, err := defaultSchemaLoader(spec, options, nil)
|
||||
resolver, err := defaultSchemaLoader(spec, options, nil, nil)
|
||||
// Just in case this ever returns an error.
|
||||
if shouldStopOnError(err, resolver.options) {
|
||||
return err
|
||||
@@ -484,7 +561,7 @@ func ExpandSpec(spec *Swagger, options *ExpandOptions) error {
|
||||
for key, definition := range spec.Definitions {
|
||||
var def *Schema
|
||||
var err error
|
||||
if def, err = expandSchema(definition, []string{fmt.Sprintf("#/defintions/%s", key)}, resolver, specBasePath); shouldStopOnError(err, resolver.options) {
|
||||
if def, err = expandSchema(definition, []string{fmt.Sprintf("#/definitions/%s", key)}, resolver, specBasePath); shouldStopOnError(err, resolver.options) {
|
||||
return err
|
||||
}
|
||||
if def != nil {
|
||||
@@ -531,25 +608,35 @@ func shouldStopOnError(err error, opts *ExpandOptions) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// 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 scema in a different file other than root
|
||||
func ExpandSchema(schema *Schema, root interface{}, cache ResolutionCache) error {
|
||||
// Only save the root to a tmp file if it isn't nil.
|
||||
var base string
|
||||
// baseForRoot loads in the cache the root document and produces a fake "root" base path entry
|
||||
// for further $ref resolution
|
||||
func baseForRoot(root interface{}, cache ResolutionCache) string {
|
||||
// cache the root document to resolve $ref's
|
||||
const rootBase = "root"
|
||||
if root != nil {
|
||||
base, _ = absPath("root")
|
||||
base, _ := absPath(rootBase)
|
||||
normalizedBase := normalizeAbsPath(base)
|
||||
debugLog("setting root doc in cache at: %s", normalizedBase)
|
||||
if cache == nil {
|
||||
cache = resCache
|
||||
}
|
||||
cache.Set(normalizeAbsPath(base), root)
|
||||
base = "root"
|
||||
cache.Set(normalizedBase, root)
|
||||
return rootBase
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// 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
|
||||
func ExpandSchema(schema *Schema, root interface{}, cache ResolutionCache) error {
|
||||
opts := &ExpandOptions{
|
||||
RelativeBase: base,
|
||||
// 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)
|
||||
}
|
||||
@@ -565,7 +652,7 @@ func ExpandSchemaWithBasePath(schema *Schema, cache ResolutionCache, opts *Expan
|
||||
basePath, _ = absPath(opts.RelativeBase)
|
||||
}
|
||||
|
||||
resolver, err := defaultSchemaLoader(nil, opts, cache)
|
||||
resolver, err := defaultSchemaLoader(nil, opts, cache, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -617,8 +704,32 @@ func basePathFromSchemaID(oldBasePath, id string) string {
|
||||
return u.String()
|
||||
}
|
||||
|
||||
func isCircular(ref *Ref, basePath string, parentRefs ...string) bool {
|
||||
return basePath != "" && swag.ContainsStringsCI(parentRefs, ref.String())
|
||||
// isCircular detects cycles in sequences of $ref.
|
||||
// It relies on a private context (which needs not be locked).
|
||||
func (r *schemaLoader) isCircular(ref *Ref, basePath string, parentRefs ...string) (foundCycle bool) {
|
||||
normalizedRef := normalizePaths(ref.String(), basePath)
|
||||
if _, ok := r.context.circulars[normalizedRef]; ok {
|
||||
// circular $ref has been already detected in another explored cycle
|
||||
foundCycle = true
|
||||
return
|
||||
}
|
||||
foundCycle = swag.ContainsStringsCI(parentRefs, normalizedRef)
|
||||
if foundCycle {
|
||||
r.context.circulars[normalizedRef] = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func updateBasePath(transitive *schemaLoader, resolver *schemaLoader, basePath string) string {
|
||||
if transitive != resolver {
|
||||
debugLog("got a new resolver")
|
||||
if transitive.options != nil && transitive.options.RelativeBase != "" {
|
||||
basePath, _ = absPath(transitive.options.RelativeBase)
|
||||
debugLog("new basePath = %s", basePath)
|
||||
}
|
||||
}
|
||||
|
||||
return basePath
|
||||
}
|
||||
|
||||
func expandSchema(target Schema, parentRefs []string, resolver *schemaLoader, basePath string) (*Schema, error) {
|
||||
@@ -634,6 +745,7 @@ func expandSchema(target Schema, parentRefs []string, resolver *schemaLoader, ba
|
||||
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
|
||||
@@ -645,7 +757,6 @@ func expandSchema(target Schema, parentRefs []string, resolver *schemaLoader, ba
|
||||
}
|
||||
|
||||
/* Explain here what this function does */
|
||||
|
||||
var t *Schema
|
||||
/* if Ref is found, everything else doesn't matter */
|
||||
/* Ref also changes the resolution scope of children expandSchema */
|
||||
@@ -654,14 +765,21 @@ func expandSchema(target Schema, parentRefs []string, resolver *schemaLoader, ba
|
||||
normalizedRef := normalizeFileRef(&target.Ref, basePath)
|
||||
normalizedBasePath := normalizedRef.RemoteURI()
|
||||
|
||||
/* this means there is a circle in the recursion tree */
|
||||
/* return the Ref */
|
||||
if isCircular(normalizedRef, basePath, parentRefs...) {
|
||||
target.Ref = *normalizedRef
|
||||
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
|
||||
}
|
||||
|
||||
debugLog("\nbasePath: %s", basePath)
|
||||
debugLog("basePath: %s", basePath)
|
||||
if Debug {
|
||||
b, _ := json.Marshal(target)
|
||||
debugLog("calling Resolve with target: %s", string(b))
|
||||
@@ -672,7 +790,15 @@ func expandSchema(target Schema, parentRefs []string, resolver *schemaLoader, ba
|
||||
|
||||
if t != nil {
|
||||
parentRefs = append(parentRefs, normalizedRef.String())
|
||||
return expandSchema(*t, parentRefs, resolver, normalizedBasePath)
|
||||
var err error
|
||||
transitiveResolver, err := transitiveResolver(basePath, target.Ref, resolver)
|
||||
if shouldStopOnError(err, resolver.options) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
basePath = updateBasePath(transitiveResolver, resolver, normalizedBasePath)
|
||||
|
||||
return expandSchema(*t, parentRefs, transitiveResolver, basePath)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -781,7 +907,7 @@ func derefPathItem(pathItem *PathItem, parentRefs []string, resolver *schemaLoad
|
||||
normalizedRef := normalizeFileRef(&pathItem.Ref, basePath)
|
||||
normalizedBasePath := normalizedRef.RemoteURI()
|
||||
|
||||
if isCircular(normalizedRef, basePath, parentRefs...) {
|
||||
if resolver.isCircular(normalizedRef, basePath, parentRefs...) {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -807,9 +933,17 @@ func expandPathItem(pathItem *PathItem, resolver *schemaLoader, basePath string)
|
||||
if err := derefPathItem(pathItem, parentRefs, resolver, basePath); shouldStopOnError(err, resolver.options) {
|
||||
return err
|
||||
}
|
||||
if pathItem.Ref.String() != "" {
|
||||
var err error
|
||||
resolver, err = transitiveResolver(basePath, pathItem.Ref, resolver)
|
||||
if shouldStopOnError(err, resolver.options) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
pathItem.Ref = Ref{}
|
||||
|
||||
parentRefs = parentRefs[0:]
|
||||
// Currently unused:
|
||||
//parentRefs = parentRefs[0:]
|
||||
|
||||
for idx := range pathItem.Parameters {
|
||||
if err := expandParameter(&(pathItem.Parameters[idx]), resolver, basePath); shouldStopOnError(err, resolver.options) {
|
||||
@@ -867,19 +1001,68 @@ func expandOperation(op *Operation, resolver *schemaLoader, basePath string) err
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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
|
||||
func ExpandResponse(response *Response, basePath string) error {
|
||||
opts := &ExpandOptions{
|
||||
RelativeBase: basePath,
|
||||
func transitiveResolver(basePath string, ref Ref, resolver *schemaLoader) (*schemaLoader, error) {
|
||||
if ref.IsRoot() || ref.HasFragmentOnly {
|
||||
return resolver, nil
|
||||
}
|
||||
resolver, err := defaultSchemaLoader(nil, opts, nil)
|
||||
|
||||
baseRef, _ := NewRef(basePath)
|
||||
currentRef := normalizeFileRef(&ref, basePath)
|
||||
// Set a new root to resolve against
|
||||
if !strings.HasPrefix(currentRef.String(), baseRef.String()) {
|
||||
rootURL := currentRef.GetURL()
|
||||
rootURL.Fragment = ""
|
||||
root, _ := resolver.cache.Get(rootURL.String())
|
||||
var err error
|
||||
|
||||
// shallow copy of resolver options to set a new RelativeBase when
|
||||
// traversing multiple documents
|
||||
newOptions := resolver.options
|
||||
newOptions.RelativeBase = rootURL.String()
|
||||
debugLog("setting new root: %s", newOptions.RelativeBase)
|
||||
resolver, err = defaultSchemaLoader(root, newOptions, resolver.cache, resolver.context)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return resolver, nil
|
||||
}
|
||||
|
||||
// ExpandResponseWithRoot expands a response based on a root document, not a fetchable document
|
||||
func ExpandResponseWithRoot(response *Response, root interface{}, cache ResolutionCache) error {
|
||||
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
|
||||
}
|
||||
|
||||
return expandResponse(response, resolver, basePath)
|
||||
return expandResponse(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
|
||||
func ExpandResponse(response *Response, basePath string) error {
|
||||
var specBasePath string
|
||||
if basePath != "" {
|
||||
specBasePath, _ = absPath(basePath)
|
||||
}
|
||||
opts := &ExpandOptions{
|
||||
RelativeBase: specBasePath,
|
||||
}
|
||||
resolver, err := defaultSchemaLoader(nil, opts, nil, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return expandResponse(response, resolver, opts.RelativeBase)
|
||||
}
|
||||
|
||||
func derefResponse(response *Response, parentRefs []string, resolver *schemaLoader, basePath string) error {
|
||||
@@ -889,7 +1072,7 @@ func derefResponse(response *Response, parentRefs []string, resolver *schemaLoad
|
||||
normalizedRef := normalizeFileRef(&response.Ref, basePath)
|
||||
normalizedBasePath := normalizedRef.RemoteURI()
|
||||
|
||||
if isCircular(normalizedRef, basePath, parentRefs...) {
|
||||
if resolver.isCircular(normalizedRef, basePath, parentRefs...) {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -910,16 +1093,31 @@ func expandResponse(response *Response, resolver *schemaLoader, basePath string)
|
||||
if response == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
parentRefs := []string{}
|
||||
if err := derefResponse(response, parentRefs, resolver, basePath); shouldStopOnError(err, resolver.options) {
|
||||
return err
|
||||
}
|
||||
if response.Ref.String() != "" {
|
||||
transitiveResolver, err := transitiveResolver(basePath, response.Ref, resolver)
|
||||
if shouldStopOnError(err, transitiveResolver.options) {
|
||||
return err
|
||||
}
|
||||
basePath = updateBasePath(transitiveResolver, resolver, basePath)
|
||||
resolver = transitiveResolver
|
||||
}
|
||||
if response.Schema != nil && response.Schema.Ref.String() != "" {
|
||||
// schema expanded to a $ref in another root
|
||||
var ern error
|
||||
response.Schema.Ref, ern = NewRef(normalizePaths(response.Schema.Ref.String(), response.Ref.RemoteURI()))
|
||||
if ern != nil {
|
||||
return ern
|
||||
}
|
||||
}
|
||||
response.Ref = Ref{}
|
||||
|
||||
parentRefs = parentRefs[0:]
|
||||
if !resolver.options.SkipSchemas && response.Schema != nil {
|
||||
parentRefs = append(parentRefs, response.Schema.Ref.String())
|
||||
// parentRefs = append(parentRefs, response.Schema.Ref.String())
|
||||
s, err := expandSchema(*response.Schema, parentRefs, resolver, basePath)
|
||||
if shouldStopOnError(err, resolver.options) {
|
||||
return err
|
||||
@@ -930,19 +1128,40 @@ func expandResponse(response *Response, resolver *schemaLoader, basePath string)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ExpandParameter expands a parameter based on a basepath
|
||||
// This is the exported version of expandParameter
|
||||
// all refs inside parameter will be resolved relative to basePath
|
||||
func ExpandParameter(parameter *Parameter, basePath string) error {
|
||||
// ExpandParameterWithRoot expands a parameter based on a root document, not a fetchable document
|
||||
func ExpandParameterWithRoot(parameter *Parameter, root interface{}, cache ResolutionCache) error {
|
||||
opts := &ExpandOptions{
|
||||
RelativeBase: basePath,
|
||||
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(nil, opts, nil)
|
||||
resolver, err := defaultSchemaLoader(root, opts, nil, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return expandParameter(parameter, resolver, basePath)
|
||||
return expandParameter(parameter, resolver, opts.RelativeBase)
|
||||
}
|
||||
|
||||
// ExpandParameter expands a parameter based on a basepath
|
||||
// This is the exported version of expandParameter
|
||||
// all refs inside parameter will be resolved relative to basePath
|
||||
func ExpandParameter(parameter *Parameter, basePath string) error {
|
||||
var specBasePath string
|
||||
if basePath != "" {
|
||||
specBasePath, _ = absPath(basePath)
|
||||
}
|
||||
opts := &ExpandOptions{
|
||||
RelativeBase: specBasePath,
|
||||
}
|
||||
resolver, err := defaultSchemaLoader(nil, opts, nil, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return expandParameter(parameter, resolver, opts.RelativeBase)
|
||||
}
|
||||
|
||||
func derefParameter(parameter *Parameter, parentRefs []string, resolver *schemaLoader, basePath string) error {
|
||||
@@ -951,7 +1170,7 @@ func derefParameter(parameter *Parameter, parentRefs []string, resolver *schemaL
|
||||
normalizedRef := normalizeFileRef(¶meter.Ref, basePath)
|
||||
normalizedBasePath := normalizedRef.RemoteURI()
|
||||
|
||||
if isCircular(normalizedRef, basePath, parentRefs...) {
|
||||
if resolver.isCircular(normalizedRef, basePath, parentRefs...) {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -977,11 +1196,27 @@ func expandParameter(parameter *Parameter, resolver *schemaLoader, basePath stri
|
||||
if err := derefParameter(parameter, parentRefs, resolver, basePath); shouldStopOnError(err, resolver.options) {
|
||||
return err
|
||||
}
|
||||
if parameter.Ref.String() != "" {
|
||||
transitiveResolver, err := transitiveResolver(basePath, parameter.Ref, resolver)
|
||||
if shouldStopOnError(err, transitiveResolver.options) {
|
||||
return err
|
||||
}
|
||||
basePath = updateBasePath(transitiveResolver, resolver, basePath)
|
||||
resolver = transitiveResolver
|
||||
}
|
||||
|
||||
if parameter.Schema != nil && parameter.Schema.Ref.String() != "" {
|
||||
// schema expanded to a $ref in another root
|
||||
var ern error
|
||||
parameter.Schema.Ref, ern = NewRef(normalizePaths(parameter.Schema.Ref.String(), parameter.Ref.RemoteURI()))
|
||||
if ern != nil {
|
||||
return ern
|
||||
}
|
||||
}
|
||||
parameter.Ref = Ref{}
|
||||
|
||||
parentRefs = parentRefs[0:]
|
||||
if !resolver.options.SkipSchemas && parameter.Schema != nil {
|
||||
parentRefs = append(parentRefs, parameter.Schema.Ref.String())
|
||||
s, err := expandSchema(*parameter.Schema, parentRefs, resolver, basePath)
|
||||
if shouldStopOnError(err, resolver.options) {
|
||||
return err
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
module github.com/go-openapi/spec
|
||||
|
||||
require (
|
||||
github.com/PuerkitoBio/purell v1.1.0 // indirect
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.17.0
|
||||
github.com/go-openapi/jsonreference v0.17.0
|
||||
github.com/go-openapi/swag v0.17.0
|
||||
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/stretchr/testify v1.2.2
|
||||
golang.org/x/net v0.0.0-20181005035420-146acd28ed58 // indirect
|
||||
golang.org/x/text v0.3.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.2.1
|
||||
)
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
github.com/PuerkitoBio/purell v1.1.0 h1:rmGxhojJlM0tuKtfdvliR84CFHljx9ag64t2xmVkjK4=
|
||||
github.com/PuerkitoBio/purell v1.1.0/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/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.17.0 h1:Bpl2DtZ6k7wKqfFs7e+4P08+M9I3FQgn09a1UsRUQbk=
|
||||
github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
|
||||
github.com/go-openapi/jsonreference v0.17.0 h1:d/o7/fsLWWQZACbihvZxcyLQ59jfUVs7WOJv/ak7T7A=
|
||||
github.com/go-openapi/jsonreference v0.17.0/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
|
||||
github.com/go-openapi/swag v0.17.0 h1:7wu+dZ5k83kvUWeAb+WUkFiUhDzwGqzTR/NhWzeo1JU=
|
||||
github.com/go-openapi/swag v0.17.0/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
|
||||
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 h1:2gxZ0XQIU/5z3Z3bUBu+FXuk2pFbkN6tcwi/pjyaDic=
|
||||
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
golang.org/x/net v0.0.0-20181005035420-146acd28ed58 h1:otZG8yDCO4LVps5+9bxOeNiCvgmOyt96J3roHTYs7oE=
|
||||
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
+8
-10
@@ -22,6 +22,7 @@ import (
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// HeaderProps describes a response header
|
||||
type HeaderProps struct {
|
||||
Description string `json:"description,omitempty"`
|
||||
}
|
||||
@@ -153,7 +154,7 @@ func (h Header) MarshalJSON() ([]byte, error) {
|
||||
return swag.ConcatJSON(b1, b2, b3), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON marshal this from JSON
|
||||
// UnmarshalJSON unmarshals this header from JSON
|
||||
func (h *Header) UnmarshalJSON(data []byte) error {
|
||||
if err := json.Unmarshal(data, &h.CommonValidations); err != nil {
|
||||
return err
|
||||
@@ -164,32 +165,29 @@ func (h *Header) UnmarshalJSON(data []byte) error {
|
||||
if err := json.Unmarshal(data, &h.VendorExtensible); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := json.Unmarshal(data, &h.HeaderProps); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return json.Unmarshal(data, &h.HeaderProps)
|
||||
}
|
||||
|
||||
// JSONLookup look up a value by the json property name
|
||||
func (p Header) JSONLookup(token string) (interface{}, error) {
|
||||
if ex, ok := p.Extensions[token]; ok {
|
||||
func (h Header) JSONLookup(token string) (interface{}, error) {
|
||||
if ex, ok := h.Extensions[token]; ok {
|
||||
return &ex, nil
|
||||
}
|
||||
|
||||
r, _, err := jsonpointer.GetForToken(p.CommonValidations, token)
|
||||
r, _, err := jsonpointer.GetForToken(h.CommonValidations, token)
|
||||
if err != nil && !strings.HasPrefix(err.Error(), "object has no field") {
|
||||
return nil, err
|
||||
}
|
||||
if r != nil {
|
||||
return r, nil
|
||||
}
|
||||
r, _, err = jsonpointer.GetForToken(p.SimpleSchema, token)
|
||||
r, _, err = jsonpointer.GetForToken(h.SimpleSchema, token)
|
||||
if err != nil && !strings.HasPrefix(err.Error(), "object has no field") {
|
||||
return nil, err
|
||||
}
|
||||
if r != nil {
|
||||
return r, nil
|
||||
}
|
||||
r, _, err = jsonpointer.GetForToken(p.HeaderProps, token)
|
||||
r, _, err = jsonpointer.GetForToken(h.HeaderProps, token)
|
||||
return r, err
|
||||
}
|
||||
|
||||
+4
-4
@@ -52,14 +52,14 @@ func (e Extensions) GetBool(key string) (bool, bool) {
|
||||
// GetStringSlice gets a string value from the extensions
|
||||
func (e Extensions) GetStringSlice(key string) ([]string, bool) {
|
||||
if v, ok := e[strings.ToLower(key)]; ok {
|
||||
arr, ok := v.([]interface{})
|
||||
if !ok {
|
||||
arr, isSlice := v.([]interface{})
|
||||
if !isSlice {
|
||||
return nil, false
|
||||
}
|
||||
var strs []string
|
||||
for _, iface := range arr {
|
||||
str, ok := iface.(string)
|
||||
if !ok {
|
||||
str, isString := iface.(string)
|
||||
if !isString {
|
||||
return nil, false
|
||||
}
|
||||
strs = append(strs, str)
|
||||
|
||||
+8
-4
@@ -22,6 +22,7 @@ import (
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// SimpleSchema describe swagger simple schemas for parameters and headers
|
||||
type SimpleSchema struct {
|
||||
Type string `json:"type,omitempty"`
|
||||
Format string `json:"format,omitempty"`
|
||||
@@ -31,6 +32,7 @@ type SimpleSchema struct {
|
||||
Example interface{} `json:"example,omitempty"`
|
||||
}
|
||||
|
||||
// TypeName return the type (or format) of a simple schema
|
||||
func (s *SimpleSchema) TypeName() string {
|
||||
if s.Format != "" {
|
||||
return s.Format
|
||||
@@ -38,6 +40,7 @@ func (s *SimpleSchema) TypeName() string {
|
||||
return s.Type
|
||||
}
|
||||
|
||||
// ItemsTypeName yields the type of items in a simple schema array
|
||||
func (s *SimpleSchema) ItemsTypeName() string {
|
||||
if s.Items == nil {
|
||||
return ""
|
||||
@@ -45,6 +48,7 @@ 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"`
|
||||
@@ -212,18 +216,18 @@ func (i Items) MarshalJSON() ([]byte, error) {
|
||||
}
|
||||
|
||||
// JSONLookup look up a value by the json property name
|
||||
func (p Items) JSONLookup(token string) (interface{}, error) {
|
||||
func (i Items) JSONLookup(token string) (interface{}, error) {
|
||||
if token == "$ref" {
|
||||
return &p.Ref, nil
|
||||
return &i.Ref, nil
|
||||
}
|
||||
|
||||
r, _, err := jsonpointer.GetForToken(p.CommonValidations, token)
|
||||
r, _, err := jsonpointer.GetForToken(i.CommonValidations, token)
|
||||
if err != nil && !strings.HasPrefix(err.Error(), "object has no field") {
|
||||
return nil, err
|
||||
}
|
||||
if r != nil {
|
||||
return r, nil
|
||||
}
|
||||
r, _, err = jsonpointer.GetForToken(p.SimpleSchema, token)
|
||||
r, _, err = jsonpointer.GetForToken(i.SimpleSchema, token)
|
||||
return r, err
|
||||
}
|
||||
|
||||
+3
-2
@@ -21,6 +21,7 @@ import (
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// OperationProps describes an operation
|
||||
type OperationProps struct {
|
||||
Description string `json:"description,omitempty"`
|
||||
Consumes []string `json:"consumes,omitempty"`
|
||||
@@ -38,9 +39,9 @@ type OperationProps struct {
|
||||
|
||||
// MarshalJSON takes care of serializing operation properties to JSON
|
||||
//
|
||||
// We use a custom marhaller here to handle a special cases related
|
||||
// We use a custom marhaller here to handle a special cases related to
|
||||
// the Security field. We need to preserve zero length slice
|
||||
// while omiting the field when the value is nil/unset.
|
||||
// while omitting the field when the value is nil/unset.
|
||||
func (op OperationProps) MarshalJSON() ([]byte, error) {
|
||||
type Alias OperationProps
|
||||
if op.Security == nil {
|
||||
|
||||
+1
@@ -64,6 +64,7 @@ func ParamRef(uri string) *Parameter {
|
||||
return p
|
||||
}
|
||||
|
||||
// ParamProps describes the specific attributes of an operation parameter
|
||||
type ParamProps struct {
|
||||
Description string `json:"description,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@ import (
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// pathItemProps the path item specific properties
|
||||
// PathItemProps the path item specific properties
|
||||
type PathItemProps struct {
|
||||
Get *Operation `json:"get,omitempty"`
|
||||
Put *Operation `json:"put,omitempty"`
|
||||
|
||||
+5
-5
@@ -39,15 +39,15 @@ type Response struct {
|
||||
}
|
||||
|
||||
// JSONLookup look up a value by the json property name
|
||||
func (p Response) JSONLookup(token string) (interface{}, error) {
|
||||
if ex, ok := p.Extensions[token]; ok {
|
||||
func (r Response) JSONLookup(token string) (interface{}, error) {
|
||||
if ex, ok := r.Extensions[token]; ok {
|
||||
return &ex, nil
|
||||
}
|
||||
if token == "$ref" {
|
||||
return &p.Ref, nil
|
||||
return &r.Ref, nil
|
||||
}
|
||||
r, _, err := jsonpointer.GetForToken(p.ResponseProps, token)
|
||||
return r, err
|
||||
ptr, _, err := jsonpointer.GetForToken(r.ResponseProps, token)
|
||||
return ptr, err
|
||||
}
|
||||
|
||||
// UnmarshalJSON hydrates this items instance with the data from JSON
|
||||
|
||||
+5
@@ -85,11 +85,15 @@ func (r Responses) MarshalJSON() ([]byte, error) {
|
||||
return concated, nil
|
||||
}
|
||||
|
||||
// ResponsesProps describes all responses for an operation.
|
||||
// It tells what is the default response and maps all responses with a
|
||||
// HTTP status code.
|
||||
type ResponsesProps struct {
|
||||
Default *Response
|
||||
StatusCodeResponses map[int]Response
|
||||
}
|
||||
|
||||
// MarshalJSON marshals responses as JSON
|
||||
func (r ResponsesProps) MarshalJSON() ([]byte, error) {
|
||||
toser := map[string]Response{}
|
||||
if r.Default != nil {
|
||||
@@ -101,6 +105,7 @@ func (r ResponsesProps) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(toser)
|
||||
}
|
||||
|
||||
// UnmarshalJSON unmarshals responses from JSON
|
||||
func (r *ResponsesProps) UnmarshalJSON(data []byte) error {
|
||||
var res map[string]Response
|
||||
if err := json.Unmarshal(data, &res); err != nil {
|
||||
|
||||
+4
-2
@@ -203,6 +203,7 @@ func (r *SchemaURL) fromMap(v map[string]interface{}) error {
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// SchemaProps describes a JSON schema (draft 4)
|
||||
type SchemaProps struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
Ref Ref `json:"-"`
|
||||
@@ -240,6 +241,7 @@ type SchemaProps struct {
|
||||
Definitions Definitions `json:"definitions,omitempty"`
|
||||
}
|
||||
|
||||
// SwaggerSchemaProps are additional properties supported by swagger schemas, but not JSON-schema (draft 4)
|
||||
type SwaggerSchemaProps struct {
|
||||
Discriminator string `json:"discriminator,omitempty"`
|
||||
ReadOnly bool `json:"readOnly,omitempty"`
|
||||
@@ -604,8 +606,8 @@ func (s *Schema) UnmarshalJSON(data []byte) error {
|
||||
return err
|
||||
}
|
||||
|
||||
sch.Ref.fromMap(d)
|
||||
sch.Schema.fromMap(d)
|
||||
_ = sch.Ref.fromMap(d)
|
||||
_ = sch.Schema.fromMap(d)
|
||||
|
||||
delete(d, "$ref")
|
||||
delete(d, "$schema")
|
||||
|
||||
Generated
Vendored
+1
@@ -78,6 +78,7 @@ func OAuth2AccessToken(authorizationURL, tokenURL string) *SecurityScheme {
|
||||
}}
|
||||
}
|
||||
|
||||
// SecuritySchemeProps describes a swagger security scheme in the securityDefinitions section
|
||||
type SecuritySchemeProps struct {
|
||||
Description string `json:"description,omitempty"`
|
||||
Type string `json:"type"`
|
||||
|
||||
+1
@@ -67,6 +67,7 @@ func (s *Swagger) UnmarshalJSON(data []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SwaggerProps captures the top-level properties of an Api specification
|
||||
type SwaggerProps struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
Consumes []string `json:"consumes,omitempty"`
|
||||
|
||||
+1
@@ -21,6 +21,7 @@ import (
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// TagProps describe a tag entry in the top level tags section of a swagger spec
|
||||
type TagProps struct {
|
||||
Description string `json:"description,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
|
||||
@@ -1 +1,3 @@
|
||||
secrets.yml
|
||||
vendor
|
||||
Godeps
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
linters-settings:
|
||||
govet:
|
||||
check-shadowing: true
|
||||
golint:
|
||||
min-confidence: 0
|
||||
gocyclo:
|
||||
min-complexity: 25
|
||||
maligned:
|
||||
suggest-new: true
|
||||
dupl:
|
||||
threshold: 100
|
||||
goconst:
|
||||
min-len: 3
|
||||
min-occurrences: 2
|
||||
|
||||
linters:
|
||||
enable-all: true
|
||||
disable:
|
||||
- maligned
|
||||
- lll
|
||||
+8
-6
@@ -1,14 +1,16 @@
|
||||
language: go
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
go:
|
||||
- 1.8
|
||||
- '1.9'
|
||||
- 1.10.x
|
||||
- 1.11.x
|
||||
install:
|
||||
- go get -u github.com/stretchr/testify
|
||||
- go get -u github.com/mailru/easyjson
|
||||
- go get -u gopkg.in/yaml.v2
|
||||
script:
|
||||
- go test -v -race -cover -coverprofile=coverage.txt -covermode=atomic ./...
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
language: go
|
||||
notifications:
|
||||
slack:
|
||||
secure: QUWvCkBBK09GF7YtEvHHVt70JOkdlNBG0nIKu/5qc4/nW5HP8I2w0SEf/XR2je0eED1Qe3L/AfMCWwrEj+IUZc3l4v+ju8X8R3Lomhme0Eb0jd1MTMCuPcBT47YCj0M7RON7vXtbFfm1hFJ/jLe5+9FXz0hpXsR24PJc5ZIi/ogNwkaPqG4BmndzecpSh0vc2FJPZUD9LT0I09REY/vXR0oQAalLkW0asGD5taHZTUZq/kBpsNxaAFrLM23i4mUcf33M5fjLpvx5LRICrX/57XpBrDh2TooBU6Qj3CgoY0uPRYUmSNxbVx1czNzl2JtEpb5yjoxfVPQeg0BvQM00G8LJINISR+ohrjhkZmAqchDupAX+yFrxTtORa78CtnIL6z/aTNlgwwVD8kvL/1pFA/JWYmKDmz93mV/+6wubGzNSQCstzjkFA4/iZEKewKUoRIAi/fxyscP6L/rCpmY/4llZZvrnyTqVbt6URWpopUpH4rwYqreXAtJxJsfBJIeSmUIiDIOMGkCTvyTEW3fWGmGoqWtSHLoaWDyAIGb7azb+KvfpWtEcoPFWfSWU+LGee0A/YsUhBl7ADB9A0CJEuR8q4BPpKpfLwPKSiKSAXL7zDkyjExyhtgqbSl2jS+rKIHOZNL8JkCcTP2MKMVd563C5rC5FMKqu3S9m2b6380E=
|
||||
script:
|
||||
- go test -v -race -cover -coverprofile=coverage.txt -covermode=atomic ./...
|
||||
|
||||
+16
-5
@@ -1,12 +1,23 @@
|
||||
# Swag [](https://travis-ci.org/go-openapi/swag) [](https://codecov.io/gh/go-openapi/swag) [](https://slackin.goswagger.io)
|
||||
|
||||
[](https://raw.githubusercontent.com/go-openapi/swag/master/LICENSE) [](http://godoc.org/github.com/go-openapi/swag)
|
||||
[](https://raw.githubusercontent.com/go-openapi/swag/master/LICENSE)
|
||||
[](http://godoc.org/github.com/go-openapi/swag)
|
||||
[](https://golangci.com)
|
||||
[](https://goreportcard.com/report/github.com/go-openapi/swag)
|
||||
|
||||
Contains a bunch of helper functions:
|
||||
Contains a bunch of helper functions for go-openapi and go-swagger projects.
|
||||
|
||||
* convert between value and pointers for builtins
|
||||
* convert from string to builtin
|
||||
You may also use it standalone for your projects.
|
||||
|
||||
* convert between value and pointers for builtin types
|
||||
* convert from string to builtin types (wraps strconv)
|
||||
* fast json concatenation
|
||||
* search in path
|
||||
* load from file or http
|
||||
* name manglin
|
||||
* name mangling
|
||||
|
||||
|
||||
This repo has only few dependencies outside of the standard library:
|
||||
|
||||
* JSON utilities depend on github.com/mailru/easyjson
|
||||
* YAML utilities depend on gopkg.in/yaml.v2
|
||||
|
||||
+33
-14
@@ -22,8 +22,9 @@ import (
|
||||
|
||||
// same as ECMA Number.MAX_SAFE_INTEGER and Number.MIN_SAFE_INTEGER
|
||||
const (
|
||||
maxJSONFloat = float64(1<<53 - 1) // 9007199254740991.0 2^53 - 1
|
||||
minJSONFloat = -float64(1<<53 - 1) //-9007199254740991.0 -2^53 - 1
|
||||
maxJSONFloat = float64(1<<53 - 1) // 9007199254740991.0 2^53 - 1
|
||||
minJSONFloat = -float64(1<<53 - 1) //-9007199254740991.0 -2^53 - 1
|
||||
epsilon float64 = 1e-9
|
||||
)
|
||||
|
||||
// IsFloat64AJSONInteger allow for integers [-2^53, 2^53-1] inclusive
|
||||
@@ -31,21 +32,39 @@ func IsFloat64AJSONInteger(f float64) bool {
|
||||
if math.IsNaN(f) || math.IsInf(f, 0) || f < minJSONFloat || f > maxJSONFloat {
|
||||
return false
|
||||
}
|
||||
fa := math.Abs(f)
|
||||
g := float64(uint64(f))
|
||||
ga := math.Abs(g)
|
||||
|
||||
return f == float64(int64(f)) || f == float64(uint64(f))
|
||||
diff := math.Abs(f - g)
|
||||
|
||||
// more info: https://floating-point-gui.de/errors/comparison/#look-out-for-edge-cases
|
||||
if f == g { // best case
|
||||
return true
|
||||
} else if f == float64(int64(f)) || f == float64(uint64(f)) { // optimistic case
|
||||
return true
|
||||
} else if f == 0 || g == 0 || diff < math.SmallestNonzeroFloat64 { // very close to 0 values
|
||||
return diff < (epsilon * math.SmallestNonzeroFloat64)
|
||||
}
|
||||
// check the relative error
|
||||
return diff/math.Min(fa+ga, math.MaxFloat64) < epsilon
|
||||
}
|
||||
|
||||
var evaluatesAsTrue = map[string]struct{}{
|
||||
"true": struct{}{},
|
||||
"1": struct{}{},
|
||||
"yes": struct{}{},
|
||||
"ok": struct{}{},
|
||||
"y": struct{}{},
|
||||
"on": struct{}{},
|
||||
"selected": struct{}{},
|
||||
"checked": struct{}{},
|
||||
"t": struct{}{},
|
||||
"enabled": struct{}{},
|
||||
var evaluatesAsTrue map[string]struct{}
|
||||
|
||||
func init() {
|
||||
evaluatesAsTrue = map[string]struct{}{
|
||||
"true": {},
|
||||
"1": {},
|
||||
"yes": {},
|
||||
"ok": {},
|
||||
"y": {},
|
||||
"on": {},
|
||||
"selected": {},
|
||||
"checked": {},
|
||||
"t": {},
|
||||
"enabled": {},
|
||||
}
|
||||
}
|
||||
|
||||
// ConvertBool turn a string into a boolean
|
||||
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// 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 swag contains a bunch of helper functions for go-openapi and go-swagger projects.
|
||||
|
||||
You may also use it standalone for your projects.
|
||||
|
||||
* convert between value and pointers for builtin types
|
||||
* convert from string to builtin types (wraps strconv)
|
||||
* fast json concatenation
|
||||
* search in path
|
||||
* load from file or http
|
||||
* name mangling
|
||||
|
||||
|
||||
This repo has only few dependencies outside of the standard library:
|
||||
|
||||
* JSON utilities depend on github.com/mailru/easyjson
|
||||
* YAML utilities depend on gopkg.in/yaml.v2
|
||||
*/
|
||||
package swag
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
module github.com/go-openapi/swag
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/stretchr/testify v1.2.2
|
||||
gopkg.in/yaml.v2 v2.2.1
|
||||
)
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
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/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 h1:2gxZ0XQIU/5z3Z3bUBu+FXuk2pFbkN6tcwi/pjyaDic=
|
||||
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
+29
-13
@@ -26,14 +26,21 @@ import (
|
||||
"github.com/mailru/easyjson/jwriter"
|
||||
)
|
||||
|
||||
// nullJSON represents a JSON object with null type
|
||||
var nullJSON = []byte("null")
|
||||
|
||||
// DefaultJSONNameProvider the default cache for types
|
||||
var DefaultJSONNameProvider = NewNameProvider()
|
||||
|
||||
const comma = byte(',')
|
||||
|
||||
var closers = map[byte]byte{
|
||||
'{': '}',
|
||||
'[': ']',
|
||||
var closers map[byte]byte
|
||||
|
||||
func init() {
|
||||
closers = map[byte]byte{
|
||||
'{': '}',
|
||||
'[': ']',
|
||||
}
|
||||
}
|
||||
|
||||
type ejMarshaler interface {
|
||||
@@ -79,10 +86,7 @@ func DynamicJSONToStruct(data interface{}, target interface{}) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ReadJSON(b, target); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return ReadJSON(b, target)
|
||||
}
|
||||
|
||||
// ConcatJSON concatenates multiple json objects efficiently
|
||||
@@ -90,17 +94,29 @@ func ConcatJSON(blobs ...[]byte) []byte {
|
||||
if len(blobs) == 0 {
|
||||
return nil
|
||||
}
|
||||
if len(blobs) == 1 {
|
||||
|
||||
last := len(blobs) - 1
|
||||
for blobs[last] == nil || bytes.Equal(blobs[last], nullJSON) {
|
||||
// strips trailing null objects
|
||||
last = last - 1
|
||||
if last < 0 {
|
||||
// there was nothing but "null"s or nil...
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if last == 0 {
|
||||
return blobs[0]
|
||||
}
|
||||
|
||||
last := len(blobs) - 1
|
||||
var opening, closing byte
|
||||
a := 0
|
||||
idx := 0
|
||||
var idx, a int
|
||||
buf := bytes.NewBuffer(nil)
|
||||
|
||||
for i, b := range blobs {
|
||||
for i, b := range blobs[:last+1] {
|
||||
if b == nil || bytes.Equal(b, nullJSON) {
|
||||
// a null object is in the list: skip it
|
||||
continue
|
||||
}
|
||||
if len(b) > 0 && opening == 0 { // is this an array or an object?
|
||||
opening, closing = b[0], closers[b[0]]
|
||||
}
|
||||
@@ -245,7 +261,7 @@ func (n *NameProvider) GetJSONNames(subject interface{}) []string {
|
||||
names = n.makeNameIndex(tpe)
|
||||
}
|
||||
|
||||
var res []string
|
||||
res := make([]string, 0, len(names.jsonNames))
|
||||
for k := range names.jsonNames {
|
||||
res = append(res, k)
|
||||
}
|
||||
|
||||
+7
-1
@@ -43,7 +43,13 @@ func LoadStrategy(path string, local, remote func(string) ([]byte, error)) func(
|
||||
if strings.HasPrefix(path, "http") {
|
||||
return remote
|
||||
}
|
||||
return func(pth string) ([]byte, error) { return local(filepath.FromSlash(pth)) }
|
||||
return func(pth string) ([]byte, error) {
|
||||
upth, err := pathUnescape(pth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return local(filepath.FromSlash(upth))
|
||||
}
|
||||
}
|
||||
|
||||
func loadHTTPBytes(timeout time.Duration) func(path string) ([]byte, error) {
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// +build go1.8
|
||||
|
||||
package swag
|
||||
|
||||
import "net/url"
|
||||
|
||||
func pathUnescape(path string) (string, error) {
|
||||
return url.PathUnescape(path)
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
// +build go1.9
|
||||
|
||||
package swag
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// indexOfInitialisms is a thread-safe implementation of the sorted index of initialisms.
|
||||
// Since go1.9, this may be implemented with sync.Map.
|
||||
type indexOfInitialisms struct {
|
||||
sortMutex *sync.Mutex
|
||||
index *sync.Map
|
||||
}
|
||||
|
||||
func newIndexOfInitialisms() *indexOfInitialisms {
|
||||
return &indexOfInitialisms{
|
||||
sortMutex: new(sync.Mutex),
|
||||
index: new(sync.Map),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *indexOfInitialisms) load(initial map[string]bool) *indexOfInitialisms {
|
||||
m.sortMutex.Lock()
|
||||
defer m.sortMutex.Unlock()
|
||||
for k, v := range initial {
|
||||
m.index.Store(k, v)
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *indexOfInitialisms) isInitialism(key string) bool {
|
||||
_, ok := m.index.Load(key)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (m *indexOfInitialisms) add(key string) *indexOfInitialisms {
|
||||
m.index.Store(key, true)
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *indexOfInitialisms) sorted() (result []string) {
|
||||
m.sortMutex.Lock()
|
||||
defer m.sortMutex.Unlock()
|
||||
m.index.Range(func(key, value interface{}) bool {
|
||||
k := key.(string)
|
||||
result = append(result, k)
|
||||
return true
|
||||
})
|
||||
sort.Sort(sort.Reverse(byLength(result)))
|
||||
return
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// +build !go1.8
|
||||
|
||||
package swag
|
||||
|
||||
import "net/url"
|
||||
|
||||
func pathUnescape(path string) (string, error) {
|
||||
return url.QueryUnescape(path)
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
// +build !go1.9
|
||||
|
||||
package swag
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// indexOfInitialisms is a thread-safe implementation of the sorted index of initialisms.
|
||||
// Before go1.9, this may be implemented with a mutex on the map.
|
||||
type indexOfInitialisms struct {
|
||||
getMutex *sync.Mutex
|
||||
index map[string]bool
|
||||
}
|
||||
|
||||
func newIndexOfInitialisms() *indexOfInitialisms {
|
||||
return &indexOfInitialisms{
|
||||
getMutex: new(sync.Mutex),
|
||||
index: make(map[string]bool, 50),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *indexOfInitialisms) load(initial map[string]bool) *indexOfInitialisms {
|
||||
m.getMutex.Lock()
|
||||
defer m.getMutex.Unlock()
|
||||
for k, v := range initial {
|
||||
m.index[k] = v
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *indexOfInitialisms) isInitialism(key string) bool {
|
||||
m.getMutex.Lock()
|
||||
defer m.getMutex.Unlock()
|
||||
_, ok := m.index[key]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (m *indexOfInitialisms) add(key string) *indexOfInitialisms {
|
||||
m.getMutex.Lock()
|
||||
defer m.getMutex.Unlock()
|
||||
m.index[key] = true
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *indexOfInitialisms) sorted() (result []string) {
|
||||
m.getMutex.Lock()
|
||||
defer m.getMutex.Unlock()
|
||||
for k := range m.index {
|
||||
result = append(result, k)
|
||||
}
|
||||
sort.Sort(sort.Reverse(byLength(result)))
|
||||
return
|
||||
}
|
||||
+144
-75
@@ -18,62 +18,85 @@ import (
|
||||
"math"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// Taken from https://github.com/golang/lint/blob/3390df4df2787994aea98de825b964ac7944b817/lint.go#L732-L769
|
||||
var commonInitialisms = map[string]bool{
|
||||
"ACL": true,
|
||||
"API": true,
|
||||
"ASCII": true,
|
||||
"CPU": true,
|
||||
"CSS": true,
|
||||
"DNS": true,
|
||||
"EOF": true,
|
||||
"GUID": true,
|
||||
"HTML": true,
|
||||
"HTTPS": true,
|
||||
"HTTP": true,
|
||||
"ID": true,
|
||||
"IP": true,
|
||||
"JSON": true,
|
||||
"LHS": true,
|
||||
"QPS": true,
|
||||
"RAM": true,
|
||||
"RHS": true,
|
||||
"RPC": true,
|
||||
"SLA": true,
|
||||
"SMTP": true,
|
||||
"SQL": true,
|
||||
"SSH": true,
|
||||
"TCP": true,
|
||||
"TLS": true,
|
||||
"TTL": true,
|
||||
"UDP": true,
|
||||
"UI": true,
|
||||
"UID": true,
|
||||
"UUID": true,
|
||||
"URI": true,
|
||||
"URL": true,
|
||||
"UTF8": true,
|
||||
"VM": true,
|
||||
"XML": true,
|
||||
"XMPP": true,
|
||||
"XSRF": true,
|
||||
"XSS": true,
|
||||
}
|
||||
// commonInitialisms are common acronyms that are kept as whole uppercased words.
|
||||
var commonInitialisms *indexOfInitialisms
|
||||
|
||||
// initialisms is a slice of sorted initialisms
|
||||
var initialisms []string
|
||||
|
||||
var once sync.Once
|
||||
|
||||
var isInitialism func(string) bool
|
||||
|
||||
func init() {
|
||||
for k := range commonInitialisms {
|
||||
initialisms = append(initialisms, k)
|
||||
// Taken from https://github.com/golang/lint/blob/3390df4df2787994aea98de825b964ac7944b817/lint.go#L732-L769
|
||||
var configuredInitialisms = map[string]bool{
|
||||
"ACL": true,
|
||||
"API": true,
|
||||
"ASCII": true,
|
||||
"CPU": true,
|
||||
"CSS": true,
|
||||
"DNS": true,
|
||||
"EOF": true,
|
||||
"GUID": true,
|
||||
"HTML": true,
|
||||
"HTTPS": true,
|
||||
"HTTP": true,
|
||||
"ID": true,
|
||||
"IP": true,
|
||||
"JSON": true,
|
||||
"LHS": true,
|
||||
"OAI": true,
|
||||
"QPS": true,
|
||||
"RAM": true,
|
||||
"RHS": true,
|
||||
"RPC": true,
|
||||
"SLA": true,
|
||||
"SMTP": true,
|
||||
"SQL": true,
|
||||
"SSH": true,
|
||||
"TCP": true,
|
||||
"TLS": true,
|
||||
"TTL": true,
|
||||
"UDP": true,
|
||||
"UI": true,
|
||||
"UID": true,
|
||||
"UUID": true,
|
||||
"URI": true,
|
||||
"URL": true,
|
||||
"UTF8": true,
|
||||
"VM": true,
|
||||
"XML": true,
|
||||
"XMPP": true,
|
||||
"XSRF": true,
|
||||
"XSS": true,
|
||||
}
|
||||
sort.Sort(sort.Reverse(byLength(initialisms)))
|
||||
|
||||
// a thread-safe index of initialisms
|
||||
commonInitialisms = newIndexOfInitialisms().load(configuredInitialisms)
|
||||
|
||||
// a test function
|
||||
isInitialism = commonInitialisms.isInitialism
|
||||
}
|
||||
|
||||
// JoinByFormat joins a string array by a known format:
|
||||
func ensureSorted() {
|
||||
initialisms = commonInitialisms.sorted()
|
||||
}
|
||||
|
||||
const (
|
||||
//collectionFormatComma = "csv"
|
||||
collectionFormatSpace = "ssv"
|
||||
collectionFormatTab = "tsv"
|
||||
collectionFormatPipe = "pipes"
|
||||
collectionFormatMulti = "multi"
|
||||
)
|
||||
|
||||
// JoinByFormat joins a string array by a known format (e.g. swagger's collectionFormat attribute):
|
||||
// ssv: space separated value
|
||||
// tsv: tab separated value
|
||||
// pipes: pipe (|) separated value
|
||||
@@ -84,13 +107,13 @@ func JoinByFormat(data []string, format string) []string {
|
||||
}
|
||||
var sep string
|
||||
switch format {
|
||||
case "ssv":
|
||||
case collectionFormatSpace:
|
||||
sep = " "
|
||||
case "tsv":
|
||||
case collectionFormatTab:
|
||||
sep = "\t"
|
||||
case "pipes":
|
||||
case collectionFormatPipe:
|
||||
sep = "|"
|
||||
case "multi":
|
||||
case collectionFormatMulti:
|
||||
return data
|
||||
default:
|
||||
sep = ","
|
||||
@@ -103,19 +126,20 @@ func JoinByFormat(data []string, format string) []string {
|
||||
// tsv: tab separated value
|
||||
// pipes: pipe (|) separated value
|
||||
// csv: comma separated value (default)
|
||||
//
|
||||
func SplitByFormat(data, format string) []string {
|
||||
if data == "" {
|
||||
return nil
|
||||
}
|
||||
var sep string
|
||||
switch format {
|
||||
case "ssv":
|
||||
case collectionFormatSpace:
|
||||
sep = " "
|
||||
case "tsv":
|
||||
case collectionFormatTab:
|
||||
sep = "\t"
|
||||
case "pipes":
|
||||
case collectionFormatPipe:
|
||||
sep = "|"
|
||||
case "multi":
|
||||
case collectionFormatMulti:
|
||||
return nil
|
||||
default:
|
||||
sep = ","
|
||||
@@ -142,7 +166,7 @@ func (s byLength) Less(i, j int) bool {
|
||||
}
|
||||
|
||||
// Prepares strings by splitting by caps, spaces, dashes, and underscore
|
||||
func split(str string) (words []string) {
|
||||
func split(str string) []string {
|
||||
repl := strings.NewReplacer(
|
||||
"@", "At ",
|
||||
"&", "And ",
|
||||
@@ -163,15 +187,15 @@ func split(str string) (words []string) {
|
||||
|
||||
// Split when uppercase is found (needed for Snake)
|
||||
str = rex1.ReplaceAllString(str, " $1")
|
||||
// check if consecutive single char things make up an initialism
|
||||
|
||||
// check if consecutive single char things make up an initialism
|
||||
once.Do(ensureSorted)
|
||||
for _, k := range initialisms {
|
||||
str = strings.Replace(str, rex1.ReplaceAllString(k, " $1"), " "+k, -1)
|
||||
}
|
||||
// Get the final list of words
|
||||
words = rex2.FindAllString(str, -1)
|
||||
|
||||
return
|
||||
//words = rex2.FindAllString(str, -1)
|
||||
return rex2.FindAllString(str, -1)
|
||||
}
|
||||
|
||||
// Removes leading whitespaces
|
||||
@@ -189,19 +213,36 @@ func lower(str string) string {
|
||||
return strings.ToLower(trim(str))
|
||||
}
|
||||
|
||||
// Camelize an uppercased word
|
||||
func Camelize(word string) (camelized string) {
|
||||
for pos, ru := range word {
|
||||
if pos > 0 {
|
||||
camelized += string(unicode.ToLower(ru))
|
||||
} else {
|
||||
camelized += string(unicode.ToUpper(ru))
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ToFileName lowercases and underscores a go type name
|
||||
func ToFileName(name string) string {
|
||||
var out []string
|
||||
for _, w := range split(name) {
|
||||
in := split(name)
|
||||
out := make([]string, 0, len(in))
|
||||
|
||||
for _, w := range in {
|
||||
out = append(out, lower(w))
|
||||
}
|
||||
|
||||
return strings.Join(out, "_")
|
||||
}
|
||||
|
||||
// ToCommandName lowercases and underscores a go type name
|
||||
func ToCommandName(name string) string {
|
||||
var out []string
|
||||
for _, w := range split(name) {
|
||||
in := split(name)
|
||||
out := make([]string, 0, len(in))
|
||||
|
||||
for _, w := range in {
|
||||
out = append(out, lower(w))
|
||||
}
|
||||
return strings.Join(out, "-")
|
||||
@@ -209,9 +250,11 @@ func ToCommandName(name string) string {
|
||||
|
||||
// ToHumanNameLower represents a code name as a human series of words
|
||||
func ToHumanNameLower(name string) string {
|
||||
var out []string
|
||||
for _, w := range split(name) {
|
||||
if !commonInitialisms[upper(w)] {
|
||||
in := split(name)
|
||||
out := make([]string, 0, len(in))
|
||||
|
||||
for _, w := range in {
|
||||
if !isInitialism(upper(w)) {
|
||||
out = append(out, lower(w))
|
||||
} else {
|
||||
out = append(out, w)
|
||||
@@ -222,10 +265,12 @@ func ToHumanNameLower(name string) string {
|
||||
|
||||
// ToHumanNameTitle represents a code name as a human series of words with the first letters titleized
|
||||
func ToHumanNameTitle(name string) string {
|
||||
var out []string
|
||||
for _, w := range split(name) {
|
||||
in := split(name)
|
||||
out := make([]string, 0, len(in))
|
||||
|
||||
for _, w := range in {
|
||||
uw := upper(w)
|
||||
if !commonInitialisms[uw] {
|
||||
if !isInitialism(uw) {
|
||||
out = append(out, upper(w[:1])+lower(w[1:]))
|
||||
} else {
|
||||
out = append(out, w)
|
||||
@@ -236,8 +281,10 @@ func ToHumanNameTitle(name string) string {
|
||||
|
||||
// ToJSONName camelcases a name which can be underscored or pascal cased
|
||||
func ToJSONName(name string) string {
|
||||
var out []string
|
||||
for i, w := range split(name) {
|
||||
in := split(name)
|
||||
out := make([]string, 0, len(in))
|
||||
|
||||
for i, w := range in {
|
||||
if i == 0 {
|
||||
out = append(out, lower(w))
|
||||
continue
|
||||
@@ -250,7 +297,7 @@ func ToJSONName(name string) string {
|
||||
// ToVarName camelcases a name which can be underscored or pascal cased
|
||||
func ToVarName(name string) string {
|
||||
res := ToGoName(name)
|
||||
if _, ok := commonInitialisms[res]; ok {
|
||||
if isInitialism(res) {
|
||||
return lower(res)
|
||||
}
|
||||
if len(res) <= 1 {
|
||||
@@ -261,11 +308,13 @@ func ToVarName(name string) string {
|
||||
|
||||
// ToGoName translates a swagger name which can be underscored or camel cased to a name that golint likes
|
||||
func ToGoName(name string) string {
|
||||
var out []string
|
||||
for _, w := range split(name) {
|
||||
in := split(name)
|
||||
out := make([]string, 0, len(in))
|
||||
|
||||
for _, w := range in {
|
||||
uw := upper(w)
|
||||
mod := int(math.Min(float64(len(uw)), 2))
|
||||
if !commonInitialisms[uw] && !commonInitialisms[uw[:len(uw)-mod]] {
|
||||
if !isInitialism(uw) && !isInitialism(uw[:len(uw)-mod]) {
|
||||
uw = upper(w[:1]) + lower(w[1:])
|
||||
}
|
||||
out = append(out, uw)
|
||||
@@ -284,6 +333,16 @@ func ToGoName(name string) string {
|
||||
return result
|
||||
}
|
||||
|
||||
// ContainsStrings searches a slice of strings for a case-sensitive match
|
||||
func ContainsStrings(coll []string, item string) bool {
|
||||
for _, a := range coll {
|
||||
if a == item {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ContainsStringsCI searches a slice of strings for a case-insensitive match
|
||||
func ContainsStringsCI(coll []string, item string) bool {
|
||||
for _, a := range coll {
|
||||
@@ -328,6 +387,16 @@ func IsZero(data interface{}) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// AddInitialisms add additional initialisms
|
||||
func AddInitialisms(words ...string) {
|
||||
for _, word := range words {
|
||||
//commonInitialisms[upper(word)] = true
|
||||
commonInitialisms.add(upper(word))
|
||||
}
|
||||
// sort again
|
||||
initialisms = commonInitialisms.sorted()
|
||||
}
|
||||
|
||||
// CommandLineOptionsGroup represents a group of user-defined command line options
|
||||
type CommandLineOptionsGroup struct {
|
||||
ShortDescription string
|
||||
|
||||
+25
-12
@@ -42,6 +42,7 @@ func YAMLToJSON(data interface{}) (json.RawMessage, error) {
|
||||
return json.RawMessage(b), err
|
||||
}
|
||||
|
||||
// BytesToYAMLDoc converts a byte slice into a YAML document
|
||||
func BytesToYAMLDoc(data []byte) (interface{}, error) {
|
||||
var canary map[interface{}]interface{} // validate this is an object and not a different type
|
||||
if err := yaml.Unmarshal(data, &canary); err != nil {
|
||||
@@ -55,14 +56,17 @@ func BytesToYAMLDoc(data []byte) (interface{}, error) {
|
||||
return document, nil
|
||||
}
|
||||
|
||||
// JSONMapSlice represent a JSON object, with the order of keys maintained
|
||||
type JSONMapSlice []JSONMapItem
|
||||
|
||||
// MarshalJSON renders a JSONMapSlice as JSON
|
||||
func (s JSONMapSlice) MarshalJSON() ([]byte, error) {
|
||||
w := &jwriter.Writer{Flags: jwriter.NilMapAsEmpty | jwriter.NilSliceAsEmpty}
|
||||
s.MarshalEasyJSON(w)
|
||||
return w.BuildBytes()
|
||||
}
|
||||
|
||||
// MarshalEasyJSON renders a JSONMapSlice as JSON, using easyJSON
|
||||
func (s JSONMapSlice) MarshalEasyJSON(w *jwriter.Writer) {
|
||||
w.RawByte('{')
|
||||
|
||||
@@ -78,11 +82,14 @@ func (s JSONMapSlice) MarshalEasyJSON(w *jwriter.Writer) {
|
||||
w.RawByte('}')
|
||||
}
|
||||
|
||||
// UnmarshalJSON makes a JSONMapSlice from JSON
|
||||
func (s *JSONMapSlice) UnmarshalJSON(data []byte) error {
|
||||
l := jlexer.Lexer{Data: data}
|
||||
s.UnmarshalEasyJSON(&l)
|
||||
return l.Error()
|
||||
}
|
||||
|
||||
// UnmarshalEasyJSON makes a JSONMapSlice from JSON, using easyJSON
|
||||
func (s *JSONMapSlice) UnmarshalEasyJSON(in *jlexer.Lexer) {
|
||||
if in.IsNull() {
|
||||
in.Skip()
|
||||
@@ -99,23 +106,34 @@ func (s *JSONMapSlice) UnmarshalEasyJSON(in *jlexer.Lexer) {
|
||||
*s = result
|
||||
}
|
||||
|
||||
// JSONMapItem represents the value of a key in a JSON object held by JSONMapSlice
|
||||
type JSONMapItem struct {
|
||||
Key string
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
// MarshalJSON renders a JSONMapItem as JSON
|
||||
func (s JSONMapItem) MarshalJSON() ([]byte, error) {
|
||||
w := &jwriter.Writer{Flags: jwriter.NilMapAsEmpty | jwriter.NilSliceAsEmpty}
|
||||
s.MarshalEasyJSON(w)
|
||||
return w.BuildBytes()
|
||||
}
|
||||
|
||||
// MarshalEasyJSON renders a JSONMapItem as JSON, using easyJSON
|
||||
func (s JSONMapItem) MarshalEasyJSON(w *jwriter.Writer) {
|
||||
w.String(s.Key)
|
||||
w.RawByte(':')
|
||||
w.Raw(WriteJSON(s.Value))
|
||||
}
|
||||
|
||||
// UnmarshalJSON makes a JSONMapItem from JSON
|
||||
func (s *JSONMapItem) UnmarshalJSON(data []byte) error {
|
||||
l := jlexer.Lexer{Data: data}
|
||||
s.UnmarshalEasyJSON(&l)
|
||||
return l.Error()
|
||||
}
|
||||
|
||||
// UnmarshalEasyJSON makes a JSONMapItem from JSON, using easyJSON
|
||||
func (s *JSONMapItem) UnmarshalEasyJSON(in *jlexer.Lexer) {
|
||||
key := in.UnsafeString()
|
||||
in.WantColon()
|
||||
@@ -124,11 +142,6 @@ func (s *JSONMapItem) UnmarshalEasyJSON(in *jlexer.Lexer) {
|
||||
s.Key = key
|
||||
s.Value = value
|
||||
}
|
||||
func (s *JSONMapItem) UnmarshalJSON(data []byte) error {
|
||||
l := jlexer.Lexer{Data: data}
|
||||
s.UnmarshalEasyJSON(&l)
|
||||
return l.Error()
|
||||
}
|
||||
|
||||
func transformData(input interface{}) (out interface{}, err error) {
|
||||
switch in := input.(type) {
|
||||
@@ -146,9 +159,9 @@ func transformData(input interface{}) (out interface{}, err error) {
|
||||
return nil, fmt.Errorf("types don't match expect map key string or int got: %T", mi.Key)
|
||||
}
|
||||
|
||||
v, err := transformData(mi.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
v, ert := transformData(mi.Value)
|
||||
if ert != nil {
|
||||
return nil, ert
|
||||
}
|
||||
nmi.Value = v
|
||||
o[i] = nmi
|
||||
@@ -167,9 +180,9 @@ func transformData(input interface{}) (out interface{}, err error) {
|
||||
return nil, fmt.Errorf("types don't match expect map key string or int got: %T", ke)
|
||||
}
|
||||
|
||||
v, err := transformData(va)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
v, ert := transformData(va)
|
||||
if ert != nil {
|
||||
return nil, ert
|
||||
}
|
||||
nmi.Value = v
|
||||
o = append(o, nmi)
|
||||
@@ -201,7 +214,7 @@ func YAMLDoc(path string) (json.RawMessage, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return json.RawMessage(data), nil
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// YAMLData loads a yaml document from either http or a file
|
||||
|
||||
+89
-37
@@ -21,6 +21,7 @@ import (
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/text/secure/bidirule"
|
||||
"golang.org/x/text/unicode/bidi"
|
||||
"golang.org/x/text/unicode/norm"
|
||||
)
|
||||
|
||||
@@ -68,7 +69,7 @@ func VerifyDNSLength(verify bool) Option {
|
||||
}
|
||||
|
||||
// RemoveLeadingDots removes leading label separators. Leading runes that map to
|
||||
// dots, such as U+3002, are removed as well.
|
||||
// dots, such as U+3002 IDEOGRAPHIC FULL STOP, are removed as well.
|
||||
//
|
||||
// This is the behavior suggested by the UTS #46 and is adopted by some
|
||||
// browsers.
|
||||
@@ -92,7 +93,7 @@ func ValidateLabels(enable bool) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// StrictDomainName limits the set of permissable ASCII characters to those
|
||||
// StrictDomainName limits the set of permissible ASCII characters to those
|
||||
// allowed in domain names as defined in RFC 1034 (A-Z, a-z, 0-9 and the
|
||||
// hyphen). This is set by default for MapForLookup and ValidateForRegistration.
|
||||
//
|
||||
@@ -142,7 +143,6 @@ func MapForLookup() Option {
|
||||
o.mapping = validateAndMap
|
||||
StrictDomainName(true)(o)
|
||||
ValidateLabels(true)(o)
|
||||
RemoveLeadingDots(true)(o)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,14 +160,14 @@ type options struct {
|
||||
|
||||
// mapping implements a validation and mapping step as defined in RFC 5895
|
||||
// or UTS 46, tailored to, for example, domain registration or lookup.
|
||||
mapping func(p *Profile, s string) (string, error)
|
||||
mapping func(p *Profile, s string) (mapped string, isBidi bool, err error)
|
||||
|
||||
// bidirule, if specified, checks whether s conforms to the Bidi Rule
|
||||
// defined in RFC 5893.
|
||||
bidirule func(s string) bool
|
||||
}
|
||||
|
||||
// A Profile defines the configuration of a IDNA mapper.
|
||||
// A Profile defines the configuration of an IDNA mapper.
|
||||
type Profile struct {
|
||||
options
|
||||
}
|
||||
@@ -251,23 +251,21 @@ var (
|
||||
|
||||
punycode = &Profile{}
|
||||
lookup = &Profile{options{
|
||||
transitional: true,
|
||||
useSTD3Rules: true,
|
||||
validateLabels: true,
|
||||
removeLeadingDots: true,
|
||||
trie: trie,
|
||||
fromPuny: validateFromPunycode,
|
||||
mapping: validateAndMap,
|
||||
bidirule: bidirule.ValidString,
|
||||
transitional: true,
|
||||
useSTD3Rules: true,
|
||||
validateLabels: true,
|
||||
trie: trie,
|
||||
fromPuny: validateFromPunycode,
|
||||
mapping: validateAndMap,
|
||||
bidirule: bidirule.ValidString,
|
||||
}}
|
||||
display = &Profile{options{
|
||||
useSTD3Rules: true,
|
||||
validateLabels: true,
|
||||
removeLeadingDots: true,
|
||||
trie: trie,
|
||||
fromPuny: validateFromPunycode,
|
||||
mapping: validateAndMap,
|
||||
bidirule: bidirule.ValidString,
|
||||
useSTD3Rules: true,
|
||||
validateLabels: true,
|
||||
trie: trie,
|
||||
fromPuny: validateFromPunycode,
|
||||
mapping: validateAndMap,
|
||||
bidirule: bidirule.ValidString,
|
||||
}}
|
||||
registration = &Profile{options{
|
||||
useSTD3Rules: true,
|
||||
@@ -302,14 +300,16 @@ func (e runeError) Error() string {
|
||||
// see http://www.unicode.org/reports/tr46.
|
||||
func (p *Profile) process(s string, toASCII bool) (string, error) {
|
||||
var err error
|
||||
var isBidi bool
|
||||
if p.mapping != nil {
|
||||
s, err = p.mapping(p, s)
|
||||
s, isBidi, err = p.mapping(p, s)
|
||||
}
|
||||
// Remove leading empty labels.
|
||||
if p.removeLeadingDots {
|
||||
for ; len(s) > 0 && s[0] == '.'; s = s[1:] {
|
||||
}
|
||||
}
|
||||
// TODO: allow for a quick check of the tables data.
|
||||
// It seems like we should only create this error on ToASCII, but the
|
||||
// UTS 46 conformance tests suggests we should always check this.
|
||||
if err == nil && p.verifyDNSLength && s == "" {
|
||||
@@ -335,6 +335,7 @@ func (p *Profile) process(s string, toASCII bool) (string, error) {
|
||||
// Spec says keep the old label.
|
||||
continue
|
||||
}
|
||||
isBidi = isBidi || bidirule.DirectionString(u) != bidi.LeftToRight
|
||||
labels.set(u)
|
||||
if err == nil && p.validateLabels {
|
||||
err = p.fromPuny(p, u)
|
||||
@@ -349,6 +350,14 @@ func (p *Profile) process(s string, toASCII bool) (string, error) {
|
||||
err = p.validateLabel(label)
|
||||
}
|
||||
}
|
||||
if isBidi && p.bidirule != nil && err == nil {
|
||||
for labels.reset(); !labels.done(); labels.next() {
|
||||
if !p.bidirule(labels.label()) {
|
||||
err = &labelError{s, "B"}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if toASCII {
|
||||
for labels.reset(); !labels.done(); labels.next() {
|
||||
label := labels.label()
|
||||
@@ -380,16 +389,26 @@ func (p *Profile) process(s string, toASCII bool) (string, error) {
|
||||
return s, err
|
||||
}
|
||||
|
||||
func normalize(p *Profile, s string) (string, error) {
|
||||
return norm.NFC.String(s), nil
|
||||
func normalize(p *Profile, s string) (mapped string, isBidi bool, err error) {
|
||||
// TODO: consider first doing a quick check to see if any of these checks
|
||||
// need to be done. This will make it slower in the general case, but
|
||||
// faster in the common case.
|
||||
mapped = norm.NFC.String(s)
|
||||
isBidi = bidirule.DirectionString(mapped) == bidi.RightToLeft
|
||||
return mapped, isBidi, nil
|
||||
}
|
||||
|
||||
func validateRegistration(p *Profile, s string) (string, error) {
|
||||
func validateRegistration(p *Profile, s string) (idem string, bidi bool, err error) {
|
||||
// TODO: filter need for normalization in loop below.
|
||||
if !norm.NFC.IsNormalString(s) {
|
||||
return s, &labelError{s, "V1"}
|
||||
return s, false, &labelError{s, "V1"}
|
||||
}
|
||||
for i := 0; i < len(s); {
|
||||
v, sz := trie.lookupString(s[i:])
|
||||
if sz == 0 {
|
||||
return s, bidi, runeError(utf8.RuneError)
|
||||
}
|
||||
bidi = bidi || info(v).isBidi(s[i:])
|
||||
// Copy bytes not copied so far.
|
||||
switch p.simplify(info(v).category()) {
|
||||
// TODO: handle the NV8 defined in the Unicode idna data set to allow
|
||||
@@ -397,21 +416,50 @@ func validateRegistration(p *Profile, s string) (string, error) {
|
||||
case valid, deviation:
|
||||
case disallowed, mapped, unknown, ignored:
|
||||
r, _ := utf8.DecodeRuneInString(s[i:])
|
||||
return s, runeError(r)
|
||||
return s, bidi, runeError(r)
|
||||
}
|
||||
i += sz
|
||||
}
|
||||
return s, nil
|
||||
return s, bidi, nil
|
||||
}
|
||||
|
||||
func validateAndMap(p *Profile, s string) (string, error) {
|
||||
func (c info) isBidi(s string) bool {
|
||||
if !c.isMapped() {
|
||||
return c&attributesMask == rtl
|
||||
}
|
||||
// TODO: also store bidi info for mapped data. This is possible, but a bit
|
||||
// cumbersome and not for the common case.
|
||||
p, _ := bidi.LookupString(s)
|
||||
switch p.Class() {
|
||||
case bidi.R, bidi.AL, bidi.AN:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func validateAndMap(p *Profile, s string) (vm string, bidi bool, err error) {
|
||||
var (
|
||||
err error
|
||||
b []byte
|
||||
k int
|
||||
b []byte
|
||||
k int
|
||||
)
|
||||
// combinedInfoBits contains the or-ed bits of all runes. We use this
|
||||
// to derive the mayNeedNorm bit later. This may trigger normalization
|
||||
// overeagerly, but it will not do so in the common case. The end result
|
||||
// is another 10% saving on BenchmarkProfile for the common case.
|
||||
var combinedInfoBits info
|
||||
for i := 0; i < len(s); {
|
||||
v, sz := trie.lookupString(s[i:])
|
||||
if sz == 0 {
|
||||
b = append(b, s[k:i]...)
|
||||
b = append(b, "\ufffd"...)
|
||||
k = len(s)
|
||||
if err == nil {
|
||||
err = runeError(utf8.RuneError)
|
||||
}
|
||||
break
|
||||
}
|
||||
combinedInfoBits |= info(v)
|
||||
bidi = bidi || info(v).isBidi(s[i:])
|
||||
start := i
|
||||
i += sz
|
||||
// Copy bytes not copied so far.
|
||||
@@ -438,7 +486,9 @@ func validateAndMap(p *Profile, s string) (string, error) {
|
||||
}
|
||||
if k == 0 {
|
||||
// No changes so far.
|
||||
s = norm.NFC.String(s)
|
||||
if combinedInfoBits&mayNeedNorm != 0 {
|
||||
s = norm.NFC.String(s)
|
||||
}
|
||||
} else {
|
||||
b = append(b, s[k:]...)
|
||||
if norm.NFC.QuickSpan(b) != len(b) {
|
||||
@@ -447,7 +497,7 @@ func validateAndMap(p *Profile, s string) (string, error) {
|
||||
// TODO: the punycode converters require strings as input.
|
||||
s = string(b)
|
||||
}
|
||||
return s, err
|
||||
return s, bidi, err
|
||||
}
|
||||
|
||||
// A labelIter allows iterating over domain name labels.
|
||||
@@ -542,8 +592,13 @@ func validateFromPunycode(p *Profile, s string) error {
|
||||
if !norm.NFC.IsNormalString(s) {
|
||||
return &labelError{s, "V1"}
|
||||
}
|
||||
// TODO: detect whether string may have to be normalized in the following
|
||||
// loop.
|
||||
for i := 0; i < len(s); {
|
||||
v, sz := trie.lookupString(s[i:])
|
||||
if sz == 0 {
|
||||
return runeError(utf8.RuneError)
|
||||
}
|
||||
if c := p.simplify(info(v).category()); c != valid && c != deviation {
|
||||
return &labelError{s, "V6"}
|
||||
}
|
||||
@@ -616,16 +671,13 @@ var joinStates = [][numJoinTypes]joinState{
|
||||
|
||||
// validateLabel validates the criteria from Section 4.1. Item 1, 4, and 6 are
|
||||
// already implicitly satisfied by the overall implementation.
|
||||
func (p *Profile) validateLabel(s string) error {
|
||||
func (p *Profile) validateLabel(s string) (err error) {
|
||||
if s == "" {
|
||||
if p.verifyDNSLength {
|
||||
return &labelError{s, "A4"}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if p.bidirule != nil && !p.bidirule(s) {
|
||||
return &labelError{s, "B"}
|
||||
}
|
||||
if !p.validateLabels {
|
||||
return nil
|
||||
}
|
||||
|
||||
+2238
-2158
File diff suppressed because it is too large
Load Diff
+11
-6
@@ -26,9 +26,9 @@ package idna
|
||||
// 15..3 index into xor or mapping table
|
||||
// }
|
||||
// } else {
|
||||
// 15..13 unused
|
||||
// 12 modifier (including virama)
|
||||
// 11 virama modifier
|
||||
// 15..14 unused
|
||||
// 13 mayNeedNorm
|
||||
// 12..11 attributes
|
||||
// 10..8 joining type
|
||||
// 7..3 category type
|
||||
// }
|
||||
@@ -49,15 +49,20 @@ const (
|
||||
joinShift = 8
|
||||
joinMask = 0x07
|
||||
|
||||
viramaModifier = 0x0800
|
||||
// Attributes
|
||||
attributesMask = 0x1800
|
||||
viramaModifier = 0x1800
|
||||
modifier = 0x1000
|
||||
rtl = 0x0800
|
||||
|
||||
mayNeedNorm = 0x2000
|
||||
)
|
||||
|
||||
// A category corresponds to a category defined in the IDNA mapping table.
|
||||
type category uint16
|
||||
|
||||
const (
|
||||
unknown category = 0 // not defined currently in unicode.
|
||||
unknown category = 0 // not currently defined in unicode.
|
||||
mapped category = 1
|
||||
disallowedSTD3Mapped category = 2
|
||||
deviation category = 3
|
||||
@@ -110,5 +115,5 @@ func (c info) isModifier() bool {
|
||||
}
|
||||
|
||||
func (c info) isViramaModifier() bool {
|
||||
return c&(viramaModifier|catSmallMask) == viramaModifier
|
||||
return c&(attributesMask|catSmallMask) == viramaModifier
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user