Skip to content

validateSchema: validate Input Objects self-references#1359

Merged
IvanGoncharov merged 1 commit intographql:masterfrom
IvanGoncharov:validateInputObjSelfRef
Jun 3, 2019
Merged

validateSchema: validate Input Objects self-references#1359
IvanGoncharov merged 1 commit intographql:masterfrom
IvanGoncharov:validateInputObjSelfRef

Conversation

@IvanGoncharov
Copy link
Copy Markdown
Member

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

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

@IvanGoncharov IvanGoncharov May 25, 2018

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

Comment thread src/type/validate.js Outdated
const fieldPathIndexByTypeName = Object.create(null);

const typeMap = context.schema.getTypeMap();
for (const type of objectValues(typeMap)) {
Copy link
Copy Markdown
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
Copy Markdown
Member Author

@IvanGoncharov IvanGoncharov May 27, 2018

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
]);
});

it('accepts an Input Object with breakable circular reference', () => {
Copy link
Copy Markdown
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.

Comment thread src/type/validate.js Outdated
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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.

Comment thread src/type/validate.js Outdated
});
}

function validateInputObjectCircularReferences(
Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
Contributor

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

@spawnia
Copy link
Copy Markdown
Member

spawnia commented Jun 11, 2018

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

@IvanGoncharov IvanGoncharov force-pushed the validateInputObjSelfRef branch from 615c05a to 408bcda Compare June 3, 2019 08:57
@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
Copy Markdown
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.

4 participants