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

Closes #660 added excluded with and without #664

Merged
merged 3 commits into from Sep 27, 2020
Merged

Closes #660 added excluded with and without #664

merged 3 commits into from Sep 27, 2020

Conversation

erma07
Copy link
Contributor

@erma07 erma07 commented Sep 15, 2020

Fixes #660

Make sure that you've checked the boxes below before you submit PR:

  • Tests exist or have been written that cover this particular change.

Change Details:

  • added baked_in.go excluded_with function (if there is x then there must not be a y)
  • added baked_in.go excluded_with_all function
  • added baked_in.go excluded_without function ( is that if x is not there then y must not be there )
  • added baked_in.go excluded_without_all function
  • added test cases

@go-playground/admins

@andig
Copy link

andig commented Sep 15, 2020

@erma07 see #665 for my attempt. Might be wrong but I guess you could steal the tests from there? Happy to close mine...

@andig
Copy link

andig commented Sep 18, 2020

Ping @deankarn any chance this PR could get merged?

@andig
Copy link

andig commented Sep 25, 2020

Is there any chance to consider this PR for merging?

@deankarn deankarn merged commit b1cccee into go-playground:master Sep 27, 2020
@andig
Copy link

andig commented Sep 27, 2020

Much appreciated, thank you!

@@ -1416,6 +1453,18 @@ func requiredWithout(fl FieldLevel) bool {
return true
}

// RequiredWithoutAll is the validation function
Copy link
Contributor

Choose a reason for hiding this comment

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

This should be ExcludedWithoutAll.

Copy link

Choose a reason for hiding this comment

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

You could open a PR ;)

Copy link
Contributor

Choose a reason for hiding this comment

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

Sure, I'll do it tomorrow :)

Copy link
Contributor

Choose a reason for hiding this comment

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

@@ -1383,6 +1387,18 @@ func requireCheckFieldKind(fl FieldLevel, param string, defaultNotFoundValue boo
}
}

// ExcludedWith is the validation function
// The field under validation must not be present or is empty if any of the other specified fields are present.
func excludedWith(fl FieldLevel) bool {
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm trying to use excluded_with and not getting the expected behaviour. I only passes the validation if I set all the fields with the excluded_with validation, which is kind of the opposite of what I expected. Example:

type Example struct {
	AAA  *aaaStruct  `validate:"excluded_with=BBB,omitempty,dive" json:"aaa,omitempty"`
	BBB  *bbbStruct  `validate:"excluded_with=AAA,omitempty,dive" json:"bbb,omitempty"`
}

If I set AAA, I get

"Key: 'Example.BBB' Error:Field validation for 'BBB' failed on the 'excluded_with' tag"

Copy link

Choose a reason for hiding this comment

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

Do you have a play we could look at? Can you simplify it further (dive, omitempty) to expose the issue?

/cc @yaliv maybe you can spot the mistake?

Copy link
Contributor

Choose a reason for hiding this comment

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

Ok, I'll try to confirm it and make sure I'm not messing up somewhere. I'll get back to you tomorrow!

Copy link

Choose a reason for hiding this comment

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

excluded_with validator works fine with basic/primitive fields (string, int).

I never set any validations for the sub-structs, because in my case I use json.Unmarshal() before validate.
Using json.Unmarshal() will always populate the sub-structs even if they do not present in the source JSON bytes.

This leads me to ask a fundamental question: "What is an empty struct value?"

  • Is it nil?
  • Is it struct{} (with zero fields)?

Unlike map[string]interface{}, which can hold "dynamic" fields, struct has a static set of fields.
If I define a struct with some fields in it (e.g. MyStruct), and then I create a struct value from it with its default (e.g. var test MyStruct), I still have access to those fields.

@jfrancisco0 if the built-in validators do not fit with your case, you can try creating some struct-level validations (see example). Plus, using this technique can minimize overhead, because you can validate multiple fields at once.

Copy link

@andig andig Oct 7, 2020

Choose a reason for hiding this comment

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

@yaliv in this case the struct pointers are nil and hence definitely empty. The source code/docs confirm this. This is similar to a string that is treated as „not existing“ when it has the zero value.

Copy link
Contributor

Choose a reason for hiding this comment

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

I can confirm though that a field of type bool does not fail the validation, giving some strength to the hypothesis of structs behaving differently.

Copy link

Choose a reason for hiding this comment

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

@andig if I write just var test *MyStruct, then test would be nil, but if I write var test MyStruct, then &test would not be nil.
So struct pointers have possible nil value.

Yes, I can see that fields with struct type and struct pointer type have a complete opposite behavior against primitive types.
Using the excluded_with validator on 2 fields, each mentioning each other:

  • string type: both have values --> 2 validation errors; one has value --> validation passed 😃
  • struct type: they always have values --> validation passed ☹️
  • struct pointer type: both have values --> validation passed; one has value --> 1 validation error; both empty --> 2 validation errors ☹️

Copy link

Choose a reason for hiding this comment

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

so to summarise: there is an issue with struct pointers and excluded* validations. Sounds like it would be good to open a new issue with minimal example to reproduce. Might also be good to check required* behaviour for struct pointers too, as the pr code was a close copy.

Copy link

Choose a reason for hiding this comment

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

@yaliv did you by chance open follow-up issue?

Copy link

Choose a reason for hiding this comment

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

@andig sorry, I haven't done it yet.
I see this validator is quite complex, so I'm not sure if the problem is fixed it will not break other parts (e.g. the primitive types) or it will not cost performance.
But yes, it's still a good issue to be posted. Before it is fixed, maybe someone has a running workaround 😉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add support for denied_with and denied_without
5 participants