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

Add a validator for integration with custom complex validations #659

Merged
merged 5 commits into from Sep 17, 2020

Conversation

kciesielski
Copy link
Member

No description provided.

@@ -139,6 +140,14 @@ object Validator extends ValidatorMagnoliaDerivation with ValidatorEnumMacro {
}
}

case class CustomCaseClass[T](doValidate: T => List[InvalidField[_]]) extends Single[T] {
Copy link
Contributor

Choose a reason for hiding this comment

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

We already have Product validator which can validate arbitrary case classes.

The problem we are trying to solve is that with customValidator currently we cannot return more than one ValidationError.

To do this we should move message out of constructor.

But that isn't so simple because of how the end message for user is built. In ServerDefaults we do pattern matching on validator's type to provide user-friendly message:


  /**
    * Default messages when the decode failure is due to a validation error.
    */
  object ValidationMessages {

    /**
      * Default message describing why a value is invalid.
      * @param valueName Name of the validated value to be used in error messages
      */
    def invalidValueMessage[T](ve: ValidationError[T], valueName: String): String =
      ve.validator match {
         ....

I think that this could be addressed by splitting ValidationError into ValidationErrorCustom and ValidationErrorPrimitive, so that ValidationErrorCustom wouldn't need to have validator: Validator.Primitive field. For ValidationErrorPrimitive we could reuse the current logic, and for ValidationErrorCustom we would just concat the error messages.

WDYT?

Copy link
Member

Choose a reason for hiding this comment

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

But Validator.Custom extends Validator.Primitive, so that shouldn't be a problem?

Copy link
Contributor

Choose a reason for hiding this comment

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

It is because we want to have a list of error messages inside ValidationError rather than reference to Validator.Custom

Copy link
Member

Choose a reason for hiding this comment

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

ahh yes I get it. Hm, maybe ValidationError should contain an optional message, and if it's not specified, the default one is used?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, it is an option, but I would rather prefer to create another type which more clearly express developers' intentions.

Copy link
Member

Choose a reason for hiding this comment

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

ok, so I don't think we want to have a list of error messages inside ValidationError, but rather a list of validation errors, each with its own message. Validating a single value can lead to multiple errors on multiple (nested) values - as in any complex case class.

So the basic method of a validator needs to remain T => List[ValidationError]. Now as you write, we can have two subclasses of that, one binding the error with a specific validator (as currently - DefaultValidationError?), and the other one with a custom message, or maybe rather: custom constraint description (that's being validated), e.g. CustomValidationError. That should cover the use-cases I guess.

@@ -139,6 +140,14 @@ object Validator extends ValidatorMagnoliaDerivation with ValidatorEnumMacro {
}
}

case class CustomCaseClass[T](doValidate: T => List[InvalidField[_]]) extends Single[T] {
Copy link
Member

Choose a reason for hiding this comment

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

maybe we can simply change the Custom validator so that it accepts a doValidate: T= > List[InvalidValue] function?

Copy link
Contributor

Choose a reason for hiding this comment

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

Wouldn't that require executing doValidate function twice - one for validation and second time for calculating error message?

Copy link
Member

Choose a reason for hiding this comment

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

yeah, we need to design it so that we avoid calling it twice :)

@ghostbuster91 ghostbuster91 marked this pull request as ready for review August 19, 2020 13:26
@@ -236,7 +232,7 @@ object Validator extends ValidatorMagnoliaDerivation with ValidatorEnumMacro {
case MaxLength(value) => Some(s"length<=$value")
case MinSize(value) => Some(s"size>=$value")
case MaxSize(value) => Some(s"size<=$value")
case Custom(_, message) => Some(message)
case Custom(_) => Some("custom message") //TODO?
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe we could add additional optional property - description which will be used for that purpose?

@adamw adamw merged commit e34760e into master Sep 17, 2020
@mergify mergify bot deleted the feature/custom-validator branch September 17, 2020 08:52
@adamw
Copy link
Member

adamw commented Sep 17, 2020

Released in 0.17.0-M1, thanks!

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

3 participants