From ffaa9da9a635367a24bf8ed86a1ed5e799281a51 Mon Sep 17 00:00:00 2001 From: Ankit Hans Date: Tue, 6 Jul 2021 13:45:59 +0530 Subject: [PATCH 1/3] feat: add ignore command option --- validator/example/cmd.go | 1 - validator/example/cmd_test.go | 3 +++ validator/rules/config.go | 3 ++- validator/rules/executor.go | 27 +++++++++++++++++---------- website/docs/validator.md | 10 ++++++++++ 5 files changed, 32 insertions(+), 12 deletions(-) diff --git a/validator/example/cmd.go b/validator/example/cmd.go index 11a2c3b..c09271f 100644 --- a/validator/example/cmd.go +++ b/validator/example/cmd.go @@ -18,7 +18,6 @@ func NewCommand() *cobra.Command { cmd := &cobra.Command{ Use: "cmd0", - Short: "This is the short", 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) { diff --git a/validator/example/cmd_test.go b/validator/example/cmd_test.go index ef2d1e9..ed54076 100644 --- a/validator/example/cmd_test.go +++ b/validator/example/cmd_test.go @@ -12,6 +12,9 @@ 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{ + IgnoreCommands: map[string]bool{"cmd0": true}, + }, ValidatorRules: rules.ValidatorRules{ Length: rules.Length{ Limits: map[string]rules.Limit{ diff --git a/validator/rules/config.go b/validator/rules/config.go index 5e98f91..55a031b 100644 --- a/validator/rules/config.go +++ b/validator/rules/config.go @@ -17,7 +17,8 @@ type ValidatorConfig struct { // ValidatorOptions provide additional configurations // to the rules type ValidatorOptions struct { - Verbose bool `json:"Verbose"` + Verbose bool `json:"Verbose"` + IgnoreCommands map[string]bool `json:"IgnoreCommands"` } // ValidatorRules consists of all the rules diff --git a/validator/rules/executor.go b/validator/rules/executor.go index 5bc28a7..2fadcb0 100644 --- a/validator/rules/executor.go +++ b/validator/rules/executor.go @@ -30,13 +30,13 @@ func ExecuteRulesInternal(cmd *cobra.Command, ruleConfig *RuleConfig, userValida 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 +46,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.IgnoreCommands[cmd.Use]; ok { + if val { + return + } + } // traverse all rules and validate for _, rule := range ruleConfig.Rules { diff --git a/website/docs/validator.md b/website/docs/validator.md index 10cd2e5..f31fcec 100644 --- a/website/docs/validator.md +++ b/website/docs/validator.md @@ -42,4 +42,14 @@ for _, errs := range validationErr { t.Errorf("%s: cmd %s: %s", errs.Rule, errs.Cmd.CommandPath(), errs.Name) } } +``` + +## Ignore Commands +Validation for selected commands can be ignored, by passing the `Use` of commands to be skipped in `IgnoreCommands` attribute in ValidatorOptions +```go +ruleCfg := rules.ValidatorConfig{ + ValidatorOptions: rules.ValidatorOptions{ + IgnoreCommands: map[string]bool{"echo": true}, + }, +} ``` \ No newline at end of file From 4b82b8ff8fba61a0f4673e2f08da24b55396cada Mon Sep 17 00:00:00 2001 From: Ankit Hans Date: Wed, 7 Jul 2021 12:31:02 +0530 Subject: [PATCH 2/3] feat: implement skip command options --- validator/example/cmd.go | 14 ++++++++++++-- validator/example/cmd_test.go | 13 +++++++++++-- validator/rules/config.go | 15 ++++++++++----- validator/rules/executor.go | 10 +++++++++- validator/rules/length.go | 13 ++++++++++--- validator/rules/must_exist.go | 13 ++++++++++--- validator/rules/use_matches.go | 10 +++++++++- validator/validator.go | 7 +++++++ website/docs/validator.md | 31 +++++++++++++++++++++++++------ 9 files changed, 103 insertions(+), 23 deletions(-) diff --git a/validator/example/cmd.go b/validator/example/cmd.go index c09271f..ffb745d 100644 --- a/validator/example/cmd.go +++ b/validator/example/cmd.go @@ -18,6 +18,7 @@ func NewCommand() *cobra.Command { cmd := &cobra.Command{ Use: "cmd0", + 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) { @@ -27,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.", }, @@ -45,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, @@ -56,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 ed54076..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" ) @@ -13,15 +14,23 @@ func Test_ExecuteCommand(t *testing.T) { // default config can also be overrided ruleCfg := rules.ValidatorConfig{ ValidatorOptions: rules.ValidatorOptions{ - IgnoreCommands: map[string]bool{"cmd0": true}, + 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 55a031b..e9b5488 100644 --- a/validator/rules/config.go +++ b/validator/rules/config.go @@ -17,8 +17,9 @@ type ValidatorConfig struct { // ValidatorOptions provide additional configurations // to the rules type ValidatorOptions struct { - Verbose bool `json:"Verbose"` - IgnoreCommands map[string]bool `json:"IgnoreCommands"` + Verbose bool `json:"Verbose"` + SkipChildren map[string]bool `json:"SkipChildren"` + SkipCommands map[string]bool `json:"SkipCommands"` } // ValidatorRules consists of all the rules @@ -48,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}, @@ -57,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 2fadcb0..f0133c7 100644 --- a/validator/rules/executor.go +++ b/validator/rules/executor.go @@ -26,6 +26,13 @@ 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) @@ -36,6 +43,7 @@ func ExecuteRulesInternal(cmd *cobra.Command, ruleConfig *RuleConfig, userValida } 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 @@ -77,7 +85,7 @@ func executeRulesChildren(cmd *cobra.Command, info *validator.StatusLog, ruleCon func validate(cmd *cobra.Command, info *validator.StatusLog, ruleConfig *RuleConfig, userValidatorConfig *ValidatorConfig) { // if command needs to be ignored - if val, ok := userValidatorConfig.ValidatorOptions.IgnoreCommands[cmd.Use]; ok { + if val, ok := userValidatorConfig.ValidatorOptions.SkipCommands[cmd.CommandPath()]; ok { if val { return } 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 f31fcec..c46e684 100644 --- a/website/docs/validator.md +++ b/website/docs/validator.md @@ -45,11 +45,30 @@ for _, errs := range validationErr { ``` ## Ignore Commands -Validation for selected commands can be ignored, by passing the `Use` of commands to be skipped in `IgnoreCommands` attribute in ValidatorOptions +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. + +1. Skip single command ```go -ruleCfg := rules.ValidatorConfig{ - ValidatorOptions: rules.ValidatorOptions{ - IgnoreCommands: map[string]bool{"echo": true}, +ValidatorOptions: rules.ValidatorOptions{ + SkipCommands: map[string]bool{"cmd100 cmd0 subcmd01": true}, +}, +``` + +2. Skip the command including all children +```go +ValidatorOptions: rules.ValidatorOptions{ + SkipChildren: map[string]bool{"cmd100": true}, +}, +``` + +3. Skip the command for specific rule +```go +Length: rules.Length{ + RuleOptions: validator.RuleOptions{ + SkipCommands: map[string]bool{"cmd100": true}, }, -} -``` \ No newline at end of file + Limits: map[string]rules.Limit{ + "Use": {Min: 1}, + }, +}, From 49d4f6c4e690fb9a9ffb816a4fe32cb087df5e3f Mon Sep 17 00:00:00 2001 From: Wojciech Trocki Date: Wed, 7 Jul 2021 09:49:57 +0100 Subject: [PATCH 3/3] add reasonable examples --- website/docs/validator.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/website/docs/validator.md b/website/docs/validator.md index c46e684..7614e72 100644 --- a/website/docs/validator.md +++ b/website/docs/validator.md @@ -48,17 +48,17 @@ for _, errs := range validationErr { 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. -1. Skip single command + Skip single command `mycli actions create` ```go ValidatorOptions: rules.ValidatorOptions{ - SkipCommands: map[string]bool{"cmd100 cmd0 subcmd01": true}, + SkipCommands: map[string]bool{"mycli actions create": true}, }, ``` 2. Skip the command including all children ```go ValidatorOptions: rules.ValidatorOptions{ - SkipChildren: map[string]bool{"cmd100": true}, + SkipChildren: map[string]bool{"mycli": true}, }, ``` @@ -66,7 +66,7 @@ ValidatorOptions: rules.ValidatorOptions{ ```go Length: rules.Length{ RuleOptions: validator.RuleOptions{ - SkipCommands: map[string]bool{"cmd100": true}, + SkipCommands: map[string]bool{"mycli actions create": true}, }, Limits: map[string]rules.Limit{ "Use": {Min: 1},