Skip to content

language support for NullValue #544

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

Merged
merged 21 commits into from
Nov 1, 2016
Merged

Conversation

langpavel
Copy link
Contributor

@langpavel langpavel commented Oct 27, 2016

GraphQL RFC: Null value

  • Support null literal in Parser
  • Support null literal in Printer
  • astFromValue
  • valueFromAST
  • getArgumentValues, getVariableValue in execution/values.js
  • isValidLiteralValue in utilities/isValidLiteralValue.js
  • introspection of __InputValue defaultValue field in type/introspection.js
  • utilities/schemaPrinter.js
  • some more tests

@langpavel
Copy link
Contributor Author

langpavel commented Oct 27, 2016

Help wanted! 😄

Copy link
Contributor

@leebyron leebyron left a comment

Choose a reason for hiding this comment

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

Thanks for starting this!

export type NullValue = {
kind: 'NullValue';
loc?: Location;
// value: null;
Copy link
Contributor

Choose a reason for hiding this comment

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

no need for this line - I think it's more confusing than helpful since the type doesn't actually have a value field, and if you attempted to read one, you'd see undefined instead of null

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was in doubt with this. Can I add value property? Because other literal AST nodes have value - so I keep it commented. So you suggest remove value or actually set it to null?

I think remove it will be better, but thank you for your careful review 👍

Copy link
Contributor

Choose a reason for hiding this comment

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

I agree, removing it is better. No sense in including the field which will always be the same value :)

null
);

expect(astFromValue(null, GraphQLBoolean)).to.deep.equal(
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you also add a test for astFromValue(null, new GraphQLNonNull(GraphQLBoolean)) to expect null

Copy link
Contributor Author

@langpavel langpavel Oct 28, 2016

Choose a reason for hiding this comment

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

@leebyron I'm little confused by this.
What is correct result for astFromValue(null, new GraphQLNonNull(*))?

In astFromValue implementation there is this comment at line 74:

Note: we're not checking that the result is non-null.
This function is not responsible for validating the input value.

So it is correct to ignore this comment here? But I think you are right.. so I should drop this comment..

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, it makes sense. I added condition and test in 02d71a0

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah - I like the change you made.

// only explicit null, not undefined, NaN
if (_value === null) {
return ({ kind: NULL }: NullValue);
} else if (isNullish(_value)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

no need for the else since you're returning in the if branch

@langpavel
Copy link
Contributor Author

@leebyron Can you review please?

I think I'm done with code, if you want some more tests I can add, but it seems I shift coverage a little up and I'm happy with mine 🔢 😄

Copy link
Contributor

@leebyron leebyron left a comment

Choose a reason for hiding this comment

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

This is looking great!

Also take a look at getArgumentValues and getVariableValue in execution/values.js - they have a similar kind of isNullish check to determine whether or not to apply a default value. Right now those are probably behaving correctly between null and undefined values. Adding tests for ensuring default values are used for variables definitions and argument definitions in those conditions would be great.

Also check out isValidLiteralValue in utilities/isValidLiteralValue.js - that also needs to handle the null literal. It's used during validation for DefaultValuesOfCorrectType and ArgumentsOfCorrectType - it would be great to add new test cases for those two (in validation/tests/) for nullable and non-null types being provided a null literal.

Also check out the introspection of __InputValue defaultValue field in type/introspection.js - right now it probably fails to represent a default value of null (which would appear as "null", representing null in a string of the graphql language)

Finally there's utilities/schemaPrinter.js which needs to support null literals as default values.

(I found the above by grepping for usage of isNullish - then checking to see if adding null literal support would break its behavior)

obj[fieldName] = fieldValue;
} else if (fieldValue !== null) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This could just be else since fieldValue !== null is guaranteed to be true here, otherwise the first branch would have been followed.

Also, the two conditions above are the same.

I think you can further shorten to a trinary.

// If no valid field value was provided, use the default value
obj[fieldName] = fieldValue === undefined || fieldValue !== fieldValue ? 
  field.defaultValue :
  fieldValue;

I think you can probably skip the isNullish in favor of inlining the check here. I think isNullish(x) && x !== null would be confusing to read.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ternary it is, thanks

@@ -111,6 +117,7 @@ export function valueFromAST(
);

const parsed = type.parseLiteral(valueAST);
// TODO: Should be this condition ommited?
Copy link
Contributor

Choose a reason for hiding this comment

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

This is a little bit tricky now, but I think the right decision is to leave this check alone.

Right now the definition for parseLiteral is to either return a value, or return null (or some nullish value) if the parse failed. I don't think it's ever appropriate for parseLiteral to return null and have that be intended as a null value to be used.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Comment removed

@@ -52,6 +54,7 @@ import { GraphQLID } from '../type/scalars';
*
* | JSON Value | GraphQL Value |
* | ------------- | -------------------- |
* | null | NullValue |
Copy link
Contributor

Choose a reason for hiding this comment

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

nitpick, could you put this entry at the bottom of the table, that way it's in the same order as the comments above valueFromAST.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Moved

null
);

