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

validateSchema: validate Input Objects self-references #1359

Merged

Conversation

IvanGoncharov
Copy link
Member

Implements graphql/graphql-spec#445
Based on #1352

function validateInputObjectCircularReferences(
context: SchemaValidationContext,
): void {
// Modified copy of algorithm from 'src/validation/rules/NoFragmentCycles.js'.
Copy link
Member Author

Choose a reason for hiding this comment

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

Since this algorithm is not trivial would be great to extract duplicating code into jsutils

const fieldPathIndexByTypeName = Object.create(null);

const typeMap = context.schema.getTypeMap();
for (const type of objectValues(typeMap)) {
Copy link
Member Author

Choose a reason for hiding this comment

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

It's bad that we need to iterate over every type one more time.
Would be great to integrate this check into the loop inside validateTypes function without affecting its readability.

Copy link
Member Author

Choose a reason for hiding this comment

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

Solved by integrating this check into the main cycle inside validateTypes.

@IvanGoncharov IvanGoncharov added the spec RFC Implementation of a proposed change to the GraphQL specification label May 25, 2018
@@ -687,6 +687,80 @@ describe('Type System: Input Objects must have fields', () => {
]);
});

it('accepts an Input Object with breakable circular reference', () => {
Copy link
Member

Choose a reason for hiding this comment

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

I like how you considered all the possibilities of breaking a circular reference in the first test case, to ensure this does not cause false-positives.

const fieldNames = cyclePath.map(fieldObj => fieldObj.name);
context.reportError(
`Cannot reference Input Object "${fieldType.name}" within itself ` +
`through a series of non-null field: "${fieldNames.join('.')}".`,
Copy link
Member

Choose a reason for hiding this comment

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

field -> fields

Copy link
Member Author

Choose a reason for hiding this comment

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

@spawnia Fixed.
Should we have a separate error for the case where it's the only one field?

Copy link
Member

Choose a reason for hiding this comment

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

It would not hurt, but i think this way it is also fine. The error should still be quite obvious.

@@ -578,6 +581,64 @@ function validateInputFields(
});
}

function validateInputObjectCircularReferences(
Copy link
Member

Choose a reason for hiding this comment

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

Shouldn't this be in a seperate rules file, similar to how NoFragmentCycles is implemented?

Copy link
Member Author

Choose a reason for hiding this comment

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

@spawnia src/validate/rules/* is intended to validate AST only with a focus on the query syntax. You can create the schema using SDL or JS classes like here:

new GraphQLSchema({
  queryType: new GraphQLObjectType({
    name: 'Query',
    fields: {
      foo: {
        type: GraphQLString,
      },
    }
  }),
})

Also you construct schema from introspection using buildClientSchema.
So schema validation shouldn't be tied to AST and should only use GraphQL* classes.

On the other hand, your queries are always AST so they validation is also AST based.

That's why there is two completely different validation mechanisms: validate(AST based, mostly query) and validateSchema(GraphQL* classes, only for schema).

},
]);
});

Copy link
Member

@spawnia spawnia May 26, 2018

Choose a reason for hiding this comment

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

I suggest to add another test-case:

  it('rejects Input Objects with multiple non-breakable circular reference', () => {
    const schema = buildSchema(`
      type Query {
        field(arg: SomeInputObject): String
      }
 
      input SomeInputObject {
        startLoop: AnotherInputObject!
      }
 
      input AnotherInputObject {
        closeLoop: SomeInputObject!
        startSecondLoop: YetAnotherInputObject!
      }
 
      input YetAnotherInputObject {
        closeSecondLoop: AnotherInputObject!
        nonNullSelf: YetAnotherInputObject!
      }
    `);
 
    expect(validateSchema(schema)).to.deep.equal([
      {
        message:
          'Cannot reference Input Object "SomeInputObject" within itself through a series of non-null field: "startLoop.closeLoop".',
        locations: [
          { line: 7, column: 9 },
          { line: 11, column: 9 },
        ],
      },
    ],[
      {
        message:
          'Cannot reference Input Object "AnotherInputObject" within itself through a series of non-null field: "startSecondLoop.closeSecondLoop".',
        locations: [
          { line: 12, column: 9 },
          { line: 16, column: 9 },
        ],
      },
    ],[
      {
        message:
          'Cannot reference Input Object "YetAnotherInputObject" within itself through a series of non-null field: "nonNullSelf".',
        locations: [{ line: 17, column: 9 }],
      },
    ]);
  });
 

Copy link
Member Author

Choose a reason for hiding this comment

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

@spawnia Great test 👍 Added to PR.

@IvanGoncharov IvanGoncharov force-pushed the validateInputObjSelfRef branch 2 times, most recently from b90c798 to cf0c922 Compare May 26, 2018 23:25
Copy link
Member

@spawnia spawnia left a comment

Choose a reason for hiding this comment

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

The implementation is solid and the test cases both cover validation errors but ensure no false-positives are thrown. Looks good to me!

@IvanGoncharov IvanGoncharov changed the title [Draft]validateSchema: validate Input Objects self-references validateSchema: validate Input Objects self-references May 27, 2018
if (!visitedFrags[node.name.value]) {
detectCycleRecursive(node);
}
detectCycleRecursive(node);
Copy link
Contributor

Choose a reason for hiding this comment

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

Do you mind factoring the changes to this file to a separate PR?

Copy link
Member Author

Choose a reason for hiding this comment

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

@leebyron Done as #1381

@leebyron
Copy link
Contributor

This is looking great! Let's make sure the spec text is clear as well before merging this

@spawnia
Copy link
Member

spawnia commented Jun 11, 2018

@leebyron Anything you would change about the spec change? graphql/graphql-spec#445

@IvanGoncharov IvanGoncharov added the PR: feature 🚀 requires increase of "minor" version number label Jun 3, 2019
@IvanGoncharov IvanGoncharov merged commit ed74228 into graphql:master Jun 3, 2019
@IvanGoncharov
Copy link
Member Author

Merged since graphql/graphql-spec#445 is on Stage2 and spec is encourage implementing proposal on this stage:
https://github.com/graphql/graphql-spec/blob/master/CONTRIBUTING.md#stage-2-draft

GraphQL libraries should implement drafts to provide valuable feedback, though are encouraged not to enable the draft feature without explicit opt-in when possible.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed PR: feature 🚀 requires increase of "minor" version number spec RFC Implementation of a proposed change to the GraphQL specification
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants