Feature: introduce Validator function on fields
- Dominant language
- Go
- Stars
- 17.2k
- Forks
- 1k
- PR merge metrics
- No merged PRs in 30d
Description
Currently fields have the `Validate` method, which takese the format of `Validate(in ) error`.
It would be great to have a method where we can inject a new validator as an object, which can hold more information about the validation rule.
This will allow more details to be exposed about the validator which can be used for other things such as i18n and linking it to other objects. We are looking at both scenarios, where we want to display localized error messages as well as showing the rules of what a valid field looks like. We're also looking into how we can use this to auto generate validation constraints for our GraphQL API.
Some thoughts/examples on implementation:
```go
type stringValidator interface {
Validate(string) error
}
func (b *stringBuilder) Validator(val stringValidator) *stringBuilder {
b.Validators = append(b.Validators, val)
return b
}
```
We can then implement a new validator as such:
```go
type stringValidator interface {
Validate(string) error
}
func (b *stringBuilder) Validator(val stringValidator) *stringBuilder {
b.Validators = append(b.Validators, val)
return b
}
```
We can then update the [runtime validator generator](https://github.com/facebookincubator/ent/blob/master/entc/gen/template/runtime.tmpl#L165) to check for the interface and use the underlying method if it matches.
### Further suggestions
#### Function names
Since Go doesn't support pattern matching, it would be great if we could make the function handler descriptive such as `ValidateString(string) error` and `ValidateInt64(int64) error`. That way a single struct can implement multiple interfaces as a check.
#### Builder injection
Some validators may want to update some settings on the builder, such as a custom MaxLen validator. We could introduce an interface that allows us to do that and mark that as a custom validator:
```go
type Validator interface {
ConfigureDescriptor(d *Descriptor)
}
type stringValidator interface {
Validator
Validate(string) error
}
```
Another use case would be to do a similar thing like what the `EnumValidator` does, allow us to validate the given input against a desired set of fields.
#### Conditional validators
In some cases we want to do validation based on other field, eg. only mark a field required when another enum field has a certain value. This could be a differentiator between validators and validator functions, where we inject the object into the validate function:
```go
type stringValidator interface {
Validator
Validate(interface{}, string) error
}
```
This will allow the implementor to cast the interface to the desired type and do conditional validation based on the given input of other fields.
This will require some more work as currently the runtime validator configuration has the function signature where it only takes a single value.
Contributor guide
Assessment
This issue has not been assessed yet.