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 support for constraints #201

Merged
merged 55 commits into from Aug 28, 2019
Merged

Add support for constraints #201

merged 55 commits into from Aug 28, 2019

Conversation

ghostbuster91
Copy link
Contributor

@ghostbuster91 ghostbuster91 commented Aug 17, 2019

This refers to #48

}

trait Validator[T] { outer =>
def validate(t: T): Boolean

Choose a reason for hiding this comment

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

Would it be more useful for this to return a cats Validated or ValidatedNel? That way the Validate decoder can expose specific errors.

Copy link
Member

Choose a reason for hiding this comment

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

yes we should probably return some more information. Validated would be good though it would mean adding cats as a dependency. So I'm not sure about that yet :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Couldn't we just copy Validated from cats?

Copy link
Member

Choose a reason for hiding this comment

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

maybe let's start with a simple ValidationError(msg: String)?

override def validate(t: T): Boolean = constraints.forall(_.check(t))
}

trait Constraint[T] {

Choose a reason for hiding this comment

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

Will this eventually be sealed, or users be able to define their own custom constraints? If it's custom, I think there will need to be a 2nd method defining how to express the constraint in the OpenAPI model.

Copy link
Member

Choose a reason for hiding this comment

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

I think we should allow users to define their custom constraints. Initially we might only support a well-defined subset for openapi (that's a closed set anyway), and discard any custom constraints

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For the first version lets make it sealed and we will think about allowing users to define their custom constraints latter.

}

trait Constraint[T] {
def check(t: T): Boolean

Choose a reason for hiding this comment

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

Same question as above, should this return a Validated?

case TSchema.SInteger =>
Right(OSchema(SchemaType.Integer))
case (TSchema.SInteger, ValueValidator(constraints)) =>
Right(OSchema(SchemaType.Integer).copy(minimum = constraints.collectFirst { case Constraint.Minimum(v) => v }))

Choose a reason for hiding this comment

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

This pattern feels like it might be tough to scale as the number of constraints grows. Would it be better to add an OSchema => OSchema method onto Constraint so the custom logic for each constraint is located in a single place?


private[tapir] val validator: Validator[T] = Validator.passing

def decodeAndValidate(f: F): DecodeResult[T] = {
Copy link
Member

Choose a reason for hiding this comment

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

now we have three methods: decode, safeDecode and decodeAndValidate. This might be confusing. Maybe we could rename decode to rawDecode or sth like that, and keep only decode which would (1) validate (2) catch exceptions (3) call rawDecode? That would also collapse the Validate and Decode traits into one.

I don't think there's any use-case in calling these methods separately

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agree, but I at the same time I like the separation of decode and validate. Also decode/encode creates nice couple.
How about renaming decodeAndValidate to safeDecode, leaving Decode::safeDecode (will be called using super.safeDecode) and also leaving Codec::decode without change?

Btw: This is already implemented so you can see how it looks like

Copy link
Member

Choose a reason for hiding this comment

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

Separation as private methods: sure. But no need to introduce public traits/methods, which can only lead to confusion.

For me safeDecode doesn't say anything about validation, only exceptions. So I'd either go with decode - meaning the whole process (raw decode, exceptions, validations), or sth with "validation" in the name

def forArray: Validator[Array[T]] = (t: Array[T]) => {
t.forall(outer.validate)
}
def forSet: Validator[Set[T]] = (t: Set[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 have a single ElementValidator which takes an Iterable? The logic is all the same here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've just added support for collections. There were two problems:

  1. I had to create two implementations - one for array and the other one for iterable
  2. There is an ugly cast inside each one, types should be ok, but I couldn't omit it.

# Conflicts:
#	docs/openapi-docs/src/main/scala/tapir/docs/openapi/schema/ObjectSchemasForEndpoints.scala
#	docs/openapi-docs/src/test/scala/tapir/docs/openapi/VerifyYamlTest.scala
#	openapi/openapi-model/src/main/scala/tapir/openapi/OpenAPI.scala
@ghostbuster91 ghostbuster91 changed the title [WIP] Add support for constraints Add support for constraints Aug 27, 2019
@adamw adamw merged commit 1522df3 into master Aug 28, 2019
@adamw
Copy link
Member

adamw commented Aug 29, 2019

Released in 0.10.0

@ghostbuster91 ghostbuster91 deleted the constraints-14 branch August 29, 2019 15:42
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