Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add ignore command option #144

Merged
merged 3 commits into from Jul 7, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 12 additions & 3 deletions validator/example/cmd.go
Expand Up @@ -18,7 +18,7 @@ func NewCommand() *cobra.Command {

cmd := &cobra.Command{
Use: "cmd0",
Short: "This is the short",
Short: "This is the short description",
Long: `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis malesuada varius lacus, sit amet dictum risus convallis nec. Quisque suscipit at neque in blandit. Proin a accumsan ante. Aenean cursus suscipit sem. Nunc sollicitudin, ante et vehicula pharetra, mauris elit porta felis, et ultricies nulla justo eleifend justo. Proin sit amet.`,
Example: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis malesuada varius lacus, sit amet dictum risus convallis nec. Quisque suscipit at neque in blandit. Proin a accumsan ante. Aenean cursus suscipit sem. Nunc sollicitudin, ante et vehicula pharetra, mauris elit porta felis, et ultricies nulla justo eleifend justo. Proin sit amet.",
Run: func(cmd *cobra.Command, args []string) {
Expand All @@ -28,7 +28,6 @@ func NewCommand() *cobra.Command {
var commands []command = []command{
{
use: "subcmd01",
short: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis malesuada varius lacus, sit amet dictum risus convallis nec. Quisque suscipit at neque in blandit. Proin a accumsan ante. Aenean cursus suscipit sem. Nunc sollicitudin, ante et vehicula pharetra, mauris elit porta felis, et ultricies nulla justo eleifend justo. Proin sit amet.",
long: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis malesuada varius lacus, sit amet dictum risus convallis nec. Quisque suscipit at neque in blandit. Proin a accumsan ante. Aenean cursus suscipit sem. Nunc sollicitudin, ante et vehicula pharetra, mauris elit porta felis, et ultricies nulla justo eleifend justo. Proin sit amet.",
example: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis malesuada varius lacus, sit amet dictum risus convallis nec. Quisque suscipit at neque in blandit. Proin a accumsan ante. Aenean cursus suscipit sem. Nunc sollicitudin, ante et vehicula pharetra, mauris elit porta felis, et ultricies nulla justo eleifend justo. Proin sit amet.",
},
Expand All @@ -46,6 +45,16 @@ func NewCommand() *cobra.Command {
},
}

cmd100 := &cobra.Command{
Use: "cmd100",
Long: `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis malesuada varius lacus, sit amet dictum risus convallis nec. Quisque suscipit at neque in blandit. Proin a accumsan ante. Aenean cursus suscipit sem. Nunc sollicitudin, ante et vehicula pharetra, mauris elit porta felis, et ultricies nulla justo eleifend justo. Proin sit amet.`,
Example: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis malesuada varius lacus, sit amet dictum risus convallis nec. Quisque suscipit at neque in blandit. Proin a accumsan ante. Aenean cursus suscipit sem. Nunc sollicitudin, ante et vehicula pharetra, mauris elit porta felis, et ultricies nulla justo eleifend justo. Proin sit amet.",
Run: func(cmd *cobra.Command, args []string) {
},
}

cmd100.AddCommand(cmd)

for _, cm := range commands {
cm.name = &cobra.Command{
Use: cm.use,
Expand All @@ -57,7 +66,7 @@ func NewCommand() *cobra.Command {
cmd.AddCommand(cm.name)
}

return cmd
return cmd100
}

func main() {
Expand Down
14 changes: 13 additions & 1 deletion validator/example/cmd_test.go
Expand Up @@ -3,6 +3,7 @@ package main
import (
"testing"

"github.com/aerogear/charmil/validator"
"github.com/aerogear/charmil/validator/rules"
)

Expand All @@ -12,13 +13,24 @@ func Test_ExecuteCommand(t *testing.T) {
// Testing cobra commands with default recommended config
// default config can also be overrided
ruleCfg := rules.ValidatorConfig{
ValidatorOptions: rules.ValidatorOptions{
SkipCommands: map[string]bool{"cmd100 cmd0 subcmd01": true},
},
ValidatorRules: rules.ValidatorRules{
Length: rules.Length{
RuleOptions: validator.RuleOptions{
SkipCommands: map[string]bool{"cmd100": true},
},
Limits: map[string]rules.Limit{
"Use": {Min: 1},
},
},
MustExist: rules.MustExist{Fields: map[string]bool{"Run": true}},
MustExist: rules.MustExist{
RuleOptions: validator.RuleOptions{
SkipCommands: map[string]bool{"cmd100": true},
},
Fields: map[string]bool{"Run": true},
},
UseMatches: rules.UseMatches{Regexp: `^[^-_+]+$`},
},
}
Expand Down
14 changes: 10 additions & 4 deletions validator/rules/config.go
Expand Up @@ -17,7 +17,9 @@ type ValidatorConfig struct {
// ValidatorOptions provide additional configurations
// to the rules
type ValidatorOptions struct {
Verbose bool `json:"Verbose"`
Verbose bool `json:"Verbose"`
SkipChildren map[string]bool `json:"SkipChildren"`
SkipCommands map[string]bool `json:"SkipCommands"`
}

// ValidatorRules consists of all the rules
Expand Down Expand Up @@ -47,7 +49,9 @@ func ValidatorConfigToRuleConfig(validatorConfig *ValidatorConfig, ruleConfig *R
},
ValidatorRules: ValidatorRules{
Length: Length{
Verbose: defaultVerbose,
RuleOptions: validator.RuleOptions{
Verbose: defaultVerbose,
},
Limits: map[string]Limit{
"Use": {Min: 2},
"Short": {Min: 15},
Expand All @@ -56,8 +60,10 @@ func ValidatorConfigToRuleConfig(validatorConfig *ValidatorConfig, ruleConfig *R
},
},
MustExist: MustExist{
Verbose: defaultVerbose,
Fields: map[string]bool{"Use": true, "Short": true, "Long": true, "Example": true},
RuleOptions: validator.RuleOptions{
Verbose: defaultVerbose,
},
Fields: map[string]bool{"Use": true, "Short": true, "Long": true, "Example": true},
},
},
}
Expand Down
35 changes: 25 additions & 10 deletions validator/rules/executor.go
Expand Up @@ -26,17 +26,25 @@ func ExecuteRulesInternal(cmd *cobra.Command, ruleConfig *RuleConfig, userValida
var errors []validator.ValidationError
info := validator.StatusLog{TotalTested: 0, TotalErrors: 0, Errors: errors}

// if command needs to be ignored
if val, ok := userValidatorConfig.ValidatorOptions.SkipChildren[cmd.CommandPath()]; ok {
if val {
return info.Errors
}
}

// initialize default rules
initDefaultRules(userValidatorConfig, ruleConfig)

// validate the root command
validate(cmd, &info, ruleConfig)
validate(cmd, &info, ruleConfig, userValidatorConfig)

return executeHelper(cmd, &info, ruleConfig)
return executeHelper(cmd, &info, ruleConfig, userValidatorConfig)
}

func executeHelper(cmd *cobra.Command, info *validator.StatusLog, ruleConfig *RuleConfig) []validator.ValidationError {
info.Errors = executeRecursive(cmd, info, ruleConfig)
func executeHelper(cmd *cobra.Command, info *validator.StatusLog, ruleConfig *RuleConfig, userValidatorConfig *ValidatorConfig) []validator.ValidationError {

info.Errors = executeRecursive(cmd, info, ruleConfig, userValidatorConfig)

// prints additional info for the checks
fmt.Fprintf(os.Stderr, "commands checked: %d\nchecks failed: %d\n", info.TotalTested, info.TotalErrors)
Expand All @@ -46,35 +54,42 @@ func executeHelper(cmd *cobra.Command, info *validator.StatusLog, ruleConfig *Ru

// executeRecursive recursively traverse over all the subcommands
// and validate using executeRulesChildren function
func executeRecursive(cmd *cobra.Command, info *validator.StatusLog, ruleConfig *RuleConfig) []validator.ValidationError {
func executeRecursive(cmd *cobra.Command, info *validator.StatusLog, ruleConfig *RuleConfig, userValidatorConfig *ValidatorConfig) []validator.ValidationError {
for _, child := range cmd.Commands() {
// base case
if !child.IsAvailableCommand() || child.IsAdditionalHelpTopicCommand() {
continue
}
// recursive call
info.Errors = executeRecursive(child, info, ruleConfig)
info.Errors = executeRecursive(child, info, ruleConfig, userValidatorConfig)
}
info.Errors = executeRulesChildren(cmd, info, ruleConfig)
info.Errors = executeRulesChildren(cmd, info, ruleConfig, userValidatorConfig)

return info.Errors
}

// executeRulesChildren execute rules on children of cmd
func executeRulesChildren(cmd *cobra.Command, info *validator.StatusLog, ruleConfig *RuleConfig) []validator.ValidationError {
func executeRulesChildren(cmd *cobra.Command, info *validator.StatusLog, ruleConfig *RuleConfig, userValidatorConfig *ValidatorConfig) []validator.ValidationError {
children := cmd.Commands()
for _, child := range children {

if !child.IsAvailableCommand() || child.IsAdditionalHelpTopicCommand() {
continue
}
validate(child, info, ruleConfig)
validate(child, info, ruleConfig, userValidatorConfig)
}
return info.Errors
}

// validate returns validation errors by executing the rules
func validate(cmd *cobra.Command, info *validator.StatusLog, ruleConfig *RuleConfig) {
func validate(cmd *cobra.Command, info *validator.StatusLog, ruleConfig *RuleConfig, userValidatorConfig *ValidatorConfig) {

// if command needs to be ignored
if val, ok := userValidatorConfig.ValidatorOptions.SkipCommands[cmd.CommandPath()]; ok {
if val {
return
}
}

// traverse all rules and validate
for _, rule := range ruleConfig.Rules {
Expand Down
13 changes: 10 additions & 3 deletions validator/rules/length.go
Expand Up @@ -32,15 +32,22 @@ type Limit struct {
// with key as attribute for which length is controlled
// and value limit as Limit struct
type Length struct {
Verbose bool `json:"Verbose"`
Limits map[string]Limit `json:"Limits"`
RuleOptions validator.RuleOptions
Limits map[string]Limit `json:"Limits"`
}

// Validate is a method of type Rule Interface
// which returns validation errors
func (l *Length) Validate(cmd *cobra.Command) []validator.ValidationError {
var errors []validator.ValidationError

// if command needs to be ignored
if val, ok := l.RuleOptions.SkipCommands[cmd.CommandPath()]; ok {
if val {
return errors
}
}

for fieldName, limits := range l.Limits {
// reflects the fieldName in cobra.Command struct
reflectValue := reflect.ValueOf(cmd).Elem().FieldByName(fieldName)
Expand All @@ -52,7 +59,7 @@ func (l *Length) Validate(cmd *cobra.Command) []validator.ValidationError {
}

// validate fieldName
err := validateField(cmd, limits, reflectValue.String(), cmd.CommandPath(), fieldName, l.Verbose)
err := validateField(cmd, limits, reflectValue.String(), cmd.CommandPath(), fieldName, l.RuleOptions.Verbose)
if err.Err != nil {
errors = append(errors, err)
}
Expand Down
13 changes: 10 additions & 3 deletions validator/rules/must_exist.go
Expand Up @@ -21,15 +21,22 @@ var MustExistRule = "MUST_EXIST_RULE"
// MustExist is a struct that provides
// Fields defined for MustExist validation
type MustExist struct {
Verbose bool `json:"Verbose"`
Fields map[string]bool `json:"Fields"`
RuleOptions validator.RuleOptions
Fields map[string]bool `json:"Fields"`
}

// Validate is a method of type Rule Interface
// which returns validation errors
func (m *MustExist) Validate(cmd *cobra.Command) []validator.ValidationError {
var errors []validator.ValidationError

// if command needs to be ignored
if val, ok := m.RuleOptions.SkipCommands[cmd.CommandPath()]; ok {
if val {
return errors
}
}

for field, isTrue := range m.Fields {
// reflects the field in cobra.Command struct
reflectValue := reflect.ValueOf(cmd).Elem().FieldByName(field)
Expand All @@ -42,7 +49,7 @@ func (m *MustExist) Validate(cmd *cobra.Command) []validator.ValidationError {

// validate field and append errors
if isTrue {
errors = append(errors, validateByType(cmd, &reflectValue, field, cmd.CommandPath(), m.Verbose)...)
errors = append(errors, validateByType(cmd, &reflectValue, field, cmd.CommandPath(), m.RuleOptions.Verbose)...)
}
}
return errors
Expand Down
10 changes: 9 additions & 1 deletion validator/rules/use_matches.go
Expand Up @@ -18,7 +18,8 @@ var UseMatchesRule = "USE_MATCHES_RULE"

// UseMatches defines Regexp to be compared
type UseMatches struct {
Regexp string `json:"Regexp"`
RuleOptions validator.RuleOptions
Regexp string `json:"Regexp"`
}

// Validate is a method of type Rule Interface
Expand All @@ -27,6 +28,13 @@ type UseMatches struct {
func (u *UseMatches) Validate(cmd *cobra.Command) []validator.ValidationError {
var errors []validator.ValidationError

// if command needs to be ignored
if val, ok := u.RuleOptions.SkipCommands[cmd.CommandPath()]; ok {
if val {
return errors
}
}

r, err := regexp.Compile(u.Regexp)
if err != nil {
errors = append(errors, validator.ValidationError{Name: "given regexp is invalid", Err: ErrInvalidRegexp, Rule: UseMatchesRule, Cmd: cmd})
Expand Down
7 changes: 7 additions & 0 deletions validator/validator.go
Expand Up @@ -4,6 +4,13 @@ import (
"github.com/spf13/cobra"
)

// RuleOptions is present in each rule
// to control the options limited to the rule
type RuleOptions struct {
Verbose bool
SkipCommands map[string]bool
}

// ValidationError is a default validation error
type ValidationError struct {
Name string
Expand Down
31 changes: 30 additions & 1 deletion website/docs/validator.md
Expand Up @@ -42,4 +42,33 @@ for _, errs := range validationErr {
t.Errorf("%s: cmd %s: %s", errs.Rule, errs.Cmd.CommandPath(), errs.Name)
}
}
```
```

## Ignore Commands
Sometimes during development, you want to pass the tests for certain commands, but at the same time use Validator for tests. Validation can be skipped/ignored for the commands, mentioned in the validator configuration.
To ignore the commands you need to specify the path of the command in validator configuration.

Skip single command `mycli actions create`
```go
ValidatorOptions: rules.ValidatorOptions{
SkipCommands: map[string]bool{"mycli actions create": true},
},
```

2. Skip the command including all children
```go
ValidatorOptions: rules.ValidatorOptions{
SkipChildren: map[string]bool{"mycli": true},
},
```

3. Skip the command for specific rule
```go
Length: rules.Length{
RuleOptions: validator.RuleOptions{
SkipCommands: map[string]bool{"mycli actions create": true},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ankithans is that ok?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes.. this looks good!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

example had only create command so I wasn't sure if you are using cobra paths for this

},
Limits: map[string]rules.Limit{
"Use": {Min: 1},
},
},