expect(astFromValue(null, GraphQLBoolean)).to.deep.equal(
Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah - I like the change you made.

@langpavel
Copy link
Contributor Author

Ahh, sorry, I rebased! But I hope your input was not lost!
I have 5:37 A.M. here so I probably should go sleep today 😄 🌃

@leebyron
Copy link
Contributor

Definitely get some rest. Thanks for the progress!

@langpavel langpavel force-pushed the graphql-null-83 branch 4 times, most recently from ece2b53 to f747000 Compare October 28, 2016 12:17
@langpavel langpavel force-pushed the graphql-null-83 branch 2 times, most recently from 9812d88 to 8261a4d Compare October 28, 2016 13:05
@langpavel
Copy link
Contributor Author

Hi @leebyron. I'm kindly asking you for next review round, please :-)

I still should write some more tests mainly for validating null in NonNull but I'm not sure where is best place to settle this tests. Can you hint me?

Thanks!

@langpavel
Copy link
Contributor Author

langpavel commented Oct 28, 2016

I also discovered that it is not completed yet, because it is still possible to set default value to null for NonNull fields... This is definitely not right but I don't know how I can check this correctly..

@langpavel
Copy link
Contributor Author

langpavel commented Nov 1, 2016

@leebyron It's finally done I think 😋
(Except of changing behavior of astFromValue — which I think is bad idea)

@leebyron
Copy link
Contributor

leebyron commented Nov 1, 2016

Which astFromValue change are you referencing?

@langpavel
Copy link
Contributor Author

langpavel commented Nov 1, 2016

Which astFromValue change are you referencing?

astFromValue returns null. You proposed undefined but expected result is Value AST node...

Result for null value is ({ kind: NULL }: NullValue) and this function will never returns undefined

@leebyron
Copy link
Contributor

leebyron commented Nov 1, 2016

Yeah it's a minor detail since you're right that the function either returns an AST node or it does not do so. My concern is that returning null to represent no AST node has the potential to be misunderstood as a representation of null, vs implicitly returning undefined with return; also represents no AST node but is far less likely to be misunderstood.

@langpavel
Copy link
Contributor Author

Yeah it's a minor detail...

Exactly, but it is exported interface covered with tests so I'm for not changing it's behavior..

But if you wish I can change tests and return; from this function.. Then it should be mentioned in changelog too..

@langpavel
Copy link
Contributor Author

Ok,I will go sleep, deep night here :-) Please think about all this.. I know it is big thing so it probably needs more time for landing then I think just now.. But I'm very excited with this null support here..

I know, some people can hate me in future for introducing this null thing again to their lives, but I love possibility to say: Hey system, I don't know this value! :-)

And in this point I must say: Hey @leebyron, thank you for null RFC and I'm really happy I can work on this thing with you!

Good night

Comment clarity
Valid return values
@leebyron leebyron merged commit c23f578 into graphql:master Nov 1, 2016
@leebyron
Copy link
Contributor

leebyron commented Nov 1, 2016

Thanks for all your hard work - let's land this and see how it goes

@nodkz
Copy link
Contributor

nodkz commented Nov 1, 2016

Can not wait for 0.8.0 anymore ;)
For me, a custom package build with NULL works perfectly.
Thanks @langpavel and @leebyron!

If somebody wants to test NULL in args and flowtype in their app, I built graphql-js package on commit 2b717f1. Just run:

npm install https://www.dropbox.com/s/zrd3b00dmx3jhae/graphql-0.7.2.tgz\?dl\=1

PS. Yarn does not work with this link (((

@langpavel
Copy link
Contributor Author

Awesome! Thanks!

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

Successfully merging this pull request may close these issues.

6 participants