From bf947064c6d3f9c4c8cfacd7a519c939ab49d531 Mon Sep 17 00:00:00 2001 From: quobix Date: Sun, 20 Oct 2024 15:45:44 -0400 Subject: [PATCH] Fixed inconsistent example code the JSON path issue in the doctor still needs resolving, but for now this keeps everything consistent for vacuum. --- functions/openapi/examples_missing.go | 75 ++++++++++++++-------- functions/openapi/examples_missing_test.go | 2 +- functions/openapi/examples_schema.go | 2 +- go.mod | 2 +- go.sum | 4 +- 5 files changed, 55 insertions(+), 30 deletions(-) diff --git a/functions/openapi/examples_missing.go b/functions/openapi/examples_missing.go index 128e984e..b11a955e 100644 --- a/functions/openapi/examples_missing.go +++ b/functions/openapi/examples_missing.go @@ -4,11 +4,16 @@ package openapi import ( + "context" + "fmt" "github.com/daveshanley/vacuum/model" vacuumUtils "github.com/daveshanley/vacuum/utils" "github.com/pb33f/doctor/model/high/base" + "github.com/pb33f/libopenapi/datamodel/high" + "github.com/pb33f/libopenapi/index" "gopkg.in/yaml.v3" "slices" + "strings" ) // ExamplesMissing will check anything that can have an example, has one. @@ -34,6 +39,9 @@ func (em ExamplesMissing) RunRule(_ []*yaml.Node, context model.RuleFunctionCont return results } + // create a string buffer for caching seen schemas + var buf strings.Builder + buildResult := func(message, path string, node *yaml.Node, component base.AcceptsRuleResults) model.RuleFunctionResult { result := model.RuleFunctionResult{ Message: message, @@ -58,7 +66,7 @@ func (em ExamplesMissing) RunRule(_ []*yaml.Node, context model.RuleFunctionCont return false } - seen := make(map[[32]byte]bool) + seen := make(map[string]bool) if context.DrDocument.Parameters != nil { paramClear: @@ -77,11 +85,12 @@ func (em ExamplesMissing) RunRule(_ []*yaml.Node, context model.RuleFunctionCont v := con.Value() if v.Examples != nil && (p.Examples == nil || p.Examples.Len() >= 0) { // add to seen elements, so when checking schemas we can mark them as good. - var h [32]byte - copy(h[:], p.GenerateJSONPath()) - if _, ok := seen[h]; !ok { - seen[h] = true + buf.WriteString(fmt.Sprintf("%s:%d:%d", p.Value.GoLow().GetIndex().GetSpecAbsolutePath(), + p.Value.GoLow().KeyNode.Line, p.Value.GoLow().KeyNode.Column)) + if _, ok := seen[buf.String()]; !ok { + seen[buf.String()] = true } + buf.Reset() break paramClear } } @@ -100,11 +109,12 @@ func (em ExamplesMissing) RunRule(_ []*yaml.Node, context model.RuleFunctionCont n, p)) } else { // add to seen elements, so when checking schemas we can mark them as good. - var h [32]byte - copy(h[:], p.GenerateJSONPath()) - if _, ok := seen[h]; !ok { - seen[h] = true + buf.WriteString(fmt.Sprintf("%s:%d:%d", p.Value.GoLow().GetIndex().GetSpecAbsolutePath(), + p.Value.GoLow().KeyNode.Line, p.Value.GoLow().KeyNode.Column)) + if _, ok := seen[buf.String()]; !ok { + seen[buf.String()] = true } + buf.Reset() } } } @@ -133,12 +143,12 @@ func (em ExamplesMissing) RunRule(_ []*yaml.Node, context model.RuleFunctionCont h.GenerateJSONPath(), n, h)) } else { - // add to seen elements, so when checking schemas we can mark them as good. - var hs [32]byte - copy(hs[:], h.GenerateJSONPath()) - if _, ok := seen[hs]; !ok { - seen[hs] = true + buf.WriteString(fmt.Sprintf("%s:%d:%d", h.Value.GoLow().GetIndex().GetSpecAbsolutePath(), + h.Value.GoLow().KeyNode.Line, h.Value.GoLow().KeyNode.Column)) + if _, ok := seen[buf.String()]; !ok { + seen[buf.String()] = true } + buf.Reset() } } } @@ -170,13 +180,12 @@ func (em ExamplesMissing) RunRule(_ []*yaml.Node, context model.RuleFunctionCont mt.GenerateJSONPath(), n, mt)) } else { - // add to seen elements, so when checking schemas we can mark them as good. - //h := mt.Value.GoLow().Hash() - var h [32]byte - copy(h[:], mt.GenerateJSONPath()) - if _, ok := seen[h]; !ok { - seen[h] = true + buf.WriteString(fmt.Sprintf("%s:%d:%d", mt.Value.GoLow().GetIndex().GetSpecAbsolutePath(), + mt.Value.GoLow().KeyNode.Line, mt.Value.GoLow().KeyNode.Column)) + if _, ok := seen[buf.String()]; !ok { + seen[buf.String()] = true } + buf.Reset() } } } @@ -200,18 +209,34 @@ func (em ExamplesMissing) RunRule(_ []*yaml.Node, context model.RuleFunctionCont } } } + seen = nil + buf.Reset() return results } -func extractHash(s *base.Schema) [32]byte { +type contextualPosition interface { + GetIndex() *index.SpecIndex + GetContext() context.Context + GetKeyNode() *yaml.Node +} + +func extractHash(s *base.Schema) string { if s != nil && s.Parent != nil { if p := s.Parent.(base.Foundational).GetParent(); p != nil { - var arr [32]byte - copy(arr[:], p.GenerateJSONPath()) - return arr + // check if p implements HasValue + if hv, ok := p.(base.HasValue); ok { + // check if hv.Value implements GoesLowUntyped + if gl, ko := hv.GetValue().(high.GoesLowUntyped); ko { + // check if gl.GoesLowUntyped() implements contextualPosition + if cp, kk := gl.GoLowUntyped().(contextualPosition); kk { + return fmt.Sprintf("%s:%d:%d", cp.GetIndex().GetSpecAbsolutePath(), + cp.GetKeyNode().Line, cp.GetKeyNode().Column) + } + } + } } } - return [32]byte{} + return "" } func isSchemaBoolean(schema *base.Schema) bool { diff --git a/functions/openapi/examples_missing_test.go b/functions/openapi/examples_missing_test.go index 35a7098c..b3e24bc8 100644 --- a/functions/openapi/examples_missing_test.go +++ b/functions/openapi/examples_missing_test.go @@ -180,7 +180,7 @@ components: assert.Len(t, res, 3) assert.Equal(t, "schema is missing `examples` or `example`", res[0].Message) - assert.Contains(t, res[1].Path, "$.components.schemas['Pizza'].properties") + assert.Contains(t, res[1].Path, "$.components.schemas['Pizza']") } func TestExamplesMissing_Header(t *testing.T) { diff --git a/functions/openapi/examples_schema.go b/functions/openapi/examples_schema.go index d4dbfbcd..4604fd8d 100644 --- a/functions/openapi/examples_schema.go +++ b/functions/openapi/examples_schema.go @@ -84,7 +84,7 @@ func (es ExamplesSchema) RunRule(_ []*yaml.Node, context model.RuleFunctionConte for _, r := range validationErrors { for _, err := range r.SchemaValidationErrors { result := buildResult(vacuumUtils.SuppliedOrDefault(context.Rule.Message, err.Reason), - path, keyNode, node, obj) + path, keyNode, node, s) banned := false for g := range bannedErrors { diff --git a/go.mod b/go.mod index cfff750f..a783e810 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/json-iterator/go v1.1.12 github.com/mitchellh/mapstructure v1.5.0 github.com/pb33f/doctor v0.0.14 - github.com/pb33f/libopenapi v0.18.3 + github.com/pb33f/libopenapi v0.18.4 github.com/pb33f/libopenapi-validator v0.2.0 github.com/pterm/pterm v0.12.79 github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 diff --git a/go.sum b/go.sum index a2fec3c4..b79139fe 100644 --- a/go.sum +++ b/go.sum @@ -160,8 +160,8 @@ github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw= github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= github.com/pb33f/doctor v0.0.14 h1:Yei7f6Ksuc9RBjN3umiCEI8zN7ti3s5aveTT3sLi8Ts= github.com/pb33f/doctor v0.0.14/go.mod h1:1afDRJqurrftgMfRDbf1jWPoOyvdAsaK8+GHD8MiTLI= -github.com/pb33f/libopenapi v0.18.3 h1:j4lm8xMM/GYSj2M8S7qNwZ//rOtEK5ACEiuNC7mTJzE= -github.com/pb33f/libopenapi v0.18.3/go.mod h1:9ap4lXBHgxGyFwxtOfa+B1C3IQ0rvnqteqjJvJ11oiQ= +github.com/pb33f/libopenapi v0.18.4 h1:b39Spb8CY0jzormHkB2+U/UtaKegicHHJRY7c9PlTDQ= +github.com/pb33f/libopenapi v0.18.4/go.mod h1:9ap4lXBHgxGyFwxtOfa+B1C3IQ0rvnqteqjJvJ11oiQ= github.com/pb33f/libopenapi-validator v0.2.0 h1:bixMgyHXRenN60L8927GtsWM8AE/p3fGvQdn6ZkgNHM= github.com/pb33f/libopenapi-validator v0.2.0/go.mod h1:DpluvEfTfDwfqTN2sgvTH14dPbzKDtE/vYQgcx3iNMs= github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=