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

Validate custom time.Duration types #4

Closed
wants to merge 4 commits into from

Conversation

pgporada
Copy link
Member

@pgporada pgporada commented Jun 13, 2024

We can now validate custom time.Duration types defined as type MyDuration time.Duration with all of the baked in tags such as gt, gte, eq, lt, lte, etc.

In an application, the caller will need to define their custom duration, instantiate a new validator, register the custom duration type, then configure the validation tags where applicable. What that looks like in practice is

package example

// Sometimes we need a custom type because we have application specific methods
type MyDuration time.Duration

func (m *MyDuration) UnmarshalJson() ...
func (m *MyDuration) MarshalJson() ...
package config

type MyConfig struct {
  TLS TLSConfig
  ConnectMaxIdleTime example.MyDuration `validate:"omitempty,lte=60s"`
  ConnectionLifetime example.MyDuration `validate:"required,gte=5s"`
}

func ValidateConfig(mc MyConfig, in io,Reader) {
	// Initialize the validator and load any custom tags.
	validate := validator.New()
	for tag, v := range cv.Validators {
		err := validate.RegisterValidation(tag, v)
		if err != nil {
			return err
		}
	}

	// Register example.MyDuration so the validator understands it and map it to an existing type.
	validate.RegisterCustomType(time.Duration(0), example.MyDuration(0))

       err := validate.Struct(mc)
}

@pgporada
Copy link
Member Author

Thank you @aaomidi for helping me figure this out.

util.go Outdated Show resolved Hide resolved
util.go Outdated Show resolved Hide resolved
@aarongable aarongable requested review from a team and beautifulentropy and removed request for a team June 13, 2024 22:18
validator_instance.go Outdated Show resolved Hide resolved
validator_instance.go Outdated Show resolved Hide resolved
…r struct, and use validation tags on a per-Validator basis
field := fl.Field()
param := fl.Param()
v := reflect.ValueOf(struct{}{})
val := reflect.ValueOf(struct{}{})
Copy link
Member

Choose a reason for hiding this comment

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

Is this rename necessary?

Suggested change
val := reflect.ValueOf(struct{}{})
v := reflect.ValueOf(struct{}{})

Copy link
Member Author

@pgporada pgporada Jun 24, 2024

Choose a reason for hiding this comment

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

Yes because of the new input parameter v *Validate. That parameter is needed to meet the function signature defined here. Without the rename I get an a "no new variables on left side" error.

@@ -315,56 +315,56 @@ func isUnique(fl FieldLevel) bool {
}

// isMAC is the validation function for validating if the field's value is a valid MAC address.
func isMAC(fl FieldLevel) bool {
func isMAC(v *Validate, fl FieldLevel) bool {
Copy link
Member

Choose a reason for hiding this comment

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

Here and below, v *Validate param is being added but never used. I'm not understanding why this is the case.

Copy link
Member Author

Choose a reason for hiding this comment

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

All bakedInValidators have to match the Func function signature defined here. Should this parameter be func isMAC(_ *Validate, fl FieldLevel) bool instead since I don't use v?

Copy link

@aarongable aarongable Jun 24, 2024

Choose a reason for hiding this comment

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

Having not taken the time to fully understand the whole validator package myself, it appears that there's something very weird going on where some things happen at the *Validate instance level, and others happen at the global level. This existing inconsistency is leading this PR to become very large while attempting to accomplish a seemingly-straightforward goal.

I don't think that this PR is the right place to do a massive refactoring of this library to allow everything to happen at the instance level. If we want to do that (and maybe we do!) it should be as a separate PR, where we can consider multiple different ways of making that happen (e.g. making lots of current top-level helper functions instead be methods on that type, which seems cleaner than having those methods take an instance as their first argument).

@pgporada
Copy link
Member Author

Closing in favor of letsencrypt/boulder#7564

@pgporada pgporada closed this Jun 27, 2024
@pgporada pgporada deleted the config-duration-validation branch June 27, 2024 13:57
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.

4 participants