Skip to content

Commit fa47bbf

Browse files
fredbicasualjim
authored andcommitted
Validate examples. Refactoring of validate pkg. (go-openapi#60)
* Validate examples. Refactoring of validate pkg. * Enable travis changes. Clean-up duplicated fixtures.
1 parent 0e805b1 commit fa47bbf

File tree

140 files changed

+13010
-1545
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

140 files changed

+13010
-1545
lines changed

.travis.yml

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1+
#
2+
# Config for 5 Travis CI jobs
3+
#
14
language: go
25
go:
3-
- 1.7
6+
# Test multiple versions
7+
- 1.8
8+
- 1.9
49
install:
510
- go get -u github.com/axw/gocov/gocov
611
- go get -u gopkg.in/matm/v1/gocov-html
@@ -13,8 +18,20 @@ install:
1318
- go get -u github.com/go-openapi/loads
1419
- go get -u github.com/go-openapi/strfmt
1520
- go get -u github.com/go-openapi/runtime
21+
env:
22+
# Short test suite, with race testing
23+
- TEST_COMMAND="go test -v -race -covermode=atomic"
24+
# Long test suite, with maximum coverage
25+
- TEST_COMMAND="go test -v -timeout=20m -cover -coverprofile=coverage.txt -args -enable-long"
26+
# go swagger non-reg as a separate env + exclude 1.8 for this
27+
- TEST_COMMAND="go test -v -timeout=30m -args -enable-go-swagger"
28+
# TODO(TEST): update json-schema-suite benchmark
29+
matrix:
30+
exclude:
31+
- go: 1.8
32+
env: TEST_COMMAND="go test -v -timeout=30m -args -enable-go-swagger"
1633
script:
17-
- go test -v -race -cover -coverprofile=coverage.txt -covermode=atomic
34+
- (eval "$TEST_COMMAND")
1835
after_success:
1936
- bash <(curl -s https://codecov.io/bash)
2037
notifications:

debug.go

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
1+
// Copyright 2015 go-swagger maintainers
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
115
package validate
216

317
import (
@@ -6,12 +20,24 @@ import (
620
)
721

822
var (
9-
// Debug is true when the SWAGGER_DEBUG env var is not empty
23+
// Debug is true when the SWAGGER_DEBUG env var is not empty.
24+
// It enables a more verbose logging of validators.
1025
Debug = os.Getenv("SWAGGER_DEBUG") != ""
1126
)
1227

28+
func init() {
29+
debugOptions()
30+
}
31+
32+
func debugOptions() {
33+
if Debug {
34+
log.SetFlags(log.LstdFlags | log.Lshortfile)
35+
log.SetPrefix("validate")
36+
}
37+
}
38+
1339
func debugLog(msg string, args ...interface{}) {
14-
// a private, trivial trace logger, based on go-openapi/spec/expander.go:debugLog()
40+
// A private, trivial trace logger, based on go-openapi/spec/expander.go:debugLog()
1541
if Debug {
1642
log.Printf(msg, args...)
1743
}

debug_test.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Copyright 2015 go-swagger maintainers
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package validate
16+
17+
import (
18+
"io/ioutil"
19+
"log"
20+
"os"
21+
"sync"
22+
"testing"
23+
24+
"github.com/stretchr/testify/assert"
25+
)
26+
27+
var (
28+
logMutex = &sync.Mutex{}
29+
)
30+
31+
func TestDebug(t *testing.T) {
32+
if !enableLongTests {
33+
skipNotify(t)
34+
t.SkipNow()
35+
}
36+
tmpFile, _ := ioutil.TempFile("", "debug-test")
37+
tmpName := tmpFile.Name()
38+
defer func() {
39+
Debug = false
40+
log.SetOutput(os.Stdout)
41+
// mutex for -race
42+
logMutex.Unlock()
43+
os.Remove(tmpName)
44+
}()
45+
46+
// mutex for -race
47+
logMutex.Lock()
48+
log.SetOutput(tmpFile)
49+
Debug = true
50+
debugOptions()
51+
debugLog("A debug")
52+
Debug = false
53+
tmpFile.Close()
54+
flushed, _ := os.Open(tmpName)
55+
buf := make([]byte, 500)
56+
flushed.Read(buf)
57+
log.SetOutput(os.Stdout)
58+
assert.Contains(t, string(buf), "A debug")
59+
}

default_validator.go

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
// Copyright 2015 go-swagger maintainers
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package validate
16+
17+
import (
18+
"fmt"
19+
20+
"github.com/go-openapi/errors"
21+
"github.com/go-openapi/spec"
22+
)
23+
24+
// defaultValidator validates default values in a spec.
25+
// According to Swagger spec, default values MUST validate their schema.
26+
type defaultValidator struct {
27+
SpecValidator *SpecValidator
28+
}
29+
30+
// Validate validates the default values declared in the swagger spec
31+
func (d *defaultValidator) Validate() (errs *Result) {
32+
errs = new(Result)
33+
if d == nil || d.SpecValidator == nil {
34+
return errs
35+
}
36+
errs.Merge(d.validateDefaultValueValidAgainstSchema()) // error -
37+
return errs
38+
}
39+
40+
func (d *defaultValidator) validateDefaultValueValidAgainstSchema() *Result {
41+
// every default value that is specified must validate against the schema for that property
42+
// headers, items, parameters, schema
43+
44+
res := new(Result)
45+
s := d.SpecValidator
46+
47+
for method, pathItem := range s.analyzer.Operations() {
48+
if pathItem != nil { // Safeguard
49+
for path, op := range pathItem {
50+
// parameters
51+
for _, param := range paramHelp.safeExpandedParamsFor(path, method, op.ID, res, s) {
52+
if param.Default != nil && param.Required {
53+
res.AddWarnings(requiredHasDefaultMsg(param.Name, param.In))
54+
}
55+
56+
// Check simple parameters first
57+
// default values provided must validate against their inline definition (no explicit schema)
58+
if param.Default != nil && param.Schema == nil {
59+
// check param default value is valid
60+
red := NewParamValidator(&param, s.KnownFormats).Validate(param.Default)
61+
if red.HasErrorsOrWarnings() {
62+
res.AddErrors(defaultValueDoesNotValidateMsg(param.Name, param.In))
63+
res.Merge(red)
64+
}
65+
}
66+
67+
// Recursively follows Items and Schemas
68+
if param.Items != nil {
69+
red := d.validateDefaultValueItemsAgainstSchema(param.Name, param.In, &param, param.Items)
70+
if red.HasErrorsOrWarnings() {
71+
res.AddErrors(defaultValueItemsDoesNotValidateMsg(param.Name, param.In))
72+
res.Merge(red)
73+
}
74+
}
75+
76+
if param.Schema != nil {
77+
// Validate default value against schema
78+
red := d.validateDefaultValueSchemaAgainstSchema(param.Name, param.In, param.Schema)
79+
if red.HasErrorsOrWarnings() {
80+
res.AddErrors(defaultValueDoesNotValidateMsg(param.Name, param.In))
81+
res.Merge(red)
82+
}
83+
}
84+
}
85+
86+
if op.Responses != nil {
87+
if op.Responses.Default != nil {
88+
// Same constraint on default Response
89+
res.Merge(d.validateDefaultInResponse(op.Responses.Default, "default", path, 0, op.ID))
90+
}
91+
// Same constraint on regular Responses
92+
if op.Responses.StatusCodeResponses != nil { // Safeguard
93+
for code, r := range op.Responses.StatusCodeResponses {
94+
res.Merge(d.validateDefaultInResponse(&r, "response", path, code, op.ID))
95+
}
96+
}
97+
} else {
98+
// Empty op.ID means there is no meaningful operation: no need to report a specific message
99+
if op.ID != "" {
100+
res.AddErrors(noValidResponseMsg(op.ID))
101+
}
102+
}
103+
}
104+
}
105+
}
106+
if s.spec.Spec().Definitions != nil { // Safeguard
107+
for nm, sch := range s.spec.Spec().Definitions {
108+
res.Merge(d.validateDefaultValueSchemaAgainstSchema(fmt.Sprintf("definitions.%s", nm), "body", &sch))
109+
}
110+
}
111+
return res
112+
}
113+
114+
func (d *defaultValidator) validateDefaultInResponse(resp *spec.Response, responseType, path string, responseCode int, operationID string) *Result {
115+
s := d.SpecValidator
116+
117+
response, res := responseHelp.expandResponseRef(resp, path, s)
118+
if !res.IsValid() {
119+
return res
120+
}
121+
122+
responseName, responseCodeAsStr := responseHelp.responseMsgVariants(responseType, responseCode)
123+
124+
if response.Headers != nil { // Safeguard
125+
for nm, h := range response.Headers {
126+
if h.Default != nil {
127+
red := NewHeaderValidator(nm, &h, s.KnownFormats).Validate(h.Default)
128+
if red.HasErrorsOrWarnings() {
129+
res.AddErrors(defaultValueHeaderDoesNotValidateMsg(operationID, nm, responseName))
130+
res.Merge(red)
131+
}
132+
}
133+
134+
// Headers have inline definition, like params
135+
if h.Items != nil {
136+
red := d.validateDefaultValueItemsAgainstSchema(nm, "header", &h, h.Items)
137+
if red.HasErrorsOrWarnings() {
138+
res.AddErrors(defaultValueHeaderItemsDoesNotValidateMsg(operationID, nm, responseName))
139+
res.Merge(red)
140+
}
141+
}
142+
143+
if _, err := compileRegexp(h.Pattern); err != nil {
144+
res.AddErrors(invalidPatternInHeaderMsg(operationID, nm, responseName, h.Pattern, err))
145+
}
146+
147+
// Headers don't have schema
148+
}
149+
}
150+
if response.Schema != nil {
151+
red := d.validateDefaultValueSchemaAgainstSchema(responseCodeAsStr, "response", response.Schema)
152+
if red.HasErrorsOrWarnings() {
153+
// Additional message to make sure the context of the error is not lost
154+
res.AddErrors(defaultValueInDoesNotValidateMsg(operationID, responseName))
155+
res.Merge(red)
156+
}
157+
}
158+
return res
159+
}
160+
161+
func (d *defaultValidator) validateDefaultValueSchemaAgainstSchema(path, in string, schema *spec.Schema) *Result {
162+
res := new(Result)
163+
s := d.SpecValidator
164+
if schema != nil { // Safeguard
165+
if schema.Default != nil {
166+
res.Merge(NewSchemaValidator(schema, s.spec.Spec(), path+".default", s.KnownFormats).Validate(schema.Default))
167+
}
168+
if schema.Items != nil {
169+
if schema.Items.Schema != nil {
170+
res.Merge(d.validateDefaultValueSchemaAgainstSchema(path+".items.default", in, schema.Items.Schema))
171+
}
172+
// Multiple schemas in items
173+
if schema.Items.Schemas != nil { // Safeguard
174+
for i, sch := range schema.Items.Schemas {
175+
res.Merge(d.validateDefaultValueSchemaAgainstSchema(fmt.Sprintf("%s.items[%d].default", path, i), in, &sch))
176+
}
177+
}
178+
}
179+
if _, err := compileRegexp(schema.Pattern); err != nil {
180+
res.AddErrors(errors.New(errors.CompositeErrorCode, "%s in %s has invalid pattern: %q", path, in, schema.Pattern))
181+
}
182+
if schema.AdditionalItems != nil && schema.AdditionalItems.Schema != nil {
183+
// NOTE: we keep validating values, even though additionalItems is not supported by Swagger 2.0 (and 3.0 as well)
184+
res.Merge(d.validateDefaultValueSchemaAgainstSchema(fmt.Sprintf("%s.additionalItems", path), in, schema.AdditionalItems.Schema))
185+
}
186+
for propName, prop := range schema.Properties {
187+
res.Merge(d.validateDefaultValueSchemaAgainstSchema(path+"."+propName, in, &prop))
188+
}
189+
for propName, prop := range schema.PatternProperties {
190+
res.Merge(d.validateDefaultValueSchemaAgainstSchema(path+"."+propName, in, &prop))
191+
}
192+
if schema.AdditionalProperties != nil && schema.AdditionalProperties.Schema != nil {
193+
res.Merge(d.validateDefaultValueSchemaAgainstSchema(fmt.Sprintf("%s.additionalProperties", path), in, schema.AdditionalProperties.Schema))
194+
}
195+
if schema.AllOf != nil {
196+
for i, aoSch := range schema.AllOf {
197+
res.Merge(d.validateDefaultValueSchemaAgainstSchema(fmt.Sprintf("%s.allOf[%d]", path, i), in, &aoSch))
198+
}
199+
}
200+
}
201+
return res
202+
}
203+
204+
func (d *defaultValidator) validateDefaultValueItemsAgainstSchema(path, in string, root interface{}, items *spec.Items) *Result {
205+
res := new(Result)
206+
s := d.SpecValidator
207+
if items != nil {
208+
if items.Default != nil {
209+
res.Merge(newItemsValidator(path, in, items, root, s.KnownFormats).Validate(0, items.Default))
210+
}
211+
if items.Items != nil {
212+
res.Merge(d.validateDefaultValueItemsAgainstSchema(path+"[0].default", in, root, items.Items))
213+
}
214+
if _, err := compileRegexp(items.Pattern); err != nil {
215+
res.AddErrors(invalidPatternInMsg(path, in, items.Pattern))
216+
}
217+
}
218+
return res
219+
}

0 commit comments

Comments
 (0)