-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
feat(graphql): adds @default directive for setting default field values at create and update #8017
Conversation
…ated mutation timestamps Adds support for @created and @Updated directives on GraphQL input schema. Schema gen validates that field is of type `DateTime!` Schema gen does not add @created and @Updated fields to add/update input types, preventing modification over GraphQL Rewrite mutation sets @created fields with current DateTime when node is new, and @Updated fields any time node is changed. Non null input checks ignore fields with @created/@Updated metadata
Could you add arguments to both directives?
default would be |
If you need to manually update your server generated timestamps, you might be doing it wrong :) Are you on the Dgraph team? It's not something I need but happy to add if needed for merging. Thinking about it, the behaviour would be kind of confusing... the fields would be available on mutation inputs (update / upsert) but would get set anyway if you didn't provide a value. Might make more sense on Also happy to take input on the name of the directives, was trying not to overthink it but maybe createdAt updatedAt are better? |
Hi @dpeek @maaft, I am from the Dgraph team. Really happy that you started to work on this. But I have few suggestions for you. The way i see it @created and @Updated kind of creates a confusion and has very limited scope of improvement and extension. For example, what if i just want to have a default value say 1 for Int data type etc. My suggestion is to have something like
Now we can extend the list of keywords in future easily and for now |
Hi @aman-bansal, thanks for your response and the (much better) suggested approach! I kind of assumed you guys would have an approach in mind, happy to update the implementation to match that. What do you think about the ability to remove fields from mutation inputs, perhaps not just for timestamps but other fields with default or computed values. Do you have a directive schema in mind for something like that? Also regarding my question about how best to provide the timestamp, does I'll get started on changing the directive schema and update here when done. This is some of the first Go I'm writing, so if the scope grows beyond small adjustments I might need some guidance :) |
For the schema I was thinking to always have non nullable option in schema itself. So for example for a type Defined like this
both are correct mutations
and
i would suggest to integrate basic feature first. Having just Really sorry but I dont understand this question The way i see it, you wanted to know at which point you should assign timestamps or evaluate default values, I would suggest not to take timestamps or other default values across the code with context. You will get stuck across multiple validations which dgraph might have. https://github.com/dgraph-io/dgraph/blob/master/graphql/resolve/mutation.go#L105 Here we rewrite the GraphQL queries into DQL queries. This seems like the best place to me to evaluate and apply all the default values. |
@dpeek My reasoning for allowing mutating these fields optionally is the following: Of course you could directly use DQL, but honestly, nobody wants to learn it who's coming from the GraphQL world. DQL is very complex at times and the GraphQL <-> DQL translation already exist. By the way, my use-case is synchronizing an offline database (same schema) with the always-online main dgraph instance. There I need control over the timestamps, otherwise the UI would show different information when connected to the offline database or the online database. @aman-bansal I very much like the @default approach you suggested. This could also finally fix issues when doing schema migrations which introduce new predicates. Currently, I have to manually update all database entries which is non-ideal. |
@aman-bansal I've just pushed the main changes to get I see your point about my timestamp question: I was actually asking whether the timestamps for multiple node updates in a single mutation should be exactly the same, but that's kind of silly given we're talking about milliseconds. So now we just create the timestamp when we request the default value in My remaining question about timestamps is how I should go about mocking the time for the tests in @maaft |
…nd return hard coded date only when in test.
Ok, it's looking better now. Added some more tests and simplified getting the default value for a field. Main other things I might test are deep add mutations and making sure fields with A few notes / questions: In testing different scalars in the yaml tests, when an
A quick look around suggests that no other tests are testing Ints either :P Defaults are currently provided only as For now I'm handling unit tests like this when getting the default value:
Let me know if there is some better way :) |
@dpeek That's awesome! Thank you so much for your work! :) |
@dpeek is it ready for review? |
Sure, there a a couple of questions above in my comments but probably worth you taking a look now to see if it in the right direction :) |
@aman-bansal any chance you will get to this this week? My availability to address feedback will be limited next week. |
@dpeek Thank you so much for working so hard on this feature. Me and my team will review the PR this week. I have already shared the PR in internal channels for a review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reviewed 61 of 68 files at r2, 6 of 7 files at r3, 1 of 1 files at r4, all commit messages.
Reviewable status: all files reviewed, 2 unresolved discussions (waiting on @dpeek)
graphql/schema/rules.go, line 1300 at r4 (raw file):
secrets map[string]x.Sensitive) gqlerror.List { if typ.Directives.ForName(remoteDirective) != nil { return []*gqlerror.Error{gqlerror.ErrorPosf(
lets add one more validation where $now can be part of fields whose data type are DateTime! Also values should be of same data type as their fields definitions. Say "user-random" cannot be default value for a field with Int Data type
graphql/schema/wrappers.go, line 2372 at r4 (raw file):
} if value.Raw == "$now" { if flag.Lookup("test.v") == nil {
Let's not put testing logic in the code. we can put non null checks and time date parsing logic to verify that things are working fine. I guess that would be enough validation to work with.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The change looks really nice to me. Thanks, David for the work. 🎉
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reviewable status: 65 of 68 files reviewed, 4 unresolved discussions (waiting on @aman-bansal, @Custom, @default, @deprecated, @dgraph, @id, @include, @lambda, @NamanJain8, @secret, and @Skip)
graphql/schema/rules.go, line 1294 at r4 (raw file):
Previously, NamanJain8 (Naman Jain) wrote…
I think there are several other checks that need to be incorporated (thought of) here.
Like if it should work with secret, dgraph, custom, etc directives. It is possible that we might be handling that case somewhere which I am not aware of.
Ok, I've reviewed the existing directives and implemented checks based on my understanding:
@auth - allowed, auth rules run after schema validation, mutation rewrite
@cascade - NA, not in schema def
@lambda + @Custom - not allowed, added to validation
@deprecated - allowed
@dgraph - allowed, pred mapping handled in mutation rewrite
@generate - allowed
@hasInverse - allowed, @default not allowed on edges to types
@id - not allowed
@include - NA, not in schema def
@Remote - not allowed
@remoteResponse - allowed
@search - allowed
@secret - allowed, password check is handled seperately
@Skip - NA, not in schema def
@withSubscription - allowed
@lambdaOnMutate - allowed
graphql/schema/rules.go, line 1299 at r4 (raw file):
Previously, NamanJain8 (Naman Jain) wrote…
Also, it would be great to have a test that can check the functioning of these validations.
OK, I've added tests for each to graphql/schema/gqlschema_test.yml
graphql/schema/rules.go, line 1300 at r4 (raw file):
Previously, aman-bansal (aman bansal) wrote…
lets add one more mutation where $now can be part of fields whose data type are DateTime! Also values should be of same data type as their fields definitions. Say "user-random" cannot be default value for a field with Int Data type
OK, added type validations for Int, Float, and Boolean. $now verifies field DateTime, but I'm not sure how DateTime string should be validated – ie how Dgraph is parsing strings when storing values. Also I'm parsing Floats / Ints as 64bit right now, can you confirm that is right?
graphql/schema/wrappers.go, line 2372 at r4 (raw file):
Previously, aman-bansal (aman bansal) wrote…
Let's not put testing logic in the code. we can put non null checks and time date parsing logic to verify that things are working fine. I guess that would be enough validation to work with.
OK, I've removed the testing logic, but I'm still not sure how you want to change test harness to support this kind of validation so the test suite is currently failing.
It seems like we need to add some logic to graphql/resolve/mutation_test.go:245
to recognise we are comparing two date times and support some kind of matching, but without inspecting the schema first I don't know how to do this in a non-fragile way. Suggestions appreciated :)
Do you have in mind that this could change in the future? |
@maaft sure, @aman-bansal hinted at some other use-cases in his original response – support for expressions and other tokens like @aman-bansal do you think we need to create a distinction between |
Theres one lint warning for line length remaining, but it seems like most of the function signatures ignore it anyway so I went for consistency over correctness :) |
Hi team Dgraph 👋🏻 Any feedback on changes? |
Gentle reminder :D |
Thanks @dpeek. @aman-bansal Did you consider the proposal here? https://discuss.dgraph.io/t/graphql-error-non-nullable-field-was-not-present-in-result-from-dgraph/9503/6?u=iyinoluwaayoola The design uses @input directive to define fields that should be allowed in the schema and their default value... seem very similar here. I find the ability to skip fields in the schema very powerful and useful as well. |
No comment from Dgraph team in a month... I guess you guys are busy right now, but given all the community shouting for missing GraphQL features, ignoring contributions seems like an unwise strategy :) Really the only missing bit is some advice on what I should do WRT to testing timestamps. Should I build a specific test harness for just for these? Adapt the existing one? I'm still finding my feet in golang so I worry if I can't just adapt something that exists I'll likely implement something that doesn't align with the ethos of the rest of the codebase. |
Hey @dpeek , I apologize for the delay. The team was busy with the next release-related stuff, etc. Hence, we deferred this for later. This change looks great to me and much appreciated.
It should be okay to use test.v flag lookup that you had earlier. Thanks a lot for the PR. I am approving it. We can merge it once CI passes. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice change. 🎉
@dpeek can you please fix the test cases? Once fixed we can merge the PR |
Hi @aman-bansal, I brought back the test flag check. I don't have much visibility into failing CI details so you might need to address those yourselves. I'm also moving on from dgraph in my project, so won't have much more time to invest in it. |
@dpeek I might pick this up as the timestamp feature is rather important to me but I wouldn't be able to do so until later. In the meantime, the blocking changes seem to be pretty simple. The failing lint test is shown here in GraphqlWrappers: I suspect simply changing that conditional will pass the lint. |
} else { | ||
return "2000-01-01T00:00:00.00Z" | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should fix the lint error.
} else { | |
return "2000-01-01T00:00:00.00Z" | |
} | |
} | |
return "2000-01-01T00:00:00.00Z" | |
🥳 |
…es at create and update (hypermodeinc#8017) Adds support for @default directive and don't remove fields from input types. (cherry picked from commit 7e6f813)
Just adding this here for when it may be picked up for a future release. Food for thought: why not use For example, the default value can be This may even support |
Co-authored-by: shivaji-dgraph <[email protected]>
Co-authored-by: shivaji-dgraph <[email protected]>
Adds support for
@default
directive to GraphQL input schema.Syntax:
Where a value is not provided as input for a mutation, the
add
value will be used if the node is being created, and theupdate
value will be used if the node exists and is being updated. Values are provided as strings, parsed into the correct field type by Dgraph.The string
$now
is replaced by the current DateTime string on the server, ie:Schema validation will check that:
Int
field values can be parsedstrconv.ParseInt
Float
field values can be parsed bystrconv.ParseFloat
Boolean
field values aretrue
orfalse
$now
can only be used with fields of typeDateTime
(could be extended to includeString
?)Schema validation does not currently ensure that
@default
values for enums are a valid member of the enum, so this is allowed:Schema validation also prevents use of
@default
on:@remote
directiveID
Int
,Float
,String
,Boolean
,DateTime
) or an enum@id
,@custom
,@lambda
Output schema changes:
@default(add)
values become optional inAddTypeInput
This change is