Skip to content

add support to tag validateFn #1363

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

Merged
merged 15 commits into from
Apr 16, 2025

Conversation

peczenyj
Copy link
Contributor

Fixes Or Enhances

This Pull Requests adds a new tag called isvalid

If the field is marked with the validator tag isvalid, the type must implement the interface Validate() error and the return must be nil to be considered valid

A possible use case is: when dealing with Enumerations, the type can support a method Validate() error to check if the value is in specific the range defined. If we use enumer it generates a IsA<Type>() bool method that can be used to verify if the enumeration is valid or not, instead force the oneof tag (that needs to be always updated when we add one new value.

I wrote a pull request to add a Validate method on enumerations here and the interface Validate() error seems pretty common.

It may clash with existing tags that people may register, this is something that I don't know how to solve.

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

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

@go-playground/validator-maintainers

Sorry, something went wrong.

@peczenyj peczenyj requested a review from a team as a code owner January 17, 2025 12:53
@coveralls
Copy link

coveralls commented Jan 17, 2025

Coverage Status

coverage: 73.653% (+0.05%) from 73.607%
when pulling f7f7569 on peczenyj:add-support-to-tag-valid
into 0540a5e on go-playground:master.

@nodivbyzero
Copy link
Contributor

How does this isvalid differ from custom validators?
https://pkg.go.dev/github.com/go-playground/validator/v10#hdr-Custom_Validation_Functions

String string `validate:"is-awesome"`

@tizu69
Copy link

tizu69 commented Feb 11, 2025

How does this isvalid differ from custom validators? pkg.go.dev/github.com/go-playground/validator/v10#hdr-Custom_Validation_Functions

String string `validate:"is-awesome"`

This seems more like a utility validator, and I'm all for it. It allows custom validators without ever touching reflection yourself, and instead having the library do the heavy lifting. The developer using isvalid just returns an error if they notice the struct is bad, and thats it. Benefit would also be that this may work with other validators if they adapt it.

@nodivbyzero
Copy link
Contributor

The isvalid tag seems somewhat vague in its intent. Without a clear definition, it may be unclear how it interacts with other validation mechanisms, especially since different users might interpret it differently.
Would love to hear others’ thoughts as well!
cc: @chipaca, @deankarn, @alob-mtc @zemzale

@deankarn
Copy link
Contributor

The isvalid tag seems somewhat vague in its intent. Without a clear definition, it may be unclear how it interacts with other validation mechanisms, especially since different users might interpret it differently.
Would love to hear others’ thoughts as well!
cc: @chipaca, @deankarn, @alob-mtc @zemzale

I agree I don't love the tag name especially because I could foresee a future similar validation added where the function signature is IsValid() bool which this tag would be more appropriate for, love the rest of the PR and it's intent thought, I have no issues there.

WDYT about validateFn or validateIface something to denote it's doing a validation from external code?

@tizu69
Copy link

tizu69 commented Feb 15, 2025

validateFn sounds good to me

@peczenyj
Copy link
Contributor Author

Sorry guys I just lost this entire discussion,

I have one clear motivation: if I have a custom validator, lets say is-a-valid-xxx-enumeration, and I am using in a library, I have to expose this custom validator as a public function (for instance) AND use it each time I need to include the structure that have this is-a-valid-xxx-enumeration or it will fail and that can be... tedious. and if I forget in one place (imagine a lib that include another, etc) I may have a problem in runtime if I don't add sufficient tests. Of course, it is not impossible, but tedious.

also, if I have several enumerations, I end up with several custom validators. Or I can use a generic one. That is my motivation

last the oneof tag is useful for string types, not for enumerations based on integers/ uint8 (like the ones generated by some tool like enumer)

so I can rename it to validateFn (or validateMethod) is everyone agrees

zemzale
zemzale previously approved these changes Mar 25, 2025
Copy link
Member

@zemzale zemzale left a comment

Choose a reason for hiding this comment

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

Love the PR, the only thing I would add is a negative test that it doesn't panic when the type doesn't implement the validate function.

It's not required, but would help to ensure that interface conversions are not throwing panics.

fix doc

Co-authored-by: nodivbyzero <nodivbyzero@gmail.com>
nodivbyzero
nodivbyzero previously approved these changes Mar 31, 2025
Copy link
Contributor

@nodivbyzero nodivbyzero left a comment

Choose a reason for hiding this comment

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

Approved.

It would be beneficial to add a new example to showcase the functionality. While unit tests are valuable, having both a unit test and an example is even better ;)

@peczenyj peczenyj changed the title add support to tag isvalid add support to tag validateFn Apr 12, 2025
@peczenyj
Copy link
Contributor Author

peczenyj commented Apr 12, 2025

Approved.

It would be beneficial to add a new example to showcase the functionality. While unit tests are valuable, having both a unit test and an example is even better ;)

done.

BTW, since the Validate() error method is an arbitrary choice, I improve the code a little bit

now it is possible define any method, and if none is defined the Validate() error will be used. this will be helpful to use existing apis (such as the enumer method name convention IsFoo).

it also accepts methods that returns a boolean. on this case we expect a true value

in summary, by default it will try to call Validate() error or Validate() bool

like in the example below

//go:generate enumer -type=Enum
type Enum uint8

const (
	Zero Enum = iota
	One
	Two
	Three
)

func (e *Enum) Validate() error {
	if e == nil {
		return errors.New("can't be nil")
	}

	return nil
}

type Struct struct {
	Foo *Enum `validate:"validateFn"`         // uses Validate() error by default
	Bar Enum  `validate:"validateFn=IsAEnum"` // uses IsAEnum() bool provided by enumer
}

@peczenyj
Copy link
Contributor Author

coverage fixed 😓

Copy link
Contributor

@nodivbyzero nodivbyzero left a comment

Choose a reason for hiding this comment

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

This looks great! Thank you for making those changes.

@nodivbyzero nodivbyzero merged commit 8032f40 into go-playground:master Apr 16, 2025
12 checks passed
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.

None yet

6 participants