-
-
Notifications
You must be signed in to change notification settings - Fork 51
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a separate OAS3NoRefSiblingsRule
- Loading branch information
1 parent
0d8dd6e
commit fac81e8
Showing
9 changed files
with
371 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
package openapi | ||
|
||
import ( | ||
"strings" | ||
|
||
"github.com/daveshanley/vacuum/model" | ||
vacuumUtils "github.com/daveshanley/vacuum/utils" | ||
"github.com/pb33f/libopenapi/utils" | ||
"gopkg.in/yaml.v3" | ||
) | ||
|
||
// OASNoRefSiblings validates that no properties other than `description` and `summary` are added alongside a `$ref`. | ||
// This rule helps ensure that only essential properties are attached to `$ref` nodes, preventing unnecessary and unused additions. | ||
type OASNoRefSiblings struct { | ||
} | ||
|
||
// GetCategory returns the category of the OASNoRefSiblings rule. | ||
func (nrs OASNoRefSiblings) GetCategory() string { | ||
return model.FunctionCategoryOpenAPI | ||
} | ||
|
||
// GetSchema returns a model.RuleFunctionSchema defining the schema of the OASNoRefSiblings rule. | ||
func (nrs OASNoRefSiblings) GetSchema() model.RuleFunctionSchema { | ||
return model.RuleFunctionSchema{ | ||
Name: "oasRefSiblings", | ||
} | ||
} | ||
|
||
func notAllowedKeys(node *yaml.Node) []string { | ||
var notAllowedKeys []string | ||
|
||
for i := 0; i < len(node.Content); i += 2 { | ||
key := node.Content[i].Value | ||
switch key { | ||
case "$ref", "summary", "description": | ||
continue | ||
default: | ||
notAllowedKeys = append(notAllowedKeys, key) | ||
} | ||
} | ||
return notAllowedKeys | ||
} | ||
|
||
// RunRule will execute the OASNoRefSiblings rule, based on supplied context and a supplied []*yaml.Node slice. | ||
func (nrs OASNoRefSiblings) RunRule(nodes []*yaml.Node, context model.RuleFunctionContext) []model.RuleFunctionResult { | ||
|
||
if len(nodes) <= 0 { | ||
return nil | ||
} | ||
|
||
var results []model.RuleFunctionResult | ||
siblings := context.Index.GetReferencesWithSiblings() | ||
for _, ref := range siblings { | ||
notAllowedKeys := notAllowedKeys(ref.Node) | ||
if len(notAllowedKeys) != 0 { | ||
key, val := utils.FindKeyNode("$ref", ref.Node.Content) | ||
results = append(results, model.RuleFunctionResult{ | ||
Message: "a `$ref` can only be placed next to `summary` and `description` but got:" + strings.Join(notAllowedKeys, " ,"), | ||
StartNode: key, | ||
EndNode: vacuumUtils.BuildEndNode(val), | ||
Path: ref.Path, | ||
Rule: context.Rule, | ||
}) | ||
} | ||
} | ||
return results | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,264 @@ | ||
package openapi | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/daveshanley/vacuum/model" | ||
"github.com/pb33f/libopenapi/index" | ||
"github.com/pb33f/libopenapi/utils" | ||
"github.com/stretchr/testify/assert" | ||
"gopkg.in/yaml.v3" | ||
) | ||
|
||
func TestOASNoRefSiblings_GetSchema(t *testing.T) { | ||
def := OASNoRefSiblings{} | ||
assert.Equal(t, "oasRefSiblings", def.GetSchema().Name) | ||
} | ||
|
||
func TestOASNoRefSiblings_RunRule(t *testing.T) { | ||
def := OASNoRefSiblings{} | ||
res := def.RunRule(nil, model.RuleFunctionContext{}) | ||
assert.Len(t, res, 0) | ||
} | ||
|
||
func TestOASNoRefSiblings_RunRule_Description(t *testing.T) { | ||
|
||
yml := `paths: | ||
/nice/{rice}: | ||
requestBody: | ||
content: | ||
application/json: | ||
schema: | ||
description: this is a good place to do this | ||
$ref: '#/components/schemas/Rice' | ||
/hot/{dog}: | ||
requestBody: | ||
content: | ||
application/json: | ||
schema: | ||
description: Still a good place to do this | ||
$ref: '#/components/schemas/Dog'` | ||
|
||
path := "$" | ||
|
||
var rootNode yaml.Node | ||
mErr := yaml.Unmarshal([]byte(yml), &rootNode) | ||
assert.NoError(t, mErr) | ||
nodes, _ := utils.FindNodes([]byte(yml), path) | ||
|
||
rule := buildOpenApiTestRuleAction(path, "oas3_no_ref_siblings", "", nil) | ||
ctx := buildOpenApiTestContext(model.CastToRuleAction(rule.Then), nil) | ||
config := index.CreateOpenAPIIndexConfig() | ||
ctx.Index = index.NewSpecIndexWithConfig(&rootNode, config) | ||
|
||
def := OASNoRefSiblings{} | ||
res := def.RunRule(nodes, ctx) | ||
|
||
assert.Len(t, res, 0) | ||
} | ||
|
||
func TestOASNoRefSiblings_RunRule_Deprecated_Example(t *testing.T) { | ||
|
||
yml := `paths: | ||
/nice/{rice}: | ||
requestBody: | ||
content: | ||
application/json: | ||
schema: | ||
description: the deprecated flag should not be here | ||
deprecated: true | ||
$ref: '#/components/schemas/Rice' | ||
/hot/{dog}: | ||
requestBody: | ||
content: | ||
application/json: | ||
schema: | ||
example: this should also not be here | ||
$ref: '#/components/schemas/Dog'` | ||
|
||
path := "$" | ||
|
||
var rootNode yaml.Node | ||
mErr := yaml.Unmarshal([]byte(yml), &rootNode) | ||
assert.NoError(t, mErr) | ||
nodes, _ := utils.FindNodes([]byte(yml), path) | ||
|
||
rule := buildOpenApiTestRuleAction(path, "oas3_no_ref_siblings", "", nil) | ||
ctx := buildOpenApiTestContext(model.CastToRuleAction(rule.Then), nil) | ||
config := index.CreateOpenAPIIndexConfig() | ||
ctx.Index = index.NewSpecIndexWithConfig(&rootNode, config) | ||
|
||
def := OASNoRefSiblings{} | ||
res := def.RunRule(nodes, ctx) | ||
|
||
assert.Len(t, res, 2) | ||
} | ||
|
||
func TestOASNoRefSiblings_RunRule_Components(t *testing.T) { | ||
|
||
yml := `components: | ||
schemas: | ||
Beer: | ||
description: perfect | ||
$ref: '#/components/Yum' | ||
Bottle: | ||
type: string | ||
Cake: | ||
$ref: '#/components/Sugar'` | ||
|
||
path := "$" | ||
|
||
var rootNode yaml.Node | ||
mErr := yaml.Unmarshal([]byte(yml), &rootNode) | ||
assert.NoError(t, mErr) | ||
|
||
nodes, _ := utils.FindNodes([]byte(yml), path) | ||
|
||
rule := buildOpenApiTestRuleAction(path, "oas3_no_ref_siblings", "", nil) | ||
ctx := buildOpenApiTestContext(model.CastToRuleAction(rule.Then), nil) | ||
config := index.CreateOpenAPIIndexConfig() | ||
ctx.Index = index.NewSpecIndexWithConfig(&rootNode, config) | ||
|
||
def := OASNoRefSiblings{} | ||
res := def.RunRule(nodes, ctx) | ||
|
||
assert.Len(t, res, 0) | ||
|
||
} | ||
|
||
func TestOASNoRefSiblings_RunRule_Parameters(t *testing.T) { | ||
|
||
yml := `parameters: | ||
testParam: | ||
$ref: '#/parameters/oldParam' | ||
oldParam: | ||
in: query | ||
description: old | ||
wrongParam: | ||
description: I am allowed to be here | ||
$ref: '#/parameters/oldParam'` | ||
|
||
path := "$" | ||
|
||
var rootNode yaml.Node | ||
mErr := yaml.Unmarshal([]byte(yml), &rootNode) | ||
assert.NoError(t, mErr) | ||
|
||
nodes, _ := utils.FindNodes([]byte(yml), path) | ||
|
||
rule := buildOpenApiTestRuleAction(path, "oas3_no_ref_siblings", "", nil) | ||
ctx := buildOpenApiTestContext(model.CastToRuleAction(rule.Then), nil) | ||
config := index.CreateOpenAPIIndexConfig() | ||
ctx.Index = index.NewSpecIndexWithConfig(&rootNode, config) | ||
|
||
def := OASNoRefSiblings{} | ||
res := def.RunRule(nodes, ctx) | ||
|
||
assert.Len(t, res, 0) | ||
|
||
} | ||
|
||
func TestOASNoRefSiblings_RunRule_Definitions(t *testing.T) { | ||
|
||
yml := `definitions: | ||
test: | ||
$ref: '#/definitions/old' | ||
old: | ||
type: object | ||
description: old | ||
wrong: | ||
description: I am allowed to be here | ||
$ref: '#/definitions/oldParam'` | ||
|
||
path := "$" | ||
|
||
var rootNode yaml.Node | ||
mErr := yaml.Unmarshal([]byte(yml), &rootNode) | ||
assert.NoError(t, mErr) | ||
|
||
nodes, _ := utils.FindNodes([]byte(yml), path) | ||
|
||
rule := buildOpenApiTestRuleAction(path, "oas3_no_ref_siblings", "", nil) | ||
ctx := buildOpenApiTestContext(model.CastToRuleAction(rule.Then), nil) | ||
config := index.CreateOpenAPIIndexConfig() | ||
ctx.Index = index.NewSpecIndexWithConfig(&rootNode, config) | ||
|
||
def := OASNoRefSiblings{} | ||
res := def.RunRule(nodes, ctx) | ||
|
||
assert.Len(t, res, 0) | ||
|
||
} | ||
|
||
func TestOASNoRefSiblings_RunRule_Success(t *testing.T) { | ||
|
||
yml := `paths: | ||
/nice/{rice}: | ||
requestBody: | ||
content: | ||
application/json: | ||
schema: | ||
$ref: '#/components/schemas/Rice' | ||
/hot/{dog}: | ||
requestBody: | ||
content: | ||
application/json: | ||
schema: | ||
$ref: '#/components/schemas/Dog'` | ||
|
||
path := "$" | ||
|
||
var rootNode yaml.Node | ||
mErr := yaml.Unmarshal([]byte(yml), &rootNode) | ||
assert.NoError(t, mErr) | ||
|
||
nodes, _ := utils.FindNodes([]byte(yml), path) | ||
|
||
rule := buildOpenApiTestRuleAction(path, "oas3_no_ref_siblings", "", nil) | ||
ctx := buildOpenApiTestContext(model.CastToRuleAction(rule.Then), nil) | ||
config := index.CreateOpenAPIIndexConfig() | ||
ctx.Index = index.NewSpecIndexWithConfig(&rootNode, config) | ||
|
||
def := OASNoRefSiblings{} | ||
res := def.RunRule(nodes, ctx) | ||
|
||
assert.Len(t, res, 0) | ||
|
||
} | ||
|
||
func TestOASNoRefSiblings_RunRule_Fail_Single(t *testing.T) { | ||
|
||
yml := `paths: | ||
/nice/{rice}: | ||
requestBody: | ||
content: | ||
application/json: | ||
schema: | ||
$ref: '#/components/schemas/Rice' | ||
/hot/{dog}: | ||
requestBody: | ||
content: | ||
application/json: | ||
schema: | ||
type: integer | ||
$ref: '#/components/schemas/Dog'` | ||
|
||
path := "$" | ||
|
||
var rootNode yaml.Node | ||
mErr := yaml.Unmarshal([]byte(yml), &rootNode) | ||
assert.NoError(t, mErr) | ||
|
||
nodes, _ := utils.FindNodes([]byte(yml), path) | ||
|
||
rule := buildOpenApiTestRuleAction(path, "oas3_no_ref_siblings", "", nil) | ||
ctx := buildOpenApiTestContext(model.CastToRuleAction(rule.Then), nil) | ||
config := index.CreateOpenAPIIndexConfig() | ||
ctx.Index = index.NewSpecIndexWithConfig(&rootNode, config) | ||
|
||
def := OASNoRefSiblings{} | ||
res := def.RunRule(nodes, ctx) | ||
|
||
assert.Len(t, res, 1) | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.