Skip to content

Commit

Permalink
Merge pull request #85 from daveshanley/example-tuning
Browse files Browse the repository at this point in the history
Tuned example handling
  • Loading branch information
daveshanley committed Jul 16, 2022
2 parents 742c58d + f2ae0d1 commit abc1c11
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 47 deletions.
100 changes: 79 additions & 21 deletions functions/openapi/examples.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/daveshanley/vacuum/utils"
"github.com/xeipuuv/gojsonschema"
"gopkg.in/yaml.v3"
"sync"
)

// Examples is a rule that checks that examples are being correctly used.
Expand All @@ -23,6 +24,22 @@ func (ex Examples) GetSchema() model.RuleFunctionSchema {
}
}

func checkExampleAsync(wg *sync.WaitGroup, rbNode *yaml.Node, basePath string, results *[]model.RuleFunctionResult, context model.RuleFunctionContext) {
checkExamples(rbNode, basePath, results, context)
wg.Done()
}

func analyzeExampleAsync(wg *sync.WaitGroup, nameNodeValue string, mediaTypeNode *yaml.Node, basePath string, results *[]model.RuleFunctionResult, context model.RuleFunctionContext) {
analyzeExample(nameNodeValue, mediaTypeNode, basePath, results, context)
wg.Done()
}

type opExample struct {
path string
node *yaml.Node
param *yaml.Node
}

// RunRule will execute the Examples rule, based on supplied context and a supplied []*yaml.Node slice.
func (ex Examples) RunRule(nodes []*yaml.Node, context model.RuleFunctionContext) []model.RuleFunctionResult {

Expand All @@ -35,6 +52,12 @@ func (ex Examples) RunRule(nodes []*yaml.Node, context model.RuleFunctionContext
ops := context.Index.GetPathsNode()

var opPath, opMethod string

// collect responses to scan
var responseBodyCollection []opExample
var requestBodyCollection []opExample
var paramCollection []opExample

if ops != nil {
for i, op := range ops.Content {
if i%2 == 0 {
Expand All @@ -50,17 +73,18 @@ func (ex Examples) RunRule(nodes []*yaml.Node, context model.RuleFunctionContext
}

basePath := fmt.Sprintf("$.paths.%s.%s", opPath, opMethod)
fmt.Sprintf(basePath)

// check requests.
//check requests.
_, rbNode := utils.FindKeyNode("requestBody", method.Content)

//check responses
_, respNode := utils.FindKeyNode("responses", method.Content)
//fmt.Sprintf("%v", respNode)

if rbNode != nil {
results = checkExamples(rbNode, utils.BuildPath(basePath, []string{"requestBody"}), results, context)
requestBodyCollection = append(requestBodyCollection, opExample{
path: utils.BuildPath(basePath, []string{"requestBody"}),
node: rbNode,
})
}

// check parameters.
Expand All @@ -72,28 +96,55 @@ func (ex Examples) RunRule(nodes []*yaml.Node, context model.RuleFunctionContext
// extract name from param
_, nameNode := utils.FindKeyNode("name", []*yaml.Node{param})
if nameNode != nil {
results = analyzeExample(nameNode.Value, param,
utils.BuildPath(basePath, []string{fmt.Sprintf("%s[%d]", "parameters", y)}), results, context)

paramCollection = append(paramCollection, opExample{
path: utils.BuildPath(basePath, []string{fmt.Sprintf("%s[%d]", "parameters", y)}),
node: nameNode,
param: param,
})
}
}
}

if respNode != nil {
var code string
//wg.Add()
for x, respCodeNode := range respNode.Content {

if x%2 == 0 {
code = respCodeNode.Value
continue
}
results = checkExamples(respCodeNode, utils.BuildPath(basePath, []string{fmt.Sprintf("%s.%s",
"responses", code)}), results, context)

responseBodyCollection = append(
responseBodyCollection,
opExample{
node: respCodeNode,
path: utils.BuildPath(basePath, []string{fmt.Sprintf("%s.%s", "responses", code)}),
})
}
}

}
}
}

// scan requests, responses as fast as we can asynchronously.
var wg sync.WaitGroup
wg.Add(len(responseBodyCollection))
wg.Add(len(requestBodyCollection))
wg.Add(len(paramCollection))
for _, rb := range responseBodyCollection {
go checkExampleAsync(&wg, rb.node, rb.path, results, context)
}
for _, rb := range requestBodyCollection {
go checkExampleAsync(&wg, rb.node, rb.path, results, context)
}
for _, p := range paramCollection {
go analyzeExampleAsync(&wg, p.node.Value, p.param, p.path, results, context)
}
wg.Wait()

//check components.
objNode := context.Index.GetSchemasNode()

Expand Down Expand Up @@ -352,7 +403,7 @@ func analyzeExample(nameNodeValue string, mediaTypeNode *yaml.Node, basePath str
res.EndNode = sValue
res.Path = basePath
res.Rule = context.Rule
*results = append(*results, res)
modifyExampleResults(results, res)
return results
}

Expand Down Expand Up @@ -386,7 +437,7 @@ func analyzeExample(nameNodeValue string, mediaTypeNode *yaml.Node, basePath str
z.EndNode = valueNode
z.Path = nodePath
z.Rule = context.Rule
*results = append(*results, z)
modifyExampleResults(results, z)
continue
}

Expand All @@ -397,7 +448,7 @@ func analyzeExample(nameNodeValue string, mediaTypeNode *yaml.Node, basePath str
z.EndNode = valueNode
z.Path = nodePath
z.Rule = context.Rule
*results = append(*results, z)
modifyExampleResults(results, z)
continue
}

Expand All @@ -416,7 +467,7 @@ func analyzeExample(nameNodeValue string, mediaTypeNode *yaml.Node, basePath str
z.EndNode = valueNode
z.Path = nodePath
z.Rule = context.Rule
*results = append(*results, z)
modifyExampleResults(results, z)
}
}

