Skip to content

Commit

Permalink
Add count query at root
Browse files Browse the repository at this point in the history
  • Loading branch information
vmrajas committed Oct 27, 2020
1 parent d9569ba commit 78f7d00
Show file tree
Hide file tree
Showing 5 changed files with 179 additions and 2 deletions.
83 changes: 83 additions & 0 deletions graphql/resolve/auth_query_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,25 @@
UserSecretAuth2 as var(func: uid(UserSecret1)) @filter(eq(UserSecret.ownedBy, "user1")) @cascade
}
- name: "Auth with count"
gqlquery: |
query {
aggregateUserSecret {
count
}
}
jwtvar:
USER: "user1"
dgquery: |-
query {
aggregateUserSecret(func: uid(UserSecretRoot)) {
count : count(uid)
}
UserSecretRoot as var(func: uid(UserSecret1)) @filter(uid(UserSecretAuth2))
UserSecret1 as var(func: type(UserSecret))
UserSecretAuth2 as var(func: uid(UserSecret1)) @filter(eq(UserSecret.ownedBy, "user1")) @cascade
}
- name: "Auth with top level filter : get"
gqlquery: |
query {
Expand Down Expand Up @@ -547,6 +566,21 @@
queryIssue()
}
- name: "Count on Auth with top level AND rbac false"
gqlquery: |
query {
aggregateIssue {
count
}
}
jwtvar:
ROLE: "USER"
USER: "user1"
dgquery: |-
query {
aggregateIssue()
}
- name: "Auth with top level OR rbac true"
gqlquery: |
Expand All @@ -568,6 +602,25 @@
Project1 as var(func: type(Project))
}
- name: "Count on Auth with top level OR rbac true"
gqlquery: |
query {
aggregateProject {
count
}
}
jwtvar:
ROLE: "ADMIN"
USER: "user1"
dgquery: |-
query {
aggregateProject(func: uid(ProjectRoot)) {
count : count(uid)
}
ProjectRoot as var(func: uid(Project1))
Project1 as var(func: type(Project))
}
- name: "Query with missing jwt variables"
gqlquery: |
query {
Expand Down Expand Up @@ -942,6 +995,36 @@
}
}
- name: "Count with complex auth filter"
gqlquery: |
query {
aggregateMovie {
count
}
}
jwtvar:
USER: "user1"
dgquery: |-
query {
aggregateMovie(func: uid(MovieRoot)) {
count : count(uid)
}
MovieRoot as var(func: uid(Movie1)) @filter((NOT (uid(MovieAuth2)) AND (uid(MovieAuth3) OR uid(MovieAuth4))))
Movie1 as var(func: type(Movie))
MovieAuth2 as var(func: uid(Movie1)) @filter(eq(Movie.hidden, true)) @cascade
MovieAuth3 as var(func: uid(Movie1)) @cascade {
regionsAvailable : Movie.regionsAvailable {
users : Region.users @filter(eq(User.username, "user1"))
dgraph.uid : uid
}
dgraph.uid : uid
}
MovieAuth4 as var(func: uid(Movie1)) @cascade {
regionsAvailable : Movie.regionsAvailable @filter(eq(Region.global, true))
dgraph.uid : uid
}
}
- name: "Query with missing variable - top level"
gqlquery: |
query {
Expand Down
61 changes: 59 additions & 2 deletions graphql/resolve/query_rewriter.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ func (qr *queryRewriter) Rewrite(
ctx context.Context,
gqlQuery schema.Query) (*gql.GraphQuery, error) {

if gqlQuery.Type().InterfaceImplHasAuthRules() {
if gqlQuery.ConstructedFor().InterfaceImplHasAuthRules() {
return &gql.GraphQuery{Attr: gqlQuery.ResponseName() + "()"}, nil
}

Expand All @@ -107,7 +107,7 @@ func (qr *queryRewriter) Rewrite(
authVariables: authVariables,
varGen: NewVariableGenerator(),
selector: queryAuthSelector,
parentVarName: gqlQuery.Type().Name() + "Root",
parentVarName: gqlQuery.ConstructedFor().Name() + "Root",
}
authRw.hasAuthRules = hasAuthRules(gqlQuery, authRw)
authRw.hasCascade = hasCascadeDirective(gqlQuery)
Expand Down Expand Up @@ -136,11 +136,68 @@ func (qr *queryRewriter) Rewrite(
return rewriteAsQuery(gqlQuery, authRw), nil
case schema.PasswordQuery:
return passwordQuery(gqlQuery, authRw)
case schema.AggregateQuery:
return aggregateQuery(gqlQuery, authRw), nil
default:
return nil, errors.Errorf("unimplemented query type %s", gqlQuery.QueryType())
}
}

func aggregateQuery(query schema.Query, authRw *authRewriter) *gql.GraphQuery {

// Get the type which the count query is written for
mainType := query.ConstructedFor()

rbac := authRw.evaluateStaticRules(mainType)
dgQuery := &gql.GraphQuery{
Attr: query.Name(),
}

if rbac == schema.Negative {
dgQuery.Attr = dgQuery.Attr + "()"
return dgQuery
}

if authRw != nil && (authRw.isWritingAuth || authRw.filterByUid) && (authRw.varName != "" || authRw.parentVarName != "") {
// When rewriting auth rules, they always start like
// Todo2 as var(func: uid(Todo1)) @cascade {
// Where Todo1 is the variable generated from the filter of the field
// we are adding auth to.

authRw.addVariableUIDFunc(dgQuery)
// This is executed when querying while performing delete mutation request since
// in case of delete mutation we already have variable `MutationQueryVar` at root level.
if authRw.filterByUid {
// Since the variable is only added at the top level we reset the `authRW` variables.
authRw.varName = ""
authRw.filterByUid = false
}
} else if ids := idFilter(extractQueryFilter(query), mainType.IDField()); ids != nil {
addUIDFunc(dgQuery, ids)
} else {
addTypeFunc(dgQuery, mainType.Name())
}

// Add filter
filter, _ := query.ArgValue("filter").(map[string]interface{})
_ = addFilter(dgQuery, mainType, filter)

// Add selection set. Currently, it will only be count
for _, f := range query.SelectionSet() {
if f.Name() == "count" {
child := &gql.GraphQuery{
Alias: f.DgraphAlias(),
Attr: "count(uid)",
}
dgQuery.Children = append(dgQuery.Children, child)
}
}

dgQuery = authRw.addAuthQueries(mainType, dgQuery, rbac)

return dgQuery
}

func passwordQuery(m schema.Query, authRw *authRewriter) (*gql.GraphQuery, error) {
xid, uid, err := m.IDArgValue()
if err != nil {
Expand Down
15 changes: 15 additions & 0 deletions graphql/resolve/query_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1246,6 +1246,21 @@
}
}
-
name: "Count Query"
gqlquery: |
query {
aggregateCountry(filter: { name: { regexp: "/.*ust.*/" }}) {
count
}
}
dgquery: |-
query {
aggregateCountry(func: type(Country)) @filter(regexp(Country.name, /.*ust.*/)) {
count : count(uid)
}
}
-
name: "Skip directive"
variables:
Expand Down
1 change: 1 addition & 0 deletions graphql/resolve/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ func (rf *resolverFactory) WithConventionResolvers(

queries := append(s.Queries(schema.GetQuery), s.Queries(schema.FilterQuery)...)
queries = append(queries, s.Queries(schema.PasswordQuery)...)
queries = append(queries, s.Queries(schema.AggregateQuery)...)
for _, q := range queries {
rf.WithQueryResolver(q, func(q schema.Query) QueryResolver {
return NewQueryResolver(fns.Qrw, fns.Ex, StdQueryCompletion())
Expand Down
21 changes: 21 additions & 0 deletions graphql/schema/wrappers.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ type FieldHTTPConfig struct {
const (
GetQuery QueryType = "get"
FilterQuery QueryType = "query"
AggregateQuery QueryType = "aggregate"
SchemaQuery QueryType = "schema"
PasswordQuery QueryType = "checkPassword"
HTTPQuery QueryType = "http"
Expand Down Expand Up @@ -153,6 +154,7 @@ type Mutation interface {
// A Query is a field (from the schema's Query type) from an Operation
type Query interface {
Field
ConstructedFor() Type
QueryType() QueryType
DQLQuery() string
Rename(newName string)
Expand Down Expand Up @@ -1393,6 +1395,23 @@ func (q *query) EnumValues() []string {
return nil
}

func (q *query) ConstructedFor() Type {
if q.QueryType() != AggregateQuery {
return q.Type()
}

// Its of type AggregateQuery
queryName := q.Type().Name()
typeName := queryName[:len(queryName)-15]
return &astType{
typ: &ast.Type{
NamedType: typeName,
},
inSchema: q.op.inSchema,
dgraphPredicate: q.op.inSchema.dgraphPredicate,
}
}

func (q *query) QueryType() QueryType {
return queryType(q.Name(), q.op.inSchema.customDirectives["Query"][q.Name()])
}
Expand Down Expand Up @@ -1421,6 +1440,8 @@ func queryType(name string, custom *ast.Directive) QueryType {
return FilterQuery
case strings.HasPrefix(name, "check"):
return PasswordQuery
case strings.HasPrefix(name, "aggregate"):
return AggregateQuery
default:
return NotSupportedQuery
}
Expand Down

0 comments on commit 78f7d00

Please sign in to comment.