Skip to content

Commit

Permalink
integrating libopenapi deeper into vacuum
Browse files Browse the repository at this point in the history
too many conflicting techniques and models, reduce the schema drift.

Signed-off-by: Dave Shanley <dave@quobix.com>
  • Loading branch information
daveshanley committed May 1, 2023
1 parent 7446013 commit 9eba3f1
Show file tree
Hide file tree
Showing 7 changed files with 1,202 additions and 1,108 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ require (
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d // indirect
github.com/pb33f/libopenapi-validator v0.0.3 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@ github.com/pb33f/libopenapi v0.7.0 h1:2mQgDa0UQs8/5Hd2z+KbgWvoGx0++N3ihMKihcAS5M
github.com/pb33f/libopenapi v0.7.0/go.mod h1:lvUmCtjgHUGVj6WzN3I5/CS9wkXtyN3Ykjh6ZZP5lrI=
github.com/pb33f/libopenapi v0.8.0 h1:zfXNu7/Hk9sgMV3UC2hnSB4CGlgQkMaWWwPn0Id+eLI=
github.com/pb33f/libopenapi v0.8.0/go.mod h1:lvUmCtjgHUGVj6WzN3I5/CS9wkXtyN3Ykjh6ZZP5lrI=
github.com/pb33f/libopenapi-validator v0.0.3 h1:tSRhE7awxiNPV6VHhgEQ6v0rN7kFnzW+uChHSoo5W58=
github.com/pb33f/libopenapi-validator v0.0.3/go.mod h1:lJII+eGg2vCaKOiXuqtDsVPGa0Ud2TxSbmI5IsINMHQ=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
Expand Down
182 changes: 92 additions & 90 deletions model/rules.go
Original file line number Diff line number Diff line change
@@ -1,146 +1,148 @@
package model

import (
_ "embed" // embedding is not supported by golint,
"encoding/json"
"github.com/daveshanley/vacuum/model/reports"
"github.com/pb33f/libopenapi/datamodel"
"github.com/pb33f/libopenapi/index"
"gopkg.in/yaml.v3"
"regexp"
"time"
_ "embed" // embedding is not supported by golint,
"encoding/json"
"github.com/daveshanley/vacuum/model/reports"
"github.com/pb33f/libopenapi"
"github.com/pb33f/libopenapi/datamodel"
"github.com/pb33f/libopenapi/index"
"gopkg.in/yaml.v3"
"regexp"
"time"
)

const (
SeverityError = "error"
SeverityWarn = "warn"
SeverityInfo = "info"
SeverityHint = "hint"
CategoryExamples = "examples"
CategoryOperations = "operations"
CategoryInfo = "information"
CategoryDescriptions = "descriptions"
CategorySchemas = "schemas"
CategorySecurity = "security"
CategoryTags = "tags"
CategoryValidation = "validation"
CategoryAll = "all"
SeverityError = "error"
SeverityWarn = "warn"
SeverityInfo = "info"
SeverityHint = "hint"
CategoryExamples = "examples"
CategoryOperations = "operations"
CategoryInfo = "information"
CategoryDescriptions = "descriptions"
CategorySchemas = "schemas"
CategorySecurity = "security"
CategoryTags = "tags"
CategoryValidation = "validation"
CategoryAll = "all"
)

type RuleCategory struct {
Id string `json:"id" yaml:"id"` // The category ID
Name string `json:"name" yaml:"name"` // The name of the category
Description string `json:"description" yaml:"description"` // What is the category all about?
Id string `json:"id" yaml:"id"` // The category ID
Name string `json:"name" yaml:"name"` // The name of the category
Description string `json:"description" yaml:"description"` // What is the category all about?
}

// RuleFunctionContext defines a RuleAction, Rule and Options for a RuleFunction being run.
type RuleFunctionContext struct {
RuleAction *RuleAction // A reference to the action defined configured by the rule
Rule *Rule // A reference to the Rule being used for the function
Given interface{} // Path/s being used by rule, multiple paths can be used
Options interface{} // Function options
Index *index.SpecIndex // A reference to the index created for the spec being parsed
SpecInfo *datamodel.SpecInfo // A reference to all specification information for the spec being parsed.
RuleAction *RuleAction // A reference to the action defined configured by the rule
Rule *Rule // A reference to the Rule being used for the function
Given interface{} // Path/s being used by rule, multiple paths can be used
Options interface{} // Function options
Index *index.SpecIndex // A reference to the index created for the spec being parsed
SpecInfo *datamodel.SpecInfo // A reference to all specification information for the spec being parsed.
Document libopenapi.Document // A reference to the document being parsed
}

// RuleFunctionResult describes a failure with linting after being run through a rule
type RuleFunctionResult struct {
Message string `json:"message" yaml:"message"` // What failed and why?
Range reports.Range `json:"range" yaml:"range"` // Where did it happen?
Path string `json:"path" yaml:"path"` // the JSONPath to where it can be found
RuleId string `json:"ruleId" yaml:"ruleId"` // The ID of the rule
RuleSeverity string `json:"ruleSeverity" yaml:"ruleSeverity"` // the severity of the rule used
Rule *Rule `json:"-" yaml:"-"` // The rule used
StartNode *yaml.Node `json:"-" yaml:"-"` // Start of the violation
EndNode *yaml.Node `json:"-" yaml:"-"` // end of the violation
Timestamp *time.Time `json:"-" yaml:"-"` // When the result was created.
Message string `json:"message" yaml:"message"` // What failed and why?
Range reports.Range `json:"range" yaml:"range"` // Where did it happen?
Path string `json:"path" yaml:"path"` // the JSONPath to where it can be found
RuleId string `json:"ruleId" yaml:"ruleId"` // The ID of the rule
RuleSeverity string `json:"ruleSeverity" yaml:"ruleSeverity"` // the severity of the rule used
Rule *Rule `json:"-" yaml:"-"` // The rule used
StartNode *yaml.Node `json:"-" yaml:"-"` // Start of the violation
EndNode *yaml.Node `json:"-" yaml:"-"` // end of the violation
Timestamp *time.Time `json:"-" yaml:"-"` // When the result was created.
}

// RuleResultSet contains all the results found during a linting run, and all the methods required to
// filter, sort and calculate counts.
type RuleResultSet struct {
Results []*RuleFunctionResult `json:"results,omitempty" yaml:"results,omitempty"` // All the results!
WarnCount int `json:"warningCount" yaml:"warningCount"` // Total warnings
ErrorCount int `json:"errorCount" yaml:"errorCount"` // Total errors
InfoCount int `json:"infoCount" yaml:"infoCount"` // Total info
categoryMap map[*RuleCategory][]*RuleFunctionResult `json:"-" yaml:"-"`
Results []*RuleFunctionResult `json:"results,omitempty" yaml:"results,omitempty"` // All the results!
WarnCount int `json:"warningCount" yaml:"warningCount"` // Total warnings
ErrorCount int `json:"errorCount" yaml:"errorCount"` // Total errors
InfoCount int `json:"infoCount" yaml:"infoCount"` // Total info
categoryMap map[*RuleCategory][]*RuleFunctionResult `json:"-" yaml:"-"`
}

// RuleFunction is any compatible structure that can be used to run vacuum rules.
type RuleFunction interface {
RunRule(nodes []*yaml.Node, context RuleFunctionContext) []RuleFunctionResult // The place where logic is run
GetSchema() RuleFunctionSchema // How to use the function and its details.
RunRule(nodes []*yaml.Node, context RuleFunctionContext) []RuleFunctionResult // The place where logic is run
GetSchema() RuleFunctionSchema // How to use the function and its details.
}

// RuleAction is what to do, on what field, and what options are to be used.
type RuleAction struct {
Field string `json:"field,omitempty" yaml:"field,omitempty"`
Function string `json:"function,omitempty" yaml:"function,omitempty"`
FunctionOptions interface{} `json:"functionOptions,omitempty" yaml:"functionOptions,omitempty"`
Field string `json:"field,omitempty" yaml:"field,omitempty"`
Function string `json:"function,omitempty" yaml:"function,omitempty"`
FunctionOptions interface{} `json:"functionOptions,omitempty" yaml:"functionOptions,omitempty"`
}

// Rule is a structure that represents a rule as part of a ruleset.
type Rule struct {
Id string `json:"id,omitempty" yaml:"id,omitempty"`
Description string `json:"description,omitempty" yaml:"description,omitempty"`
Given interface{} `json:"given,omitempty" yaml:"given,omitempty"`
Formats []string `json:"formats,omitempty" yaml:"formats,omitempty"`
Resolved bool `json:"resolved,omitempty" yaml:"resolved,omitempty"`
Recommended bool `json:"recommended,omitempty" yaml:"recommended,omitempty"`
Type string `json:"type,omitempty" yaml:"type,omitempty"`
Severity string `json:"severity,omitempty" yaml:"severity,omitempty"`
Then interface{} `json:"then,omitempty" yaml:"then,omitempty"`
PrecompiledPattern *regexp.Regexp `json:"-" yaml:"-"` // regex is slow.
RuleCategory *RuleCategory `json:"category,omitempty" yaml:"category,omitempty"`
Name string `json:"-" yaml:"-"`
HowToFix string `json:"howToFix,omitempty" yaml:"howToFix,omitempty"`
Id string `json:"id,omitempty" yaml:"id,omitempty"`
Description string `json:"description,omitempty" yaml:"description,omitempty"`
Given interface{} `json:"given,omitempty" yaml:"given,omitempty"`
Formats []string `json:"formats,omitempty" yaml:"formats,omitempty"`
Resolved bool `json:"resolved,omitempty" yaml:"resolved,omitempty"`
Recommended bool `json:"recommended,omitempty" yaml:"recommended,omitempty"`
Type string `json:"type,omitempty" yaml:"type,omitempty"`
Severity string `json:"severity,omitempty" yaml:"severity,omitempty"`
Then interface{} `json:"then,omitempty" yaml:"then,omitempty"`
PrecompiledPattern *regexp.Regexp `json:"-" yaml:"-"` // regex is slow.
RuleCategory *RuleCategory `json:"category,omitempty" yaml:"category,omitempty"`
Name string `json:"-" yaml:"-"`
HowToFix string `json:"howToFix,omitempty" yaml:"howToFix,omitempty"`
}

// RuleFunctionProperty is used by RuleFunctionSchema to describe the functionOptions a Rule accepts
type RuleFunctionProperty struct {
Name string `json:"name" yaml:"name"`
Description string `json:"description" yaml:"description"`
Name string `json:"name" yaml:"name"`
Description string `json:"description" yaml:"description"`
}

// RuleFunctionSchema describes the name, required properties and a slice of RuleFunctionProperty properties.
type RuleFunctionSchema struct {
Name string `json:"name,omitempty" yaml:"name,omitempty"` // The name of this function **important**
Required []string `json:"required,omitempty" yaml:"required,omitempty"` // List of all required properties to be set
RequiresField bool `json:"requiresField,omitempty" yaml:"requiresField,omitempty"` // 'field' must be used with this function
Properties []RuleFunctionProperty `json:"properties,omitempty" yaml:"properties,omitempty"` // all properties to be passed to the function
MinProperties int `json:"minProperties,omitempty" yaml:"minProperties,omitempty"` // Minimum number of properties
MaxProperties int `json:"maxProperties,omitempty" yaml:"maxProperties,omitempty"` // Maximum number of properties
ErrorMessage string `json:"errorMessage,omitempty" yaml:"errorMessage,omitempty"` // Error message to be used in case of failed validartion.
Name string `json:"name,omitempty" yaml:"name,omitempty"` // The name of this function **important**
Required []string `json:"required,omitempty" yaml:"required,omitempty"` // List of all required properties to be set
RequiresField bool `json:"requiresField,omitempty" yaml:"requiresField,omitempty"` // 'field' must be used with this function
Properties []RuleFunctionProperty `json:"properties,omitempty" yaml:"properties,omitempty"` // all properties to be passed to the function
MinProperties int `json:"minProperties,omitempty" yaml:"minProperties,omitempty"` // Minimum number of properties
MaxProperties int `json:"maxProperties,omitempty" yaml:"maxProperties,omitempty"` // Maximum number of properties
ErrorMessage string `json:"errorMessage,omitempty" yaml:"errorMessage,omitempty"` // Error message to be used in case of failed validartion.
}

// GetSeverityAsIntValue will return the severity state of the rule as an integer. If the severity is not known
// then -1 is returned.
func (r *Rule) GetSeverityAsIntValue() int {
switch r.Severity {
case SeverityError:
return 0
case SeverityWarn:
return 1
case SeverityInfo:
return 2
case SeverityHint:
return 3
}
return -1
switch r.Severity {
case SeverityError:
return 0
case SeverityWarn:
return 1
case SeverityInfo:
return 2
case SeverityHint:
return 3
}
return -1
}

// GetPropertyDescription is a shortcut method for extracting the description of a property by its name.
func (rfs RuleFunctionSchema) GetPropertyDescription(name string) string {
for _, prop := range rfs.Properties {
if prop.Name == name {
return prop.Description
}
}
return ""
for _, prop := range rfs.Properties {
if prop.Name == name {
return prop.Description
}
}
return ""
}

// ToJSON render out a rule to JSON.
func (r Rule) ToJSON() string {
d, _ := json.Marshal(r)
return string(d)
d, _ := json.Marshal(r)
return string(d)
}
Loading

0 comments on commit 9eba3f1

Please sign in to comment.