-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
The behavior or required
tag with empty structures was changed
#1142
Comments
First off, sorry to hear that, I know how frustrating unexpected breaking changes can be. Just to clarify a bit, the behavior you mentioned was previously undefined since it was not implemented. I'm not sure how feasible it would be for you, but removing the validation tags altogether would replicate the behavior seen in the previous versions. |
Yes sorry to hear that it has caused you issues. The new logic though in this case has corrected the functionality previous to v10.15.0. It should have been failing before given the definition of required which validates if a value is not its default value which Component is in this case. Along with @MysteriousPotato ‘s suggestion you could also make Component a pointer in this case if set which should also pass the validation. Out of curiosity what value is Component the way it is defined? Or is it simply as you stated and issue with some code generation somewhere? I would also be curious to learn about some of the generic use cases this could be an issue for, are you able to provide an example? |
Example real production code which broken after updating to 10.15.x: type CutOff1 struct {
DaysBefore int
}
type DeliveryDay1 struct {
CanDeliver bool
CutOff *CutOff1 `json:"cutOff,omitempty" validate:"omitempty,dive"`
}
type DeliveryDays1 struct {
Monday DeliveryDay1 `json:"mon" validate:"required,dive"`
}
func TestRequiredEmptyComponent(t *testing.T) {
v := validator.New()
val := DeliveryDays1{
Monday: DeliveryDay1{
CanDeliver: false,
},
}
if err := v.Struct(val); err != nil {
t.Error(err)
}
} Because CanDeliver is false (empty value for golang type bool), validator thinks the whole structure is empty. |
Hi guys, we're facing the same issue on v10.15.0. A lot of our code fails validation of http request bodies when body end up being equal to zero value. Here is our simplified example:
We use validation like this in all our services, so it will be a big lift for us to fix that on our side. |
@deankarn I'm not sure if this alone warrants a major version upgrade, but we could revert #1122 and include it in the next version. A few users have already encountered this issue and I fear this may just be the tip of the iceberg. I'd be willing to tackle the task if you agree with the proposition. I believe the issue raised by @TelpeNight regarding generics can be illustrated as follows: func main() {
type Option[T any] struct {
V T `validate:"required"`
}
v := validator.New()
if err := v.Struct(Option[struct{}]{}); err != nil {
// Did not return an error prior to v10.15.0
}
} |
Hi all, first off I want to say I'm sorry this is causing so many issues and all options are on the table. I also think it's important to all be on the same page about the changes implemented in order to have an informed discussion before deciding on next actions. let's start with the definition of
So The behaviour of Why are people having these issues now after #1122? To summarize so far For @asv 's, @phoenix2x's, @TelpeNight and @jonathan-innis' here examples are actually being applied as defined. My hesitation in reverting this is that it hides some very nasty bugs in others code, whereby it appears to be working correctly but in fact is not working at all. An example of this is @jonathan-innis' issue which boils down to this line here where it's trying to validate a minimum 15 minute value of a custom duration type. Although it has caused it to start failing in a noticeable way, that's actually probably a good thing because the validation wasn't working at all before, likely causing undefined behaviour within the application/system itself potentially causing far worse issues. options + pros & conns
Which is the lesser of two evils? IMO undefined behaviour is far worse and I'd lean towards option 2 because of that. Though experiments For option 1 above if I cut a new release, I then will put the Final thoughts I for one have had my code broken by Go STD library behavioural changes many many times and so understand and feel your pain. |
Hi all, first of all, thank you for your work, go-validator is a wonderful library. But I see a problem here:
I'm not aware of it, but what is the problem to implement "fair required"? Any tickets, documentation explaining the problem? |
Hey @asv thanks you for discussing :)
Maybe that's true, naming is hard. But will also say in
I would take the opposite side of the argument and would say it is important for a few reasons:
Yes the behaviour is different in this one specific way, but it comes in the form of a correction to validation behaviour and not an unintended change.
I very much wish this statement was always true, unfortunately though corrections happen to code all the time which changes behaviour, in the form of a correction. Just because behaviour could change in anyones code is not 100% cut and dry must make a major version bump, here's one example: #1146 which corrected how floats were parsed, which was a behavioural change which could cause some people programs to change behaviour and now fail validations, however they will now be correctly failing. I see this correction to be very similar to the above because it's corrected calling the validations that were asked for, the difference is it's at a higher level and applies to more than just one validation.
I am unsure what you mean here? The only thing I can think of is that in Go there is no concept of an uninitialized variable/value as in C. Again still thinking about all of this and considering everything. |
Hi guys, Just want to add our opinion here. The problem with Option 2 is that it introduces a significant risk of producing bugs in existing projects. The change is very subtle so it's not easy to catch it with tests, in our case it was not found by unit tests or integration tests. So the question is do we want to sneakily break a potentially large number of projects that were working before and fix some number of projects that never worked? Or do we want to cut a Major version with big patch notes that explain the behavior and allow people to be mindful about this upgrade? I personally don't see any drawbacks with a Major version except that it might take more time for some people to adopt it, which should be fine I suppose. Thank you for maintaining the library:) |
Hey all, been thinking and losing a lot of sleep over this and think we can meet in the middle with a compromise. I've created this POC PR, still need to add back a few tests and docs but mostly there, which re-implements the struct level validations in a different way that supports more built-in tags. The gist of it being I've added a special case which will ignore the Reasoning is I can understand and appreciate the misuse of the I'd like to hear your thoughts on this compromise as I don't wish to release a new Major version at this time. |
Hey all, PR is now ready for review, tests, docs, etc.. all added back and even new way to opt-in to nee behaviour #1150 Going to leave the PR open for your, or anyones, comment for a few days but then will merge if there's no issue found. |
Hi @deankarn I think I have a related issue - but may be stemming from same thing, not quite sure. I'm happy to write as separate issue if needed. It looks like the Describe("V10.15 bug", func() {
type Thing struct {
Name string `validate:"required"`
Token uuid.UUID
}
It("should not error on validation", func() {
var err error
Expect(func() {
err = validate.Struct(Thing{Name: "test"})
}).NotTo(Panic())
Expect(err).To(BeNil())
})
}) results in output:
I can work around this by explicitly using |
@bradleygore I tried to reproduce it using both I think it's unlikely that this is related to the new changes. Maybe a new issue would be better. Is it possible you're using Edit: Turns out it was related to the new changes. I did not realize returning |
@MysteriousPotato I didn't even think of the custom TypeFunc registration. You're right, we do have one - and it does return validCop.RegisterCustomTypeFunc(func(field reflect.Value) interface{} {
if uid, ok := field.Interface().(uuid.UUID); ok {
if uid == uuid.Nil {
return nil // <--------
}
return uid.String()
}
return field
}, uuid.New()) It has been working with the Thanks for taking the time to help think this through! 😃 |
@bradleygore This is what I initially thought. But then I saw #1155, which mentions an example in the docs that does return nil. |
@bradleygore this should be resolved thanks to @MysteriousPotato 's PR in release https://github.com/go-playground/validator/releases/tag/v10.15.3 |
Awesome - thanks to much @MysteriousPotato and @deankarn 🥳 |
Hello! Sorry being out of discussion. I think that from the practical point of view making this change optional is the best solution. |
WHEW lads, just got bit hard by this. So, @deankarn, when you say above:
(re: this being a v11.x.x as opposed to Assuming For future reference, I want to refer you to this:
This versioning scheme is not only an industry standard, it's explicitly in Golang docs:
Having predictable versioning, and what each level means, is incredibly important when releasing libraries. Please do keep this in mind for future releases, and I strongly encourage you to consider following this standard moving forward. |
Package version: v10.15.0
Issue:
The behavior or
required
tag with empty structures was changed. In v10.14.1 the code below passes validation just fine.Starting from version v10.15.0 it fails with error:
It would be nice not to break backward compatibility. I know that this is a rare case, but we currently have problems with some autogenerated code. This is also a possible issue for some usecases of go generics.
Code sample, to showcase or reproduce:
The text was updated successfully, but these errors were encountered: