-
-
Notifications
You must be signed in to change notification settings - Fork 58
feat: Add validation queries (WIP) #294
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
base: main
Are you sure you want to change the base?
Conversation
Now that I think about it, ignore the part about adding the |
👋 just want to let you know this is still on my list. I've been mega busy the past week or so. I hope to get to it this week but it may be next week before I have the chance. |
Don't worry, take your time! |
So your idea of making |
The issue that I see with that is regarding non-nilable fields, for example, with the current code, the generated validate apis will change all non-nilable fields to allow nil to bypass the Absinthe check and reach the If we only have the option, then I think we would need a way to bypass that check in Absinthe, otherwise the api will just return one error (for the non-nilable field not being passed) instead of returning all current errors which is what we should expect from a validation. I did take a look into that a week ago, there is a phase that will do these checks in Absinthe side (these two: https://github.com/absinthe-graphql/absinthe/blob/main/lib/absinthe/phase/document/validation/provided_non_null_arguments.ex and https://github.com/absinthe-graphql/absinthe/blob/main/lib/absinthe/phase/document/validation/provided_non_null_variables.ex), and that comes as default when we generate the Absinthe document object (https://github.com/absinthe-graphql/absinthe/blob/f88c226104d0e4def6d50eaa527ad70508c9a1cd/lib/absinthe/pipeline.ex#L52). The issue I came by is that at the time we generate the pipeline (I tried to find here where that happens, buy I couldn't find it), if I remember correctly, we don't yet know if we should have these validations in place. The correct approach would be to create the pipeline without these validations, and somehow add them dynamically depending on the If I would guess, I think we would need to actually bake our own version of these validations (that would just call the original behind the scenes) so we have access to the inputs and conditionally do the validation or not. If you are ok with this idea, then I can try create these validations here and see if I can make it work. The only think I would need help is to understand where exactly in the code the pipeline is being created so I can remove the original validations from the pipeline and insert our custom ones. |
I do see this line ( ash_graphql/lib/ash_graphql.ex Line 187 in 5ce7f6f
Pipeline.replace(pipeline, ProvidedNonNullVariables, MyOwnMaybeProvidedNonNullVariables)
|
Ah, right. Well... is it really a requirement that we allow making a validation request for a request that is literally impossible? The error messages produced would be impossible for the client to actually receive in practice. You'd be dealing with an impossible error state. That doesn't really seem like a good idea to me. |
I don't think I understood what you mean in your last message. But here is breakdown of what I mean by changing the validation phase: Right now, with the current code, if you don't fill the arguments that are required, you will get errors like this: {
"errors": [
{
"message": "In argument \"refCode\": Expected type \"String!\", found null.",
"locations": [
{
"line": 2,
"column": 3
}
]
},
{
"message": "In argument \"id\": Expected type \"ID!\", found null.",
"locations": [
{
"line": 2,
"column": 3
}
]
}
]
} These errors are generated by that validation phase I mentioned before. The issue with leaving the behavior for the validation option is that this phase will prevent the pipeline to reach the part that actually validates the For example, let's say that another issue from my mutation is that I also have an e-mail argument that has an invalid e-mail as value. Since the pipeline stopped at the required arguments validation, it will never generate the error for the invalid e-mail field, even if the user already filled that field in the form. This means that the output the api will give depends if all required fields are filled or not, which is not consistent with the way the validation works using a phoenix form for example. What I'm proposing is to replace these phase validations with one that checks for the In the above code, the changeset would generate 3 errors, one for each field that is required but it is not provided ( |
Oh, after re-reading you message I think I got what you meant by impossible state, but I don't agree with it. So, let's think first what is the purpose of this PR: The idea is to give the frontend a way to validate a form (that uses the graphql api behind the scenes) using the backend instead of duplicating code in the frontend. By impossible state, I think you meant that the frontend would already validate the graphql api schema anyway and show that the required input fields are required without having the call the backend to do that (basically the input will check the api schema, see that its field is non-nilable and show an error if it is not filled yet). The problem with this is that now the frontend user needs to have two implementations, one where he checks the graphql schema to make sure that the input is required or not and another one when he call the validation action and gets other errors from the backend. I don't think is a good solution, a better solution IMO is that all the validations should be done by the backend, making the frontend code simpler and without code duplication. That means that the frontend will not check if the field is mandatory or not, it will just call the api either way and just check the errors that it will return. By doing that, and bypassing the phase validations I mentioned before, the frontend will get the full changeset errors and then they can inform the user of which validations have problems (the exact same way we do now with LiveView forms). |
Hm...yeah I guess that's true. And since it's opt-in, the user can test and ensure that their action behaves fine with no required inputs present. It's important to keep in mind that action changes still run even on invalid inputs, so the validate endpoint may require users to test it out and ensure their validations/changes don't have issues etc. |
I don't think we should bypass the phase validations though if we're doing that, it should be a separate mutation at that point |
In that case we would keep the current approach from the PR? In the current code I keep these validation phases, but I change the required fields to not be required anymore in the graphql schema. In the end of the day they do the same thing. So I guess I could change to the above approach in the validation mutation just so the schema is the same? Also, just one thing to be clear, the above suggestion would not disable these phases if the user are not doing a validation. Basically they would run if |
It's hard to decide 😂 so the issue I have with disabling the pipeline validations is that many implementations of GraphQL clients are typed and so will produce type system level errors trying to call the mutation with null inputs. if we say "that doesn't matter they can fix it on their end", then our lives get a lot simpler and we can go with the approach of reusing the mutation. but for some users I suspect that will be a deal breaker. Perhaps we can start with that approach and make it extensible later, like what do you think? |
Ah, I get what you mean, I'm always on the side of making the graphql apis, not consuming them, so I'm not super familiar with the existing libraries for it. So, basically, there will be libraries that will block or warn the user if they are ignoring required arguments. In that case, I think the best approach is just continue with our current one, create a validation version of the same API and make all required fields in it not required. That way it will work for everyone, and it will not affect the existing apis in any way. What do you think? |
Yep, so we should just continue on with this PR. A few notes from my initial review:
|
Just to double check if I got you comments right:
Also, should we also support generic actions (right now it just supports create and update actions)? You also mentioned queries, but I'm not sure show exactly we would validate a query |
What I mean is that we should do something like create :validate_create, :create, validate_only?: true So that it goes down the complete same codepath right up until the end. |
You can validate |
This is a first version of having validation queries for mutations. The idea is to have an API that will work similarly to what
AshPhoenix.Form.validate
does, in other words, instead of having to duplicate validation logic in the frontend, the frontend can call the validation query and get the data directly from the backend.Right now there are some issues with this PR:
One possible argument against this is that this would make using the validation api harder for the frontend developer since they would need to have a specific query for a the validation query and one for the mutation, while if both are mutations, then the only change is the function name.
Also, another thing to think about is that maybe we could instead of generating a new graphql api only to validate, we could add a
validate
input to the mutation itself and then the frontend keeps using the same API for both cases, just changing thevalidate
boolean to define when validate and when submit (honestly, now that I think about it, I think this would be probably the best solution both in our side and also for implementing in the frontend).I implemented it for create and update actions, I didn't do it for generic actions, maybe we should have that too?
I'm not sure if I should add support for all the other options that
create
andupdate
actions have in their schema to the validation actions.I didn't add any unit tests for now since this has a great chance to change.
Here is a resource that I used to test it out:
Contributor checklist