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

refactor RuleConfig + overriding default values #105

Merged
merged 12 commits into from Jul 5, 2021
Merged

Conversation

ankithans
Copy link
Member

@ankithans ankithans commented Jul 1, 2021

Closes #104

Description

Now Rule Config can be overridden by the user

  • refactor ruleConfig and rules interface
  • overriding of rules by the user

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • Documentation change

@ankithans ankithans added the bug Something isn't working label Jul 1, 2021
@ankithans ankithans requested a review from wtrocki July 1, 2021 10:35
@ankithans ankithans self-assigned this Jul 1, 2021
var verbose bool
if config != nil && config.Verbose {
verbose = true
if err := mergo.Merge(&config.Length, defaultConfig.Length); err != nil {
Copy link
Contributor

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?

var verbose bool
if config != nil && config.Verbose {
verbose = true
if err := mergo.Merge(&config.Length, defaultConfig.Length); err != nil {
Copy link
Contributor

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)

Copy link
Member Author

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!

Copy link
Contributor

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

Copy link
Contributor

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.

Copy link
Member Author

@ankithans ankithans Jul 2, 2021

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.

Copy link
Contributor

@dimakis dimakis Jul 2, 2021

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

Copy link
Contributor

@dimakis dimakis left a comment

Choose a reason for hiding this comment

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

great work @ankithans 👍

var verbose bool
if config != nil && config.Verbose {
verbose = true
if err := mergo.Merge(&config.Length, defaultConfig.Length); err != nil {
Copy link
Contributor

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.

@ankithans ankithans linked an issue Jul 2, 2021 that may be closed by this pull request
@ankithans ankithans changed the title fix: rule config overriding refactor RuleConfig + overriding default values Jul 2, 2021
@wtrocki
Copy link
Contributor

wtrocki commented Jul 2, 2021

Is that ready for review?

@ankithans
Copy link
Member Author

Not yet, I will ping you, fixing an issue after refactor.

@ankithans ankithans force-pushed the ankithans/issue104 branch 3 times, most recently from 94cc90c to a0ffd91 Compare July 3, 2021 06:10
@ankithans ankithans mentioned this pull request Jul 3, 2021
9 tasks
@ankithans
Copy link
Member Author

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 this

RuleConfig{
	Rules: []rules.Rules{
		&rules.Length{Verbose: true, Limits: map[string]rules.Limit{"Use": {Min: 100}}},
	},
}

into this

RuleConfig{
	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 be

RuleConfig{
	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"}},
	},
}

@dimakis
Copy link
Contributor

dimakis commented Jul 3, 2021

@ankithans do you have an example of how you are trying to use it? can you push it so i can have a look?

@ankithans
Copy link
Member Author

ankithans commented Jul 3, 2021

@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

log.Fatal(errr)
}
}
// TODO: Override default configuration
Copy link
Contributor

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?

Copy link
Member Author

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.

Copy link
Contributor

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. 👍

Copy link
Member Author

@ankithans ankithans Jul 3, 2021

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?

Copy link
Member Author

@ankithans ankithans Jul 3, 2021

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 😀

@ankithans
Copy link
Member Author

@wtrocki @dimakis ready for review

@wtrocki
Copy link
Contributor

wtrocki commented Jul 3, 2021

@ankithans configuration and defaults are common problem. Please reseach this topic first.

1 min Google returned me this:
https://github.com/containous/staert

Although I'm not fan of libraries

Please check how golangci lint does it
https://github.com/golangci/golangci-lint

@ankithans
Copy link
Member Author

ankithans commented Jul 3, 2021

@wtrocki How I did this now is according to #110
User API looks like this, as you and @jackdelahunt also suggested.

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
https://github.com/aerogear/charmil/blob/ankithans%2Fissue104/validator/rules/executor.go#L96

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.
Also got much help from this issue opened in mergo a long time back darccio/mergo#121 Many were facing the same issue as I was!

@wtrocki
Copy link
Contributor

wtrocki commented Jul 3, 2021

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

@ankithans
Copy link
Member Author

Okay, I got your point now! But will it work with slice? need to initialize slice of Rule Interface.
How will it work with Min in length, as that will be different for each attribute in cobra.
I think it will be better to initialize manually in the array of rules, what do you think?

@wtrocki
Copy link
Contributor

wtrocki commented Jul 3, 2021

Just have all as structure.

Currenr structure have unneded complexity/slices etc.

Have structure that is parsable to json/yaml

Instead:

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}},
	},
}

this

ValidatorConfig{
 RuntimeOptions,
Rules:{
 LenghtRule, 
 MustExist
}
}
LenghtRule{
  Max int32
  Min int32
}

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.

Witten on phone while feeding baby. Sorry for typos

@ankithans
Copy link
Member Author

ankithans commented Jul 3, 2021

Witten on phone while feeding baby. Sorry for typos

No issues 😄, But I am bugging you again 😅

ValidatorConfig{
 RuntimeOptions,
Rules:{
 LenghtRule, 
 MustExist
}
}
LenghtRule{
  Max int32
  Min int32
}

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...

@wtrocki
Copy link
Contributor

wtrocki commented Jul 5, 2021

Rebase will solve this :D

func ValidatorConfigToRuleConfig(validatorConfig *ValidatorConfig, ruleConfig *RuleConfig) {
defaultVerbose := validatorConfig.ValidatorOptions.Verbose

defaultConfigJson := `{
Copy link
Contributor

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 :)

"ValidatorOptions": {
"Verbose": false
},
"ValidatorRules": {
Copy link
Contributor

@wtrocki wtrocki Jul 5, 2021

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.

log.Fatal(err)
}

configHelper.Length.Verbose = defaultVerbose
Copy link
Contributor

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?

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 {
Copy link
Contributor

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)

Copy link
Member Author

Choose a reason for hiding this comment

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

@wtrocki addressed comments

Copy link
Contributor

@wtrocki wtrocki left a 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 ankithans requested a review from wtrocki July 5, 2021 12:27
@wtrocki
Copy link
Contributor

wtrocki commented Jul 5, 2021

@ankithans would it be possible to update the documentation and example code?

var vali rules.RuleConfig
validationErr := vali.ExecuteRules(cmd)
ruleCfg := rules.ValidatorConfig{
ValidatorOptions: rules.ValidatorOptions{Verbose: false},
Copy link
Contributor

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?

Suggested change
ValidatorOptions: rules.ValidatorOptions{Verbose: false},

Copy link
Member Author

Choose a reason for hiding this comment

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

@wtrocki review required

Copy link
Contributor

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?

Copy link
Member Author

Choose a reason for hiding this comment

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

whole

Copy link
Member Author

Choose a reason for hiding this comment

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

now merged

Copy link
Contributor

Choose a reason for hiding this comment

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

🙈

@ankithans ankithans linked an issue Jul 5, 2021 that may be closed by this pull request
@ankithans ankithans requested a review from wtrocki July 5, 2021 15:58
@wtrocki wtrocki merged commit b2c727b into main Jul 5, 2021
@wtrocki wtrocki deleted the ankithans/issue104 branch July 5, 2021 16:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
3 participants