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

Recursive input objects #158

Closed
chrispage1 opened this issue Dec 10, 2018 · 6 comments
Closed

Recursive input objects #158

chrispage1 opened this issue Dec 10, 2018 · 6 comments

Comments

@chrispage1
Copy link
Contributor

Hi,

I've got a number of types that have a lot of relationships as the system is fairly large. For each type, I've defined an input object type. Here's just a small selection of my input types -

  • client
    -- users

  • booking
    -- client
    -- customers
    -- company

  • user
    -- client
    -- clients

Now the problem that I'm running into is that when I try and submit a booking, it seems that because a booking links to a client, client links to user and user links back to client, this seems to throw a 500 internal server error. However it doesn't output anything, just simply fails. I don't even have to reference the client field in my query for this to fail!

Now if I remove the client & clients fields from the user input object, everything works as I'd expect. The problem is, if I do this I can no longer submit a user with linked client / clients entries...

Does anyone know why this might be and if there is a way around it?

Chris.

@rebing
Copy link
Owner

rebing commented Dec 12, 2018

I think you should see a more detailed error stack in your server logs. It would help if you could show that (usually located in <app_root>/storage/logs)

@jpnut
Copy link
Contributor

jpnut commented Jan 21, 2019

I'm not completely convinced we're running into the same issue, however they sound very similar and are caused by the same intent (using recursive input objects) - so I'll post my findings here.

Problem

When validating arguments, we loop through all of the defined fields recursively to define validation rules. So, for example, if we have an input object defined

foo: {
    bar: {
        baz: String
    }
}

then this will go through the entire tree before attempting to get the rules for baz. (Source)

The problem here is that this recursion happens regardless of the presence of these arguments. I.e. even if foo was left null, this recursion would still take place. This makes sense from a certain point of view. For example, consider a situation where baz is a required field (i.e. the rules array looks like ['required']). In this case, we must go through the tree to get this validation rule, otherwise this rule could be excluded.

The problem here is that running this recursion on a recursively defined input object leads to an infinite recursion (note that this is presumably why OP found that there was a 500 server error rather than anything more descriptive. The error produced will be due to the maximum function nesting level being reached, hence it is not caught by Laravel). For example:

Foo {
    bar: String
    foo: Foo
}

Solution

The resolution to this is to skip validation of nullable fields which are not present in the provided arguments. For example, given the above defined Foo, and some input arguments as follows

{
    foo: {
        foo: {
            bar: "baz"
        }
    }
}

we should only attempt to infer validation rules for "foo.foo.bar" (not "foo.foo.foo..."). What about fields with the required rule? If a field has this validation rule, then its parent must be non-nullable, in which case this rule will always be added. Having a required field as a child of a nullable object doesn't really make sense.

In terms of code, this simply means we need to exclude nullable fields which are null from any kind of validation. For example, just before line 76 of Field.php

if (isset($arg['type']) 
    && !($arg['type'] instanceof NonNull) 
    && !isset($arguments[$name])) {
    continue;
}

Note that this would be a breaking change. Currently, if a nested field is marked as required, then the validation rule will always be added. This change would mean such a rule could be skipped if the parent object were non-nullable.

If you're OK with this change I'd be happy to create a PR. It's not particularly urgent since it's pretty trivial to wrap and override the getRules function of Field.php.

Thanks for your continued work on this package, it is greatly appreciated!

@rebing
Copy link
Owner

rebing commented Jan 22, 2019

You can create a PR, it looks like a logical way to fix the infinite recursion. Thanks for your support!

@rebing
Copy link
Owner

rebing commented Jan 29, 2019

4175bb8

@rebing rebing closed this as completed Jan 29, 2019
@Artem-Schander
Copy link
Contributor

Hello @rebing
I'm experiencing also the infinite validation loop.
In my case the relation structure differs a bit. Instead of a simple recursion i have different input objects which are pointing either directly to eachother or over a few corners.

A simple example would be:
Author hasMany Book
Author hasOne BestSellingBook (Book)
Book belongsTo Author

@mfn
Copy link
Collaborator

mfn commented Oct 31, 2019

I suggest to create a new issue with detailed steps how to reproduce this; a example repository would be even better.

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

No branches or pull requests

5 participants