-
-
Notifications
You must be signed in to change notification settings - Fork 511
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
WIP: field data processors and validators #809
Conversation
Codecov Report
@@ Coverage Diff @@
## main #809 +/- ##
==========================================
- Coverage 96.83% 96.80% -0.04%
==========================================
Files 70 71 +1
Lines 2370 2409 +39
Branches 333 339 +6
==========================================
+ Hits 2295 2332 +37
- Misses 48 49 +1
- Partials 27 28 +1 |
4cc8157
to
c8359ee
Compare
tests/types/test_validators.py
Outdated
def upper_validator(value, info): | ||
return value.upper() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't seen the implementation yet, but I'm a bit confused on the terms, we are passing validators, but we are not doing any validation here 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll add some data validation.
As discussed in #788, this same method can be used for data pre/post processing and validation. Could we have more generic name for this? Or do we want to separate these so that there would be separate validator and processor methods? Any opinions?
Hello team, Are you happy with chosen approach? I'm happy to contribute and to start working on final implementation but before that I'd like to hear your feedback. Is this the way to go? |
I have a few comments, is this going to (also) replace our permission_classes? What about decorators, we wanted to support them too, see #473? Also, do you think we can make it easier to support this pattern for handling errors with this approach: https://sachee.medium.com/200-ok-error-handling-in-graphql-7ec869aec9bc ? |
Validators can be used for permission validation as well. This can replace permission_classes if we decide so. Decorators are nice but they can be used only with those fields which have resolver function. Validators can be used with both, fields with and without resolvers. I'd support both, decorators and validators. Decorators are more flexible, but as said they cannot be always used. @jkimbo, any comments?
This is very good question. I haven't thought about that earlier. :) I think we could interpret exception as errors. Alternative results could be provided by validators. For example: @strawberry.type
class User:
groups: List[Group] = strawberry.field()
@groups.validator
def groups(value, info):
if not info.context.user.is_active:
# inactive user, return error
raise Exception('Invalid User')
if not info.context.user.has_permission():
# no permissions, return alternative response
return []
# validation passed, return original value
return value This works already! |
I've been thinking about the proposal a lot and I think there is some really interesting ideas here but I'm not sure if it's the right approach. I also feel like the feature has expanded in scope quite a bit and I'd like to focus just on the validation part first. Some thoughts (numbered for easy replying):
@strawberry.input
class UserInput:
email: str = strawberry.field(validators=[valid_email])
@strawberry.type
class InvalidEmail:
message: str
@strawberry.type
class Success:
user: User
Response = strawberry.union("Response", [InvalidEmail, Success])
@strawberry.mutation
def create_user(user_input: UserInput) -> Response:
# validation has already happened by this point If the input contains an invalid email the error will be raised before the mutation function body gets called and so there is no opportunity to return the AsideA way to support this would be to add an explicit @strawberry.mutation
def create_user(user_input: UserInput) -> Response:
try:
user_input.validate()
except ValidationError as error:
return InvalidEmail(message="Invalid email address")
AsideIt would actually be possible to support this form of validation in user land using the @strawberry.input
class UserInput:
email: str
def __post_init__(self):
if not valid_email(self.email):
raise ValidationError("Invalid email") I think there is still value in adding first class support for validators though FYI. Sorry for such a long response @la4de but this proposal has been very thought provoking for me! Looking forward to hearing what you think. Also happy to jump on a call in Discord if that helps. |
Oh, by the way, if we find a way to manage this and validation we could use the same approach for Pydantic (https://strawberry.rocks/docs/integrations/pydantic#input-types). At the moment we do exactly what @jkimbo proposed: @strawberry.mutation
def create_user(user_input: UserInput) -> Response:
try:
user_input.to_pydantic()
except ValidationError as error:
return InvalidEmail(message="Invalid email address") which isn't that bad to be honest, but I wonder if we could simplify things a bit (if that's actually useful) |
I'm still preparing better sample code but meanwhile I want give another example: https://pydantic-docs.helpmanual.io/usage/validators/ Pydantic validators return value, which can be used for data cleaning and normalization. |
@la4de that is a fair point. I still find it a bit odd because the naming doesn't really match the functionality. I guess this is why Django forms have the |
This is also a similar approach here: https://docs.nestjs.com/graphql/field-middleware |
Closing as we have extensions now 😊 |
The purpose of this pull request is to demonstrate the functionality of validators. This new feature has been proposed and discussed in #788.
Description
New
validators
list is added toStrawberryField
structure which contains list of callable validators. User defined validator function takes value as an parameter and returns validated value. Validator function is called after resolver functions with output types and before resolver with input types. See discussion in #788.Both output and input types are supported.
Types of Changes
Issues Fixed or Closed by This PR
Checklist