Expand All @@ -429,7 +480,7 @@ func analyzeExample(nameNodeValue string, mediaTypeNode *yaml.Node, basePath str
z.EndNode = valueNode
z.Path = nodePath
z.Rule = context.Rule
*results = append(*results, z)
modifyExampleResults(results, z)
}

// can`t both have a value and an external value set!
Expand All @@ -441,8 +492,7 @@ func analyzeExample(nameNodeValue string, mediaTypeNode *yaml.Node, basePath str
z.EndNode = valueNode
z.Path = nodePath
z.Rule = context.Rule
*results = append(*results, z)

modifyExampleResults(results, z)
}

}
Expand All @@ -468,7 +518,7 @@ func analyzeExample(nameNodeValue string, mediaTypeNode *yaml.Node, basePath str
}
z.Rule = context.Rule
z.Path = basePath
*results = append(*results, z)
modifyExampleResults(results, z)
return results
}

Expand All @@ -490,7 +540,7 @@ func analyzeExample(nameNodeValue string, mediaTypeNode *yaml.Node, basePath str
}
z.Rule = context.Rule
z.Path = basePath
*results = append(*results, z)
modifyExampleResults(results, z)
return results
}

Expand All @@ -508,7 +558,7 @@ func analyzeExample(nameNodeValue string, mediaTypeNode *yaml.Node, basePath str
}
z.Rule = context.Rule
z.Path = basePath
*results = append(*results, z)
modifyExampleResults(results, z)
}
return results
}
Expand Down Expand Up @@ -540,9 +590,9 @@ func analyzeExample(nameNodeValue string, mediaTypeNode *yaml.Node, basePath str
z.StartNode = sValue
z.EndNode = sValue
z.Rule = context.Rule
*results = append(*results, z)
modifyExampleResults(results, z)
}

}
if schema == nil {
return results
Expand All @@ -559,10 +609,18 @@ func analyzeExample(nameNodeValue string, mediaTypeNode *yaml.Node, basePath str
z.StartNode = pNode
z.EndNode = endNode
z.Rule = context.Rule
*results = append(*results, z)
modifyExampleResults(results, z)
}
}
}

return results
}

var exampleLock sync.Mutex

