From f5a20b0a7632fd4f14df43c5fe6820f2fdefc4aa Mon Sep 17 00:00:00 2001 From: Dave Shanley Date: Sun, 30 Apr 2023 12:18:03 -0400 Subject: [PATCH] Addressed #230 and #261 Any reference errors found in the index are rendered by vacuum, so now the rule looks both at orphans, and all index reference errors. Signed-off-by: Dave Shanley --- functions/openapi/unused_component.go | 14 ++ functions/openapi/unused_component_test.go | 189 +++++++++++---------- motor/rule_applicator.go | 2 +- 3 files changed, 110 insertions(+), 95 deletions(-) diff --git a/functions/openapi/unused_component.go b/functions/openapi/unused_component.go index 2c5bd3db..93b90583 100644 --- a/functions/openapi/unused_component.go +++ b/functions/openapi/unused_component.go @@ -139,5 +139,19 @@ func (uc UnusedComponent) RunRule(nodes []*yaml.Node, context model.RuleFunction }) } + // Check for reverse references. This is where a component is referenced, but it does not exist. + refErrors := context.Index.GetReferenceIndexErrors() + for i := range refErrors { + if rErr, ok := refErrors[i].(*index.IndexingError); ok { + results = append(results, model.RuleFunctionResult{ + Message: rErr.Err.Error(), + StartNode: rErr.Node, + EndNode: rErr.Node, + Path: rErr.Path, + Rule: context.Rule, + }) + } + } + return results } diff --git a/functions/openapi/unused_component_test.go b/functions/openapi/unused_component_test.go index 91a923b4..fb842f60 100644 --- a/functions/openapi/unused_component_test.go +++ b/functions/openapi/unused_component_test.go @@ -1,29 +1,29 @@ package openapi import ( - "github.com/daveshanley/vacuum/model" - "github.com/pb33f/libopenapi/datamodel" - "github.com/pb33f/libopenapi/index" - "github.com/pb33f/libopenapi/utils" - "github.com/stretchr/testify/assert" - "gopkg.in/yaml.v3" - "testing" + "github.com/daveshanley/vacuum/model" + "github.com/pb33f/libopenapi/datamodel" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/utils" + "github.com/stretchr/testify/assert" + "gopkg.in/yaml.v3" + "testing" ) func TestUnusedComponent_GetSchema(t *testing.T) { - def := UnusedComponent{} - assert.Equal(t, "unused_component", def.GetSchema().Name) + def := UnusedComponent{} + assert.Equal(t, "unused_component", def.GetSchema().Name) } func TestUnusedComponent_RunRule(t *testing.T) { - def := UnusedComponent{} - res := def.RunRule(nil, model.RuleFunctionContext{}) - assert.Len(t, res, 0) + def := UnusedComponent{} + res := def.RunRule(nil, model.RuleFunctionContext{}) + assert.Len(t, res, 0) } func TestUnusedComponent_RunRule_Success(t *testing.T) { - yml := `paths: + yml := `paths: /naughty/{puppy}: parameters: - $ref: '#/components/parameters/Chewy' @@ -46,28 +46,28 @@ components: in: query name: chewy` - path := "$" + path := "$" - var rootNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &rootNode) - assert.NoError(t, mErr) + var rootNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &rootNode) + assert.NoError(t, mErr) - nodes, _ := utils.FindNodes([]byte(yml), path) + nodes, _ := utils.FindNodes([]byte(yml), path) - rule := buildOpenApiTestRuleAction(path, "unused_component", "", nil) - ctx := buildOpenApiTestContext(model.CastToRuleAction(rule.Then), nil) - config := index.CreateOpenAPIIndexConfig() - ctx.Index = index.NewSpecIndexWithConfig(&rootNode, config) + rule := buildOpenApiTestRuleAction(path, "unused_component", "", nil) + ctx := buildOpenApiTestContext(model.CastToRuleAction(rule.Then), nil) + config := index.CreateOpenAPIIndexConfig() + ctx.Index = index.NewSpecIndexWithConfig(&rootNode, config) - def := UnusedComponent{} - res := def.RunRule(nodes, ctx) + def := UnusedComponent{} + res := def.RunRule(nodes, ctx) - assert.Len(t, res, 0) + assert.Len(t, res, 0) } func TestUnusedComponent_RunRule_SuccessSwaggerSecurity(t *testing.T) { - yml := `swagger: 2.0 + yml := `swagger: 2.0 securityDefinitions: basicAuth: type: basic @@ -85,30 +85,31 @@ paths: security: - sessionAuth: []` - path := "$" + path := "$" - var rootNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &rootNode) - assert.NoError(t, mErr) + var rootNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &rootNode) + assert.NoError(t, mErr) - nodes, _ := utils.FindNodes([]byte(yml), path) + nodes, _ := utils.FindNodes([]byte(yml), path) - rule := buildOpenApiTestRuleAction(path, "unused_component", "", nil) - ctx := buildOpenApiTestContext(model.CastToRuleAction(rule.Then), nil) - config := index.CreateOpenAPIIndexConfig() - ctx.Index = index.NewSpecIndexWithConfig(&rootNode, config) - info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - ctx.SpecInfo = info + rule := buildOpenApiTestRuleAction(path, "unused_component", "", nil) + ctx := buildOpenApiTestContext(model.CastToRuleAction(rule.Then), nil) + config := index.CreateOpenAPIIndexConfig() + ctx.Index = index.NewSpecIndexWithConfig(&rootNode, config) + info, _ := datamodel.ExtractSpecInfo([]byte(yml)) + ctx.SpecInfo = info + ctx.Rule = &rule - def := UnusedComponent{} - res := def.RunRule(nodes, ctx) + def := UnusedComponent{} + res := def.RunRule(nodes, ctx) - assert.Len(t, res, 0) + assert.Len(t, res, 0) } func TestUnusedComponent_RunRule_SuccessOpenAPISecurity(t *testing.T) { - yml := `openapi: 3.0.1 + yml := `openapi: 3.0.1 info: description: A test spec with a security def that is not a ref! security: @@ -117,30 +118,30 @@ components: securitySchemes: SomeSecurity: description: A secure way to do things and stuff.` - path := "$" + path := "$" - var rootNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &rootNode) - assert.NoError(t, mErr) + var rootNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &rootNode) + assert.NoError(t, mErr) - nodes, _ := utils.FindNodes([]byte(yml), path) + nodes, _ := utils.FindNodes([]byte(yml), path) - rule := buildOpenApiTestRuleAction(path, "unused_component", "", nil) - ctx := buildOpenApiTestContext(model.CastToRuleAction(rule.Then), nil) - config := index.CreateOpenAPIIndexConfig() - ctx.Index = index.NewSpecIndexWithConfig(&rootNode, config) - info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - ctx.SpecInfo = info + rule := buildOpenApiTestRuleAction(path, "unused_component", "", nil) + ctx := buildOpenApiTestContext(model.CastToRuleAction(rule.Then), nil) + config := index.CreateOpenAPIIndexConfig() + ctx.Index = index.NewSpecIndexWithConfig(&rootNode, config) + info, _ := datamodel.ExtractSpecInfo([]byte(yml)) + ctx.SpecInfo = info - def := UnusedComponent{} - res := def.RunRule(nodes, ctx) + def := UnusedComponent{} + res := def.RunRule(nodes, ctx) - assert.Len(t, res, 0) + assert.Len(t, res, 0) } func TestUnusedComponent_RunRule_Success_Fail_TwoMissing_Two_Undefined(t *testing.T) { - yml := `parameters: + yml := `parameters: Chewy: description: chewy in: query @@ -165,28 +166,28 @@ components: Kitty: $ref: '#/components/schemas/Puppy' ` - path := "$" + path := "$" - var rootNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &rootNode) - assert.NoError(t, mErr) + var rootNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &rootNode) + assert.NoError(t, mErr) - nodes, _ := utils.FindNodes([]byte(yml), path) + nodes, _ := utils.FindNodes([]byte(yml), path) - rule := buildOpenApiTestRuleAction(path, "unused_component", "", nil) - ctx := buildOpenApiTestContext(model.CastToRuleAction(rule.Then), nil) - config := index.CreateOpenAPIIndexConfig() - ctx.Index = index.NewSpecIndexWithConfig(&rootNode, config) + rule := buildOpenApiTestRuleAction(path, "unused_component", "", nil) + ctx := buildOpenApiTestContext(model.CastToRuleAction(rule.Then), nil) + config := index.CreateOpenAPIIndexConfig() + ctx.Index = index.NewSpecIndexWithConfig(&rootNode, config) - def := UnusedComponent{} - res := def.RunRule(nodes, ctx) + def := UnusedComponent{} + res := def.RunRule(nodes, ctx) - assert.Len(t, res, 2) + assert.Len(t, res, 2) } func TestUnusedComponent_RunRule_Success_Fail_Four_Undefined(t *testing.T) { - yml := `paths: + yml := `paths: /naughty/{puppy}: parameters: - $ref: '#/components/parameters/Chewy' @@ -222,28 +223,28 @@ components: in: query name: chewy` - path := "$" + path := "$" - var rootNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &rootNode) - assert.NoError(t, mErr) + var rootNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &rootNode) + assert.NoError(t, mErr) - nodes, _ := utils.FindNodes([]byte(yml), path) + nodes, _ := utils.FindNodes([]byte(yml), path) - rule := buildOpenApiTestRuleAction(path, "unused_component", "", nil) - ctx := buildOpenApiTestContext(model.CastToRuleAction(rule.Then), nil) - config := index.CreateOpenAPIIndexConfig() - ctx.Index = index.NewSpecIndexWithConfig(&rootNode, config) + rule := buildOpenApiTestRuleAction(path, "unused_component", "", nil) + ctx := buildOpenApiTestContext(model.CastToRuleAction(rule.Then), nil) + config := index.CreateOpenAPIIndexConfig() + ctx.Index = index.NewSpecIndexWithConfig(&rootNode, config) - def := UnusedComponent{} - res := def.RunRule(nodes, ctx) + def := UnusedComponent{} + res := def.RunRule(nodes, ctx) - assert.Len(t, res, 4) + assert.Len(t, res, 4) } func TestUnusedComponent_RunRule_Success_PolymorphicCheck(t *testing.T) { - yml := `paths: + yml := `paths: /naughty/{puppy}: get: responses: @@ -280,21 +281,21 @@ components: type: string description: bunny` - path := "$" + path := "$" - var rootNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &rootNode) - assert.NoError(t, mErr) + var rootNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &rootNode) + assert.NoError(t, mErr) - nodes, _ := utils.FindNodes([]byte(yml), path) + nodes, _ := utils.FindNodes([]byte(yml), path) - rule := buildOpenApiTestRuleAction(path, "unused_component", "", nil) - ctx := buildOpenApiTestContext(model.CastToRuleAction(rule.Then), nil) - config := index.CreateOpenAPIIndexConfig() - ctx.Index = index.NewSpecIndexWithConfig(&rootNode, config) + rule := buildOpenApiTestRuleAction(path, "unused_component", "", nil) + ctx := buildOpenApiTestContext(model.CastToRuleAction(rule.Then), nil) + config := index.CreateOpenAPIIndexConfig() + ctx.Index = index.NewSpecIndexWithConfig(&rootNode, config) - def := UnusedComponent{} - res := def.RunRule(nodes, ctx) + def := UnusedComponent{} + res := def.RunRule(nodes, ctx) - assert.Len(t, res, 0) + assert.Len(t, res, 0) } diff --git a/motor/rule_applicator.go b/motor/rule_applicator.go index 0b5a0e5e..d788477a 100644 --- a/motor/rule_applicator.go +++ b/motor/rule_applicator.go @@ -82,6 +82,7 @@ func ApplyRulesToRuleSet(execution *RuleSetExecution) *RuleSetExecutionResult { // create new configurations config := index.CreateClosedAPIIndexConfig() + config.AllowFileLookup = true docConfig := datamodel.NewClosedDocumentConfiguration() if execution.Base != "" { @@ -96,7 +97,6 @@ func ApplyRulesToRuleSet(execution *RuleSetExecution) *RuleSetExecutionResult { config.BasePath = execution.Base docConfig.BasePath = execution.Base } - config.AllowFileLookup = true config.AllowRemoteLookup = true }