-
-
Notifications
You must be signed in to change notification settings - Fork 96
Add error field & handler for mutations' payload
#248
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
Conversation
|
Follow up, after we get the above stored we can address the other operations |
|
I wanted to review your PR today but catch a bad error with embedded documents: I spent a half-day fixing that. |
|
Cool, no worries, thanks for the update. I had a look at the fix it looks good. OT: food for thought in future we could have a cache/reuse system to generate the smallest schema as possible, I think there may be opportunities to reuse types when used in with the default configuration, but leave that to way down the line. |
|
BTW. I merged master with your PR via force push. So this PR has the latest master changes. And I also migrated to Github Actions from Travis CI (Travis has not been working well lately). |
|
Yeah, that's the way it currently is. You have: recordId: MongoId
record: User
errors: [ErrorPayload]Look good? Cool, thanks, I'll pull the latest. |
|
I checked PR and have some thoughts. For such query: mutation {
createOne(...) {
recordId
record {
_id
}
errors: {
messages
}
}
} We will have the following response: With 2 nested arrays, It will be quite uncomfortable to work with it on the client-side. Moreover, frontend developers will be in confusion about how to deal with them. I suggest getting rid of So my proposal is:1) Create a base
|
|
Yeah, this makes sense. What your proposed solution for ...Many resolvers? As say validation errors will need to reference the document with issues. Should we overload the record in records? Or something like: |
|
Okay, this uses |
|
@RobertLowe good catch for ...Many resolvers. I think that if a client provides for us an array with input data then we should return some array with result data in the payload. I suppose that type xxxManyPayload {
records: xxxManyPayloadRecords
hasErrors: Boolean! # <--- need to find a good name for this field
}
type xxxManyPayloadRecords { # <---- try to keep the same payload shape of *One resolvers
recordId: ID
record: Entity
errors: ErrorInterface
}About |
38628dc to
735e184
Compare
|
@nodkz thanks, also just resolved tests. How about? Just type xxxManyPayload {
records: xxxManyPayloadRecords
errors: Boolean! # <--- need to find a good name for this field
}
type xxxManyPayloadRecords { # <---- try to keep the same payload shape of *One resolvers
recordId: ID
record: Entity
errors: ErrorInterface
}Yeah, this is what I was thinking too. |
|
Nope, just mutation {
userCreateMany(...) {
records {
errors # <--- real array with errors' messages
}
errors # <--- just boolean value
}
} |
|
Yeah, I'm cool with Maybe something like: type xxxManyPayload {
records: xxxManyPayloadRecords
hasRecordErrors: Boolean
error(s): [ErrorInterface] # <- runtime/ hard errors
}
type xxxManyPayloadRecords {
recordId: ID
record: Entity
errors: ErrorInterface # <-- nested validation, runtime, etc errors
} |
|
Follow up, what should be finished on this PR to close it? |
|
I've just improved What need to do
|
|
Also I found that |
|
Thanks for the improvements! |
As an example of
|
|
Thanks for the effort, sponsored the project, enjoy a few 🍻 or what you like! |
In future we may want to allow for control over Midnight for me as well, see you tomorrow morning, haha. |
Wow! Thanks a lot! 🙏 |
|
I changed the
According to current graphql schema design we return just one @RobertLowe what do you think? |
|
Thrown errors still happen at top level and are an array, ex: Where validation errors happen at the data level: Another idea would be to use My proposal is this: Makes the most sense as it follows what the singular version would do: The benefit is your errors are related to their origin, it does create one more level, but I think that's just the cost of it. Runtime, and MongoError's should be top-level. and not inside the This is the same as what Thanks again, and I'll defer to you as you know more than I. |
|
First of all, we should differentiate errors in
So any operation on data changing can be finished with to statuses
Eg. mongoose's and you may traverse If we think about Mutations in terms of
As i wrote above I consider mutations from When we consider just Moreover, if you watch Sasha's video they distinguish request parsing errors for top-level PS. What to do with batch resolvers I try to think about this evening. |
|
Okay, right, this all makes sense. We can still dig into Thanks for these explanations, I know I must be absorbing your valuable time but I'm learning a great amount and the talk was fun to watch. Yes, batch will be more tricky because they aren't done as a transaction but a All said, we should end up with results like?: "data": {
"usersCreateOne": {
record: ...,
recordId: ...,
"error": {
__typename: "ValidationError",
message: "Validation failed, some longer message",
errors: [{ "path": "email", "value": "There is already a user with this email." }]
}
}
}"data": {
"usersCreateMany": {
items: [
{record: ..., recordId: ..., "error": { ... }},
{record: ..., recordId: ..., "error": { ... }},
]
}
} |
|
Yep, for singular resolvers we can use almost the same shape as mongoose returns for us (without transformation): "data": {
"usersCreateOne": {
record: ...,
recordId: ...,
"error": {
__typename: "ValidationError",
message: "Validation failed, some longer message",
errors: [{ "path": "email", "value": "There is already a user with this email." }]
}
}
}For "batch" resolvers, I think, that we should keep the same shape for payload And also provide granularly error for every record in So for "batch" resolvers the graphql query can be the following form: mutation {
userCreateMany {
error {
message # String with a cumulative error message (which will be thrown to top-level if this field isn't requested)
}
records {
record
recordId
error { # error object for specific record like in singular mutation resolvers
message
... on ValidationError {
path
value
message
}
}
}
}
}What we can do more for "cumulative error"? |
|
For the cumulative error message I think something like KISS like |
…evel if client does not requested `errors` in payload
…or`; add `addErrorCatcherField` helper
A long time ago Jest awfully worked with such kind of Promise.reject errors check.
…request `error` field then typed error will be returned in mutation payload, otherwise it will be on top-level `errors` field.
Example query:
```graphql
mutation {
createOne(record: { name: "John", someStrangeField: "Test" }) {
record {
name
}
error {
message
... on ValidationError {
errors {
message
path
value
}
}
... on MongoError {
code
}
__typename
}
}
}
```
- Refactors some code - Changes *ValidationError to be of GraphQLError - Adds validationsForDocument for extracting validations
…or` fields because they don't know anything about it. So decision about errors (return error in mutation payload or return it in top-level) can be made only in resolver wrapper `addErrorCatcherField`.
…ally right before save method
…r for reducing copy-paste & keeping resolver logic slim
…tions. I'll just extend ValidationError by one more field in next commit. And code will be simplified quite well.
…rns empty array instead of null BREAKING CHANGE: some internal `filter` type names for args were changed. It may break some clients if you use that type names in your graphql queries: - `count` resolver change name for filter type `Filter*Input` -> `FilterCount*Input`
… be rewrapped in `addErrorCatcherField()` if client does not request `error` field in payload. We are using GraphQLError only for providing `extensions` property on top-level.
…ror is used also for *Many documents validation. All validator records are now combined into one array like was suggested in graphql-compose#248 (comment) One array better: `errors: [ { idx: 3, message: '', path: ''} ]` Than nested array with nulls: `errors: [ null, null, null, { message: '', errors: [{ message: '', path: ''}] } ]`
084dd1d to
47b0f7d
Compare
|
🎉 This PR is included in version 9.0.0-alpha.1 🎉 The release is available on: Your semantic-release bot 📦🚀 |
|
@RobertLowe please check I refactored code, most things were simplified. And during the refactor, I found that your suggestion in #248 (comment) (add
If you want you may follow my last 8 commits for more information. As a summary, I can provide the following test case which demonstrates how validation errors result returned by it('check validation for createMany', async () => {
const res = await graphql.graphql({
schema,
source: `
mutation {
createMany(records: [{ name: "Ok"}, { name: "John", someStrangeField: "Test" }]) {
records {
name
}
error {
__typename
message
... on ValidationError {
errors {
message
path
value
idx
}
}
}
}
}
`,
});
expect(res).toEqual({
data: {
createMany: {
records: null,
error: {
__typename: 'ValidationError',
message: 'Nothing has been saved. Some documents contain validation errors',
errors: [
{
message: 'this is a validate message',
path: 'someStrangeField',
value: 'Test',
idx: 1, // <---------------------------- JUST ADDED `idx` field.
},
],
},
},
},
});
});And such result when it('check validation for createMany with root-level errors & extension', async () => {
const res = await graphql.graphql({
schema,
source: `
mutation {
createMany(records: [{ name: "Ok"}, { name: "John", someStrangeField: "Test" }]) {
records {
name
}
}
}
`,
});
expect(res).toEqual({
data: { createMany: null },
errors: [
expect.objectContaining({
message: 'Nothing has been saved. Some documents contain validation errors',
extensions: {
name: 'ValidationError',
errors: [
{
idx: 1,
message: 'this is a validate message',
path: 'someStrangeField',
value: 'Test',
},
],
},
path: ['createMany'],
}),
],
});
});-- If you have any questions about current implementation – feel free to ask here. Thanks a lot for help with these amazing improvements 🙏 |
error field & handler for mutations' payload
|
🎉 This PR is included in version 9.0.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |



Hey,
Here's the work for errors payloads. I know we talked about moving
errorsinside the records payload, but that still feels awkward to me, I mean what if the model has anerrorsproperty, should we use_errors, additional where should we be injecting the errors payload type. InsidecreateInputType?Cheers,