func modifyExampleResults(results *[]model.RuleFunctionResult, result model.RuleFunctionResult) {
exampleLock.Lock()
*results = append(*results, result)
exampleLock.Unlock()
}
29 changes: 15 additions & 14 deletions functions/openapi/parameter_description.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,21 +60,22 @@ func (pd ParameterDescription) RunRule(nodes []*yaml.Node, context model.RuleFun
for path, methodMap := range opParams {
for method, paramMap := range methodMap {
for pName, param := range paramMap {
if param != nil && param.Node != nil {
_, in := utils.FindKeyNode("in", param.Node.Content)
_, desc := utils.FindKeyNode("description", param.Node.Content)
lastNode := utils.FindLastChildNode(param.Node)

_, in := utils.FindKeyNode("in", param.Node.Content)
_, desc := utils.FindKeyNode("description", param.Node.Content)
lastNode := utils.FindLastChildNode(param.Node)

if in != nil {
if desc == nil || desc.Value == "" {
pathString := fmt.Sprintf("$.paths.%s.%s.parameters", path, method)
results = append(results, model.RuleFunctionResult{
Message: fmt.Sprintf(msg, pName),
StartNode: param.Node,
EndNode: lastNode,
Path: pathString,
Rule: context.Rule,
})
if in != nil {
if desc == nil || desc.Value == "" {
pathString := fmt.Sprintf("$.paths.%s.%s.parameters", path, method)
results = append(results, model.RuleFunctionResult{
Message: fmt.Sprintf(msg, pName),
StartNode: param.Node,
EndNode: lastNode,
Path: pathString,
Rule: context.Rule,
})
}
}
}
}
Expand Down
13 changes: 7 additions & 6 deletions parser/json_schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,13 +126,15 @@ func ConvertNodeDefinitionIntoSchema(node *yaml.Node) (*Schema, error) {
errChan := make(chan error, 1)

go func() {

dat, err := yaml.Marshal(node)
if err != nil {
var dat []byte
var err error
dat, err = yaml.Marshal(node)
if dat == nil && err != nil {
errChan <- err
}
var schema Schema
err = yaml.Unmarshal(dat, &schema)

if err != nil {
errChan <- err
}
Expand All @@ -146,10 +148,9 @@ func ConvertNodeDefinitionIntoSchema(node *yaml.Node) (*Schema, error) {
case schema = <-schChan:
schema.Schema = &utils.SchemaSource
schema.Id = &utils.SchemaId

return &schema, nil
case <-time.After(40 * time.Millisecond): // even this seems long to me.
return nil, errors.New("schema failed to unpack in a reasonable timeframe")
case <-time.After(500 * time.Millisecond): // even this seems long to me.
return nil, errors.New("schema is too big! It failed to unpack in a reasonable timeframe")
}
}

Expand Down
4 changes: 2 additions & 2 deletions rulesets/ruleset_functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@ func GetOAS2ParameterDescriptionRule() *model.Rule {
Description: "Parameter description checks",
Given: "$",
Resolved: true,
Recommended: false,
Recommended: true,
RuleCategory: model.RuleCategories[model.CategoryDescriptions],
Type: style,
Severity: warn,
Expand All @@ -445,7 +445,7 @@ func GetOAS3ParameterDescriptionRule() *model.Rule {
Description: "Parameter description checks",
Given: "$",
Resolved: true,
Recommended: false,
Recommended: true,
RuleCategory: model.RuleCategories[model.CategoryDescriptions],
Type: style,
Severity: warn,
Expand Down
8 changes: 4 additions & 4 deletions rulesets/rulesets_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ func TestRuleSet_GetConfiguredRules_All(t *testing.T) {
assert.Len(t, ruleSet.Rules, 47)

ruleSet = rs.GenerateOpenAPIRecommendedRuleSet()
assert.Len(t, ruleSet.Rules, 35)
assert.Len(t, ruleSet.Rules, 37)

}

Expand All @@ -140,7 +140,7 @@ rules:
def := BuildDefaultRuleSets()
rs, _ := CreateRuleSetFromData([]byte(yaml))
override := def.GenerateRuleSetFromSuppliedRuleSet(rs)
assert.Len(t, override.Rules, 35)
assert.Len(t, override.Rules, 37)
assert.Len(t, override.RuleDefinitions, 1)
}

Expand Down Expand Up @@ -188,7 +188,7 @@ rules:
def := BuildDefaultRuleSets()
rs, _ := CreateRuleSetFromData([]byte(yaml))
override := def.GenerateRuleSetFromSuppliedRuleSet(rs)
assert.Len(t, override.Rules, 34)
assert.Len(t, override.Rules, 36)
assert.Len(t, override.RuleDefinitions, 1)
}

Expand All @@ -204,7 +204,7 @@ rules:
def := BuildDefaultRuleSets()
rs, _ := CreateRuleSetFromData([]byte(yaml))
override := def.GenerateRuleSetFromSuppliedRuleSet(rs)
assert.Len(t, override.Rules, 35)
assert.Len(t, override.Rules, 37)
assert.Equal(t, "hint", override.Rules["operation-success-response"].Severity)
}

Expand Down

0 comments on commit abc1c11

Please sign in to comment.