diff --git a/README.md b/README.md index 3e70d8fa..ba9530cb 100644 --- a/README.md +++ b/README.md @@ -99,7 +99,14 @@ come say hi! ## Documentation -🔥 **New in** `v0.12+` 🔥 : **Core functions support JSON Path**. +🔥 **New in** `v0.14+` 🔥 : **Engine Speedup**. + +**Speed!** We've made some significant improvements to how efficiently large documents are walked +Which means vacuum is now faster than ever. + +--- + +`v0.12+` : Core functions support JSON Path. Now all **core** functions return the **correct and accurate JSON path for each linting result**. Previously this was not possible at all, but with some clever engineering, we have made it happen. It's a small thing, but with huge impact. diff --git a/cmd/vacuum_report.go b/cmd/vacuum_report.go index 80ed427d..62f33081 100644 --- a/cmd/vacuum_report.go +++ b/cmd/vacuum_report.go @@ -263,12 +263,15 @@ func GetVacuumReportCommand() *cobra.Command { return err } - pterm.Success.Printf("Report generated for '%s', written to '%s'\n", args[0], reportOutputName) + if len(args) > 0 { + pterm.Success.Printf("Report generated for '%s', written to '%s'\n", args[0], reportOutputName) + fi, _ := os.Stat(args[0]) + RenderTime(timeFlag, duration, fi.Size()) + } else { + pterm.Success.Printf("Report generated, written to '%s'\n", reportOutputName) + } pterm.Println() - fi, _ := os.Stat(args[0]) - RenderTime(timeFlag, duration, fi.Size()) - return nil }, } diff --git a/functions/openapi/examples_missing.go b/functions/openapi/examples_missing.go index 7c7fc317..128e984e 100644 --- a/functions/openapi/examples_missing.go +++ b/functions/openapi/examples_missing.go @@ -7,8 +7,6 @@ import ( "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/datamodel/low" "gopkg.in/yaml.v3" "slices" ) @@ -79,7 +77,8 @@ 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. - h := p.Value.GoLow().Hash() + var h [32]byte + copy(h[:], p.GenerateJSONPath()) if _, ok := seen[h]; !ok { seen[h] = true } @@ -101,7 +100,8 @@ 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. - h := p.Value.GoLow().Hash() + var h [32]byte + copy(h[:], p.GenerateJSONPath()) if _, ok := seen[h]; !ok { seen[h] = true } @@ -112,13 +112,13 @@ func (em ExamplesMissing) RunRule(_ []*yaml.Node, context model.RuleFunctionCont if context.DrDocument.Headers != nil { for i := range context.DrDocument.Headers { h := context.DrDocument.Headers[i] - if h == nil || h.SchemaProxy == nil { + if h == nil || h.Schema == nil { continue } - if h.SchemaProxy.Schema != nil && isSchemaBoolean(h.SchemaProxy.Schema) { + if h.Schema.Schema != nil && isSchemaBoolean(h.Schema.Schema) { continue } - if h.SchemaProxy != nil && (h.SchemaProxy.Schema.Value.Examples != nil || h.SchemaProxy.Schema.Value.Example != nil) { + if h.Schema != nil && (h.Schema.Schema.Value.Examples != nil || h.Schema.Schema.Value.Example != nil) { continue } if h.Value.Examples.Len() <= 0 && isExampleNodeNull([]*yaml.Node{h.Value.Example}) { @@ -134,7 +134,8 @@ func (em ExamplesMissing) RunRule(_ []*yaml.Node, context model.RuleFunctionCont n, h)) } else { // add to seen elements, so when checking schemas we can mark them as good. - hs := h.Value.GoLow().Hash() + var hs [32]byte + copy(hs[:], h.GenerateJSONPath()) if _, ok := seen[hs]; !ok { seen[hs] = true } @@ -170,7 +171,9 @@ func (em ExamplesMissing) RunRule(_ []*yaml.Node, context model.RuleFunctionCont n, mt)) } else { // add to seen elements, so when checking schemas we can mark them as good. - h := mt.Value.GoLow().Hash() + //h := mt.Value.GoLow().Hash() + var h [32]byte + copy(h[:], mt.GenerateJSONPath()) if _, ok := seen[h]; !ok { seen[h] = true } @@ -203,11 +206,9 @@ func (em ExamplesMissing) RunRule(_ []*yaml.Node, context model.RuleFunctionCont func extractHash(s *base.Schema) [32]byte { if s != nil && s.Parent != nil { if p := s.Parent.(base.Foundational).GetParent(); p != nil { - if v := p.(base.HasValue).GetValue(); v != nil { - if g, ok := v.(high.GoesLowUntyped); ok { - return g.GoLowUntyped().(low.Hashable).Hash() - } - } + var arr [32]byte + copy(arr[:], p.GenerateJSONPath()) + return arr } } return [32]byte{} diff --git a/functions/openapi/examples_schema.go b/functions/openapi/examples_schema.go index 09cf25a4..d4dbfbcd 100644 --- a/functions/openapi/examples_schema.go +++ b/functions/openapi/examples_schema.go @@ -224,14 +224,14 @@ func (es ExamplesSchema) RunRule(_ []*yaml.Node, context model.RuleFunctionConte for i := range context.DrDocument.Headers { h := context.DrDocument.Headers[i] wg.Go(func() { - if h.Value.Examples.Len() >= 1 && h.SchemaProxy != nil { + if h.Value.Examples.Len() >= 1 && h.Schema != nil { expLock.Lock() - results = append(results, parseExamples(h.SchemaProxy.Schema, h, h.Value.Examples)...) + results = append(results, parseExamples(h.Schema.Schema, h, h.Value.Examples)...) expLock.Unlock() } else { - if h.Value.Example != nil && h.SchemaProxy != nil { + if h.Value.Example != nil && h.Schema != nil { expLock.Lock() - results = append(results, parseExample(h.SchemaProxy.Schema, h.Value.Example, + results = append(results, parseExample(h.Schema.Schema, h.Value.Example, h.Value.GoLow().Example.GetKeyNode())...) expLock.Unlock() } diff --git a/go.mod b/go.mod index 780950b6..cfff750f 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.23.0 require ( github.com/alecthomas/chroma v0.10.0 - github.com/dop251/goja v0.0.0-20240927123429-241b342198c2 + github.com/dop251/goja v0.0.0-20241009100908-5f46f2705ca3 github.com/dop251/goja_nodejs v0.0.0-20240728170619-29b559befffc github.com/dustin/go-humanize v1.0.1 github.com/fsnotify/fsnotify v1.7.0 @@ -12,8 +12,8 @@ require ( github.com/gizak/termui/v3 v3.1.0 github.com/json-iterator/go v1.1.12 github.com/mitchellh/mapstructure v1.5.0 - github.com/pb33f/doctor v0.0.13 - github.com/pb33f/libopenapi v0.18.2 + github.com/pb33f/doctor v0.0.14 + github.com/pb33f/libopenapi v0.18.3 github.com/pb33f/libopenapi-validator v0.2.0 github.com/pterm/pterm v0.12.79 github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 @@ -24,8 +24,8 @@ require ( github.com/stretchr/testify v1.9.0 github.com/tliron/glsp v0.2.2 github.com/vmware-labs/yaml-jsonpath v0.3.2 - golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 - golang.org/x/text v0.18.0 + golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c + golang.org/x/text v0.19.0 gopkg.in/yaml.v3 v3.0.1 ) @@ -36,7 +36,7 @@ require ( github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/buger/jsonparser v1.1.1 // indirect - github.com/containerd/console v1.0.3 // indirect + github.com/containerd/console v1.0.4 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dlclark/regexp2 v1.11.4 // indirect github.com/dprotaso/go-yit v0.0.0-20240618133044-5a0af90af097 // indirect @@ -79,7 +79,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect golang.org/x/crypto v0.27.0 // indirect golang.org/x/net v0.29.0 // indirect - golang.org/x/sys v0.25.0 // indirect + golang.org/x/sys v0.26.0 // indirect golang.org/x/term v0.24.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index 34b922e8..a2fec3c4 100644 --- a/go.sum +++ b/go.sum @@ -29,8 +29,9 @@ github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx2 github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw= github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= +github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro= +github.com/containerd/console v1.0.4/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -39,8 +40,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8Yc github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo= github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/dop251/goja v0.0.0-20240927123429-241b342198c2 h1:Ux9RXuPQmTB4C1MKagNLme0krvq8ulewfor+ORO/QL4= -github.com/dop251/goja v0.0.0-20240927123429-241b342198c2/go.mod h1:MxLav0peU43GgvwVgNbLAj1s/bSGboKkhuULvq/7hx4= +github.com/dop251/goja v0.0.0-20241009100908-5f46f2705ca3 h1:MXsAuToxwsTn5BEEYm2DheqIiC4jWGmkEJ1uy+KFhvQ= +github.com/dop251/goja v0.0.0-20241009100908-5f46f2705ca3/go.mod h1:MxLav0peU43GgvwVgNbLAj1s/bSGboKkhuULvq/7hx4= github.com/dop251/goja_nodejs v0.0.0-20240728170619-29b559befffc h1:MKYt39yZJi0Z9xEeRmDX2L4ocE0ETKcHKw6MVL3R+co= github.com/dop251/goja_nodejs v0.0.0-20240728170619-29b559befffc/go.mod h1:VULptt4Q/fNzQUJlqY/GP3qHyU7ZH46mFkBZe0ZTokU= github.com/dprotaso/go-yit v0.0.0-20191028211022-135eb7262960/go.mod h1:9HQzr9D/0PGwMEbC3d5AB7oi67+h4TsQqItC1GVYG58= @@ -157,10 +158,10 @@ github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1y github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= 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.13 h1:UQkJsNcEIigf+DWuQY2Tx4usrUbCWOXo5vFRmrM/EsA= -github.com/pb33f/doctor v0.0.13/go.mod h1:tXRAyGStCi0QXiVC20aGsH9iPRyjhzGz/bz4IBERmNE= -github.com/pb33f/libopenapi v0.18.2 h1:y9yfbrD3+vceKc/3lIndNF+CSZPXRLuYfAlbliBzfjQ= -github.com/pb33f/libopenapi v0.18.2/go.mod h1:9ap4lXBHgxGyFwxtOfa+B1C3IQ0rvnqteqjJvJ11oiQ= +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-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= @@ -247,8 +248,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= -golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk= -golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY= +golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY= +golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= @@ -289,10 +290,11 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= -golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -306,8 +308,8 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= -golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= diff --git a/model/results.go b/model/results.go index 0923c4c1..43f824c2 100644 --- a/model/results.go +++ b/model/results.go @@ -350,6 +350,9 @@ func (rr *RuleResultSet) CalculateCategoryHealth(category string) int { func (rr *RuleResultSet) PrepareForSerialization(info *datamodel.SpecInfo) { var wg sync.WaitGroup + if rr == nil || info == nil { + return + } wg.Add(len(rr.Results)) data := strings.Split(string(*info.SpecBytes), "\n") diff --git a/motor/rule_applicator_test.go b/motor/rule_applicator_test.go index e9c5d8b4..80499bbe 100644 --- a/motor/rule_applicator_test.go +++ b/motor/rule_applicator_test.go @@ -2303,7 +2303,7 @@ paths: content: application/json: schema: - $ref: "https://raw.githubusercontent.com/OAI/OpenAPI-Specification/main/schemas/v3.0/schema.json" + $ref: "https://raw.githubusercontent.com/OAI/OpenAPI-Specification/refs/heads/main/schemas/v3.0/schema.yaml" ` rules := make(map[string]*model.Rule)