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

Validation of argument default values #2606

Closed
danielrearden opened this issue Jun 4, 2020 · 9 comments
Closed

Validation of argument default values #2606

danielrearden opened this issue Jun 4, 2020 · 9 comments
Labels

Comments

@danielrearden
Copy link
Contributor

Any default values provided when defining an argument are currently not validated against that argument's type. So a Float argument could be passed a string as a default value and this schema will be considered valid. Moreover, the default value will be passed as-is to the resolver instead of being coerced into an appropriate value.

As far as I can tell, the current behavior is inline with the spec. But it seems counterintuitive that doing something like this would be valid:

const assert = require('assert')
const { buildSchema, validateSchema } = require('graphql')

const schema = buildSchema(`
  type Query {
    foo(bar: Float = "ABC"): Float
  }
`)

const errors = validateSchema(schema)
assert(errors.length > 0)

We do validate default values on variables, so I think it's reasonable to expect at least consistency with that behavior. At the very least, I think validateSchema should be modified to validate the provided values against the argument type. I'm less certain about coercing the values, but off-hand it seems like desirable behavior as well.

@danielrearden danielrearden added the PR: breaking change 💥 implementation requires increase of "major" version number label Jun 4, 2020
@IvanGoncharov IvanGoncharov added bug and removed PR: breaking change 💥 implementation requires increase of "major" version number labels Jun 4, 2020
@IvanGoncharov IvanGoncharov added this to the 16.0.0-alpha.1 milestone Jun 4, 2020
@IvanGoncharov
Copy link
Member

@danielrearden Thanks for extracting this issue 👍
Meta: I added PR: labels to generate changelog and to track PRs I think it's just a bug and if something could be breaking for user than you just setup milestone to the next major release.

It's a general issue with SDL validation and should be done in validateSDL(called by buildSchema) and not in validateSchema.
validateSchema happening too late in the chain and should be reserved only for stuff that can't be validated earlier. It's important because validateSDL can be used by LSP since it supports incomplete schema and is pretty close to AST, but validateSchema expect full schema (all extension are merged, query root type defined, etc.).

It's even more serious in a sense we don't validate argument of directives it's serious problem related to how validateSDL works (it doesn't have ability to work with built types only AST node for them) since it's hard to validate default value defined in the same SDL as type it should be validated against:

input SomeInput {
  field: SomeOtherInputObject = { enum: INVALID }
}

input SomeOtherInputObject {
  enum: SomeEnum
}

enum SomeEnum {
  TEST
}

It's planned for 16.0.0-alpha that we hopefully start somewhere around next month.

@danielrearden
Copy link
Contributor Author

danielrearden commented Jun 4, 2020

@IvanGoncharov Thanks for the context and for the clarification around labeling.

I used SDL in the above example, but the same issue applies to schemas built programmatically:

const assert = require('assert')
const {
  validateSchema, 
  GraphQLFloat,
  GraphQLObjectType,
  GraphQLSchema
} = require('graphql')

const schema = new GraphQLSchema({
  query: new GraphQLObjectType({
    name: 'Query',
    fields: {
      foo: {
        args: {
          bar: {
            type: GraphQLFloat,
            defaultValue: 'ABC',
          }
        },
        type: GraphQLFloat,
      }
    }
  })
})

const errors = validateSchema(schema)
assert(errors.length > 0)

@IvanGoncharov
Copy link
Member

@danielrearden It's kind of hard to do validation of default values inside GraphQL*Types since they are in internal representation and scalars (custom and standard) doesn't have a way to check if internal representation is correct or not.
So it also a non-trivial issue that we need to try to solve in 16.0.0-alpha.

@danielrearden
Copy link
Contributor Author

👍 Just wanted to clarify the scope of the issue.

@danielrearden
Copy link
Contributor Author

Although... I do wonder if we couldn't just utilize coerceInputValue for this 🤔

@IvanGoncharov
Copy link
Member

@danielrearden coerceInputValue transforms from variables into "internal" representation so we can't use it for validation. Technically we can use scalar.serialize(value) since "serialization should validate "internal" representation but it's an awkward method. This issue is not critical and almost certainly requires some API changes so it's better to leave it until "alpha".

@IvanGoncharov
Copy link
Member

@danielrearden To be honest default values and custom scalars are two most problematic places at the moment and their combination is an absolute nightmare.
With so many issues including issues with GraphQL spec itself that it deserves its own dedicated refactoring.

@spawnia
Copy link
Member

spawnia commented Apr 15, 2021

@IvanGoncharov Can scalar.parseLiteral(value) be used to validate correctness, or am I missing something here?

It appears that valueFromAST() is already used in that way:

result = type.parseLiteral(valueNode, variables);
. To make it suitable for validation, we would have to let the errors bubble up so it is possible to differentiate between having no default value and a wrong default value.

@yaacovCR
Copy link
Contributor

Closing this issue, tracked in #3814

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants