Motivation
Support for @auth rules with @custom DQL query.
User Impact
Users will now be able to use @auth rules provided in the Graphql schema with @custom DQL queries also. This will be a major feature in the @custom DQL and will enhance the user experience.
Implementation
Overview
Since @auth rules are defined in the GraphQL schema along with the type definition, we need to apply them to the DQL query based on the type queried in the DQL query. The problem reduces to finding the type of the query. After finding the type of the query, we apply auth rules and start finding the type of predicates and applying auth rules recursively.
Example
For the given schema:-
type Tweets {
id: ID!
text: String! @search(by: [fulltext])
author: User
timestamp: DateTime! @search
}
type User {
screen_name: String! @id
followers: Int @search
tweets: [Tweets] @hasInverse(field: author)
}
type UserTweetCount @remote {
screen_name: String
tweetCount: Int
}
For this @custom DQL query:-
queryUserTweetCounts: [UserTweetCount] @custom(dql: """
query {
queryUserTweetCounts(func: type(User)) {
screen_name: User.screen_name
tweetCount: count(User.tweets)
}
}
""")
The root type is User
. In this query, we apply the @auth rules of type User
at the root and then try to infer the types of predicates. In this query, both are scalar so the @auth rules are added at the root only.
But, for this @custom DQL var block of a query:-
var(func: type(Tweets)) @filter(anyoftext(Tweets.text, $search)) {
Tweets.author {
followers as User.followers
}
authorFollowerCount as sum(val(followers))
}
The root query type is Tweet
. Now the predicate Tweets.author
is of the type User
and similarly User.followers
is of the type User
. The final var
block after application of auth rules will look something like this:-
var(func: type(Tweets)) @filter(anyoftext(Tweets.text, $search)) @filter(TweetAuth){
Tweets.author @filter(UserAuth) {
followers as User.followers @filter(UserAuth)
}
authorFollowerCount as sum(val(followers))
}
In case if root func is not type(TypeName)
, then we try to find the type from filters. For example:-
me(func: has(Tweets.text)) {
...
}
Or
me(func: uid(0x1)) @filter(eq(Tweets.text, "some text")) {
...
}
In both the above queries, type can be deduced from the has
function and eq
filter respectively.
Limitations
There can be cases in which type is impossible to deduce from the existing approach. Some of the examples are:-
me(func: has(name@en)) {
...
}
or
me(func: uid(x, y) {
...
}
For these cases, we might need to introduce a directive in DQL to know which auth rules to apply to the query. For example:-
me(func: uid(x, y) @auth(Tweets) {
....
}
In this case, the @auth directive tells us that auth rules of which type should be applied to the DQL query.
Please feel free to raise questions, and give suggestions on this topic.
cc: @pawan , @abhimanyusinghgaur