diff --git a/validator/example/cmd.go b/validator/example/cmd.go index 11a2c3b..ffb745d 100644 --- a/validator/example/cmd.go +++ b/validator/example/cmd.go @@ -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) { @@ -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.", }, @@ -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, @@ -57,7 +66,7 @@ func NewCommand() *cobra.Command { cmd.AddCommand(cm.name) } - return cmd + return cmd100 } func main() { diff --git a/validator/example/cmd_test.go b/validator/example/cmd_test.go index ef2d1e9..3267fd7 100644 --- a/validator/example/cmd_test.go +++ b/validator/example/cmd_test.go @@ -3,6 +3,7 @@ package main import ( "testing" + "github.com/aerogear/charmil/validator" "github.com/aerogear/charmil/validator/rules" ) @@ -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: `^[^-_+]+$`}, }, } diff --git a/validator/rules/config.go b/validator/rules/config.go index 5e98f91..e9b5488 100644 --- a/validator/rules/config.go +++ b/validator/rules/config.go @@ -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 @@ -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}, @@ -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}, }, }, } diff --git a/validator/rules/executor.go b/validator/rules/executor.go index 5bc28a7..f0133c7 100644 --- a/validator/rules/executor.go +++ b/validator/rules/executor.go @@ -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) @@ -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 { diff --git a/validator/rules/length.go b/validator/rules/length.go index 0171385..fb39267 100644 --- a/validator/rules/length.go +++ b/validator/rules/length.go @@ -32,8 +32,8 @@ 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 @@ -41,6 +41,13 @@ type Length struct { 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) @@ -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) } diff --git a/validator/rules/must_exist.go b/validator/rules/must_exist.go index 5018944..d53ff18 100644 --- a/validator/rules/must_exist.go +++ b/validator/rules/must_exist.go @@ -21,8 +21,8 @@ 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 @@ -30,6 +30,13 @@ type MustExist struct { 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) @@ -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 diff --git a/validator/rules/use_matches.go b/validator/rules/use_matches.go index d855924..a9b03a6 100644 --- a/validator/rules/use_matches.go +++ b/validator/rules/use_matches.go @@ -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 @@ -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}) diff --git a/validator/validator.go b/validator/validator.go index e14f79b..0e74641 100644 --- a/validator/validator.go +++ b/validator/validator.go @@ -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 diff --git a/website/docs/validator.md b/website/docs/validator.md index 10cd2e5..7614e72 100644 --- a/website/docs/validator.md +++ b/website/docs/validator.md @@ -42,4 +42,33 @@ for _, errs := range validationErr { t.Errorf("%s: cmd %s: %s", errs.Rule, errs.Cmd.CommandPath(), errs.Name) } } -``` \ No newline at end of file +``` + +## 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}, + }, + Limits: map[string]rules.Limit{ + "Use": {Min: 1}, + }, +},