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
refactor RuleConfig + overriding default values #105
Conversation
5aa30a7
to
4e5ba58
Compare
validator/rules/executor.go
Outdated
var verbose bool | ||
if config != nil && config.Verbose { | ||
verbose = true | ||
if err := mergo.Merge(&config.Length, defaultConfig.Length); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why we would merge specific rules. Can we merge all config paths?
validator/rules/executor.go
Outdated
var verbose bool | ||
if config != nil && config.Verbose { | ||
verbose = true | ||
if err := mergo.Merge(&config.Length, defaultConfig.Length); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think overal config is confusing. WE should either:
- Add rules field with array of IRule
- Put string Rule after each rule(not like it personally)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
okay, so I really need to rethink the architecture, as everything is dependent on config. I will refactor it, keeping the functionality same!
I have one more concern - here we don't really need validator package, shall we do everything under rules package? But this can be changed while doing above refactoring!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes.. No pressure. Take it easy
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the aim here is to merge two config files into one giving precedence to a passed in file but keeping default values were no values are passed in and we are already using mergo
. We could try using merge with the transformer: mergo.Merge(&config.Length, defaultConfig.Length, mergo.WithOverride)
. This should keep config with first values whilst only updating fields passed in from defaultConfig.Length
.
Speaking of .Length
is this the best name for this field? is it a validated ( element size wise ) map of config fields? I may just not be getting it either, in which case my suggestion may not be applicable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I got you point @dimakis. But we want to keep defaultConfig.Length as it is and merge config.Length into it. We can do opposite as you suggested but then we need to do *config = *defaultConfig
, extra lines of code. This can be avoided I'd say. And btw this code might change as there is some major refactoring going on.
Speaking of .Length is this the best name for this field? is it a validated ( element size wise ) map of config fields? I may just not be getting it either, in which case my suggestion may not be applicable.
Length is a struct of Limits (that is map[string]Limit) and Limit is struct consisting of Max, Min length of field(int value). We can think over the names again.
Why we would merge specific rules. Can we merge all config paths?
Problem was mergo cannot merge 2 slices. It can only merge structs and maps. And the previous method also had similar problems.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I got you point @dimakis. But we want to keep defaultConfig.Length as it is and merge config.Length into it. We can do opposite as you suggested but then we need to do
*config = *defaultConfig
, extra lines of code. This can be avoided I'd say. And btw this code might change as there is some major refactoring going on.It may be one extra assignment, but it negates the need to check for duplication of values in a slice ->
removeDuplicates()
becomes obsolete.
+1 on trying to find better fitting names
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
great work @ankithans 👍
validator/rules/executor.go
Outdated
var verbose bool | ||
if config != nil && config.Verbose { | ||
verbose = true | ||
if err := mergo.Merge(&config.Length, defaultConfig.Length); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the aim here is to merge two config files into one giving precedence to a passed in file but keeping default values were no values are passed in and we are already using mergo
. We could try using merge with the transformer: mergo.Merge(&config.Length, defaultConfig.Length, mergo.WithOverride)
. This should keep config with first values whilst only updating fields passed in from defaultConfig.Length
.
Speaking of .Length
is this the best name for this field? is it a validated ( element size wise ) map of config fields? I may just not be getting it either, in which case my suggestion may not be applicable.
Is that ready for review? |
Not yet, I will ping you, fixing an issue after refactor. |
94cc90c
to
a0ffd91
Compare
One thing left in this PR ie. overriding. I am stuck and trying to figure out the way to do this! Mergo cannot be used. Merge thisRuleConfig{
Rules: []rules.Rules{
&rules.Length{Verbose: true, Limits: map[string]rules.Limit{"Use": {Min: 100}}},
},
} into thisRuleConfig{
Rules: []Rules{
&Length{
Verbose: false,
Limits: map[string]Limit{
"Use": {Min: 2},
"Short": {Min: 15},
"Long": {Min: 50},
"Example": {Min: 50},
}},
&MustExist{Verbose: false, Fields: []string{"Use", "Short", "Long", "Example"}},
},
} Result should beRuleConfig{
Rules: []Rules{
&Length{
Verbose: true,
Limits: map[string]Limit{
"Use": {Min: 100},
"Short": {Min: 15},
"Long": {Min: 50},
"Example": {Min: 50},
}},
&MustExist{Verbose: false, Fields: []string{"Use", "Short", "Long", "Example"}},
},
} |
@ankithans do you have an example of how you are trying to use it? can you push it so i can have a look? |
@dimakis this is the example I am using for testing https://github.com/aerogear/charmil/tree/ankithans/issue104/validator/example and here we need to merge defaultConfig & config(given by user) https://github.com/aerogear/charmil/blob/ankithans%2Fissue104/validator/rules/executor.go#L110 |
validator/rules/executor.go
Outdated
log.Fatal(errr) | ||
} | ||
} | ||
// TODO: Override default configuration |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ankithans All I'm seeing is an empty TODO, have/ can you push up your implementation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure give me sometime, I will ping you once done. i want to push some working code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not essential for it to be working, just change the PR to a [WIP]. I enjoy that you don't want to push non functional code, but if you are still facing issues push it and I'll see if I can help. Don't get too hung up on a single piece of the puzzle. 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thank you so much!!!
Now Length rule is working fine! as it has a map field Limits
(map[string]Limit) (Limit -> {Min, Max int}) to merge
but MustExist cannot be merged with mergo as it is a slice. One solution is to convert
Fields []string
to
Fields map[string]bool
what do you suggest?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did it this way, changed the Fields []string to Fields map[string]bool. This way user can disable the default MustExist fields(which was not possible previously)
and easy to merge with mergo 😀
b3c76e2
to
a94c0f1
Compare
abdfc14
to
8ca36df
Compare
@ankithans configuration and defaults are common problem. Please reseach this topic first. 1 min Google returned me this: Although I'm not fan of libraries Please check how golangci lint does it |
Good start is to query google properly: |
@wtrocki How I did this now is according to #110 vali := rules.RuleConfig{
Verbose: false,
Rules: []rules.Rules{
&rules.Length{Limits: map[string]rules.Limit{"Use": {Min: 2}}},
&rules.MustExist{Fields: map[string]bool{"Long": false}},
},
} And we have solved the merging of the default config & user provided config in efficient way, using mergo if err := mergo.Merge(&defaultConfig, config, mergo.WithSliceDeepCopy); err != nil {
log.Fatal(err)
}
*config = defaultConfig seen golangci-lint configuration file, it also accepts diff config for each linter. |
I think problem is that we trying doing something different: We doing merge that is tricky in go, but can do default values for structure which is supported by go core (with json) or covered by some libs |
Okay, I got your point now! But will it work with slice? need to initialize slice of Rule Interface. |
Just have all as structure. Currenr structure have unneded complexity/slices etc. Have structure that is parsable to json/yaml Instead:
this
KISS and DRY in action. Not sure why we overthinking it so much. If we write rule config as json golang can generate structure for you.
|
No issues 😄, But I am bugging you again 😅
If we follow above struct we would need to add rules in ruleExecutor manually, and that was what we wanted to solve. For maintainability we did choose array of Interface of Rules. Previously we were doing this only, accepting struct... |
Rebase will solve this :D |
eaa082e
to
fbdaf5d
Compare
validator/rules/config.go
Outdated
func ValidatorConfigToRuleConfig(validatorConfig *ValidatorConfig, ruleConfig *RuleConfig) { | ||
defaultVerbose := validatorConfig.ValidatorOptions.Verbose | ||
|
||
defaultConfigJson := `{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wrong place to put default config :)
validator/rules/config.go
Outdated
"ValidatorOptions": { | ||
"Verbose": false | ||
}, | ||
"ValidatorRules": { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The structure will be much easier to maintain when some external rules will be provided etc.
validator/rules/config.go
Outdated
log.Fatal(err) | ||
} | ||
|
||
configHelper.Length.Verbose = defaultVerbose |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is that about?
Very bad smell in code. Direct assignment. Why not make rule working with the validationOptions?
validator/rules/executor.go
Outdated
func (config *RuleConfig) ExecuteRules(cmd *cobra.Command) []validator.ValidationError { | ||
// ExecuteRulesInternal executes all the rules | ||
// provided by ruleConfig | ||
func (ruleConfig *RuleConfig) ExecuteRulesInternal(cmd *cobra.Command, userValidatorConfig *ValidatorConfig) []validator.ValidationError { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you dissagree with what I have proposed?
In what I have proposed config should not have any method (not to mention internal)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@wtrocki addressed comments
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This somewhat ended up being more convoluted than before.
Do you have any problems with what was proposed? Any challenges?
@ankithans would it be possible to update the documentation and example code? |
validator/example/cmd_test.go
Outdated
var vali rules.RuleConfig | ||
validationErr := vali.ExecuteRules(cmd) | ||
ruleCfg := rules.ValidatorConfig{ | ||
ValidatorOptions: rules.ValidatorOptions{Verbose: false}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to specify value that is used by default?
ValidatorOptions: rules.ValidatorOptions{Verbose: false}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@wtrocki review required
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Of the whole PR? or just this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
whole
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
now merged
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🙈
Closes #104
Description
Now Rule Config can be overridden by the user
Type of change