Skip to content

Commit

Permalink
fix(GraphQL): handle filters for enum properly (#6887) (#6916)
Browse files Browse the repository at this point in the history
This PR is a breaking change for the `eq` filter on the List of `enum` Types.

This PR fixes bugs in the filter generated for enum types. For the given schema:-
```
enum Status {
   ACTIVE
   DEACTIVATED
}

type VerificationFilter {
    status: [Status]! @search
}
```

the `hash` filter generated for `status` was:
```
input Status_hash {
   eq: [Status]!
   in: [Status]
}
```
whereas, it should be:
```
input Status_hash {
    eq: Status
    in: [Status]
}
```

This PR also fixes incorrect filter generated for `exact` index in List of enum types.
Earlier it was being generated like:
```
input Status_exact {
	eq: [Status]
	in: [Status]
	le: [Status]
	lt: [Status]
	ge: [Status]
	gt: [Status]
	between: Status
}
```
but it should be like:
```
input Status_exact {
	eq: Status
	in: [Status]
	le: Status
	lt: Status
	ge: Status
	gt: Status
	between: Status
}
```

Breaking change lies in the generation of `eq` filters. Earlier it was `eq: [Status]!` but now it is `eq: Status`. Both are syntactically fine but `eq: [Status]!` conflicts with the expected usage of `eq` filter.

(cherry picked from commit 40b20c7)
  • Loading branch information
minhaj-shakeel authored Nov 19, 2020
1 parent cdba5dd commit 2c2ceb4
Show file tree
Hide file tree
Showing 9 changed files with 123 additions and 40 deletions.
8 changes: 4 additions & 4 deletions graphql/e2e/common/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -1453,7 +1453,7 @@ func typenameForInterface(t *testing.T) {
Query: `query {
queryCharacter (filter: {
appearsIn: {
eq: [EMPIRE]
in: [EMPIRE]
}
}) {
name
Expand Down Expand Up @@ -1581,7 +1581,7 @@ func onlytypenameForInterface(t *testing.T) {
Query: `query {
queryCharacter (filter: {
appearsIn: {
eq: [EMPIRE]
in: [EMPIRE]
}
}) {
Expand Down Expand Up @@ -1626,7 +1626,7 @@ func defaultEnumFilter(t *testing.T) {
Query: `query {
queryCharacter (filter: {
appearsIn: {
eq: [EMPIRE]
in: [EMPIRE]
}
}) {
name
Expand Down Expand Up @@ -2230,7 +2230,7 @@ func queryWithCascade(t *testing.T) {
{
name: "parameterized cascade on interface ",
query: `query {
queryCharacter (filter: { appearsIn: { eq: [EMPIRE] } }) @cascade(fields:["appearsIn"]){
queryCharacter (filter: { appearsIn: { in: [EMPIRE] } }) @cascade(fields:["appearsIn"]){
name
appearsIn
}
Expand Down
54 changes: 53 additions & 1 deletion graphql/resolve/query_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,25 @@
}
-
name: "in filter on enum type"
name: "in filter on field which is of enum type"
gqlquery: |
query{
queryVerification(filter: {prevStatus: {in: [ACTIVE, DEACTIVATED]}}){
name
prevStatus
}
}
dgquery: |-
query {
queryVerification(func: type(Verification)) @filter(eq(Verification.prevStatus, "ACTIVE", "DEACTIVATED")) {
name : Verification.name
prevStatus : Verification.prevStatus
dgraph.uid : uid
}
}
-
name: "in filter on field which is a List of enum type"
gqlquery: |
query{
queryVerification(filter: {status: {in: [ACTIVE, DEACTIVATED]}}){
Expand All @@ -33,6 +51,40 @@
dgraph.uid : uid
}
}
- name: "eq filter on field which is a List of enum type"
gqlquery: |
query{
queryVerification(filter: {status: {eq: ACTIVE}}){
name
status
}
}
dgquery: |-
query {
queryVerification(func: type(Verification)) @filter(eq(Verification.status, "ACTIVE")) {
name : Verification.name
status : Verification.status
dgraph.uid : uid
}
}
- name: "le filter on field which is a List of enum type"
gqlquery: |
query{
queryVerification(filter: {status: {le: INACTIVE}}){
name
status
}
}
dgquery: |-
query {
queryVerification(func: type(Verification)) @filter(le(Verification.status, "INACTIVE")) {
name : Verification.name
status : Verification.status
dgraph.uid : uid
}
}
-
name: "Point query near filter"
gqlquery: |
Expand Down
6 changes: 4 additions & 2 deletions graphql/resolve/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -86,15 +86,17 @@ type Human implements Character & Employee {
female: Boolean
}

# just for testing IN filter on enum types
# just for testing filters on enum types

type Verification {
name: String @search(by: [exact])
status: Status @search
status: [Status!]! @search(by: [exact])
prevStatus: Status! @search
}

enum Status {
ACTIVE
INACTIVE
DEACTIVATED
}

Expand Down
29 changes: 22 additions & 7 deletions graphql/schema/gqlschema.go
Original file line number Diff line number Diff line change
Expand Up @@ -1113,8 +1113,8 @@ func addFieldFilters(schema *ast.Schema, defn *ast.Definition) {
addFilterArgument(schema, fld)

// Ordering and pagination, however, only makes sense for fields of
// list types (not scalar lists).
if isTypeList(fld) {
// list types (not scalar lists or enum lists).
if isTypeList(fld) && !isEnumList(fld, schema) {
addOrderArgument(schema, fld)

// Pagination even makes sense when there's no orderables because
Expand Down Expand Up @@ -1220,16 +1220,25 @@ func getFilterTypes(schema *ast.Schema, fld *ast.FieldDefinition, filterName str
for i, search := range searchArgs {
filterNames[i] = builtInFilters[search]

// For enum type, if the index is "hash" or "exact", we construct filter named
// enumTypeName_hash/ enumTypeName_exact from StringHashFilter/StringExactFilter
// by replacing the Argument type.
if (search == "hash" || search == "exact") && schema.Types[fld.Type.Name()].Kind == ast.Enum {
stringFilterName := fmt.Sprintf("String%sFilter", strings.Title(search))
var l ast.FieldList

for _, i := range schema.Types[stringFilterName].Fields {
typ := fld.Type

// In case of IN filter we need to construct List of enums as Input Type.
if i.Type.Elem != nil && fld.Type.Elem == nil {
typ = &ast.Type{Elem: &ast.Type{NamedType: fld.Type.NamedType, NonNull: fld.Type.NonNull}}
enumTypeName := fld.Type.Name()
var typ *ast.Type

if i.Type.Elem == nil {
typ = &ast.Type{
NamedType: enumTypeName,
}
} else {
typ = &ast.Type{
Elem: &ast.Type{NamedType: enumTypeName},
}
}

l = append(l, &ast.FieldDefinition{
Expand Down Expand Up @@ -1356,6 +1365,12 @@ func isTypeList(fld *ast.FieldDefinition) bool {
return !scalar && fld.Type.Elem != nil
}

// Returns true if given field is a list of enum
func isEnumList(fld *ast.FieldDefinition, sch *ast.Schema) bool {
typeDefn := sch.Types[fld.Type.Name()]
return typeDefn.Kind == "ENUM" && fld.Type.Elem != nil
}

func hasOrderables(defn *ast.Definition) bool {
return fieldAny(defn.Fields, isOrderable)
}
Expand Down
16 changes: 9 additions & 7 deletions graphql/schema/testdata/schemagen/input/searchables.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@ type Post {
isPublished: Boolean @search

postType: PostType @search
postTypeTrigram: PostType @search(by: [trigram])
postTypeRegexp: PostType @search(by: [regexp])
postTypeExact: PostType @search(by: [exact])
postTypeHash: PostType @search(by: [hash])
postTypeRegexpExact: PostType @search(by: [exact, regexp])
postTypeHashRegexp: PostType @search(by: [hash, regexp])
postTypeNone: PostType @search(by: [])
postTypeNonNull: PostType! @search
postTypeList: [PostType] @search
postTypeTrigram: PostType @search(by: [trigram])
postTypeRegexp: PostType @search(by: [regexp])
postTypeExact: [PostType] @search(by: [exact])
postTypeHash: PostType @search(by: [hash])
postTypeRegexpExact: PostType @search(by: [exact, regexp])
postTypeHashRegexp: PostType @search(by: [hash, regexp])
postTypeNone: PostType @search(by: [])
}

enum PostType {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ interface Character @secret(field: "password") {
id: ID!
name: String! @search(by: [exact])
friends(filter: CharacterFilter, order: CharacterOrder, first: Int, offset: Int): [Character]
appearsIn(first: Int, offset: Int): [Episode!]! @search
appearsIn: [Episode!]! @search
friendsAggregate(filter: CharacterFilter): CharacterAggregateResult
}

type Human implements Character @secret(field: "password") {
id: ID!
name: String! @search(by: [exact])
friends(filter: CharacterFilter, order: CharacterOrder, first: Int, offset: Int): [Character]
appearsIn(first: Int, offset: Int): [Episode!]! @search
appearsIn: [Episode!]! @search
starships(filter: StarshipFilter, order: StarshipOrder, first: Int, offset: Int): [Starship]
totalCredits: Int
friendsAggregate(filter: CharacterFilter): CharacterAggregateResult
Expand All @@ -25,7 +25,7 @@ type Droid implements Character @secret(field: "password") {
id: ID!
name: String! @search(by: [exact])
friends(filter: CharacterFilter, order: CharacterOrder, first: Int, offset: Int): [Character]
appearsIn(first: Int, offset: Int): [Episode!]! @search
appearsIn: [Episode!]! @search
primaryFunction: String
friendsAggregate(filter: CharacterFilter): CharacterAggregateResult
}
Expand Down Expand Up @@ -512,8 +512,8 @@ input DroidRef {
}

input Episode_hash {
eq: [Episode!]!
in: [Episode!]!
eq: Episode
in: [Episode]
}

input HumanFilter {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ interface Character {
id: ID!
name: String! @search(by: [exact])
friends(filter: CharacterFilter, order: CharacterOrder, first: Int, offset: Int): [Character]
appearsIn(first: Int, offset: Int): [Episode!]! @search
appearsIn: [Episode!]! @search
friendsAggregate(filter: CharacterFilter): CharacterAggregateResult
}

type Human implements Character {
id: ID!
name: String! @search(by: [exact])
friends(filter: CharacterFilter, order: CharacterOrder, first: Int, offset: Int): [Character]
appearsIn(first: Int, offset: Int): [Episode!]! @search
appearsIn: [Episode!]! @search
starships(filter: StarshipFilter, order: StarshipOrder, first: Int, offset: Int): [Starship]
totalCredits: Int
friendsAggregate(filter: CharacterFilter): CharacterAggregateResult
Expand All @@ -25,7 +25,7 @@ type Droid implements Character {
id: ID!
name: String! @search(by: [exact])
friends(filter: CharacterFilter, order: CharacterOrder, first: Int, offset: Int): [Character]
appearsIn(first: Int, offset: Int): [Episode!]! @search
appearsIn: [Episode!]! @search
primaryFunction: String
friendsAggregate(filter: CharacterFilter): CharacterAggregateResult
}
Expand Down Expand Up @@ -507,8 +507,8 @@ input DroidRef {
}

input Episode_hash {
eq: [Episode!]!
in: [Episode!]!
eq: Episode
in: [Episode]
}

input HumanFilter {
Expand Down
20 changes: 16 additions & 4 deletions graphql/schema/testdata/schemagen/output/searchables.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@ type Post {
score: Float @search
isPublished: Boolean @search
postType: PostType @search
postTypeNonNull: PostType! @search
postTypeList: [PostType] @search
postTypeTrigram: PostType @search(by: [trigram])
postTypeRegexp: PostType @search(by: [regexp])
postTypeExact: PostType @search(by: [exact])
postTypeExact: [PostType] @search(by: [exact])
postTypeHash: PostType @search(by: [hash])
postTypeRegexpExact: PostType @search(by: [exact,regexp])
postTypeHashRegexp: PostType @search(by: [hash,regexp])
Expand Down Expand Up @@ -335,6 +337,8 @@ enum PostHasFilter {
score
isPublished
postType
postTypeNonNull
postTypeList
postTypeTrigram
postTypeRegexp
postTypeExact
Expand Down Expand Up @@ -379,9 +383,11 @@ input AddPostInput {
score: Float
isPublished: Boolean
postType: PostType
postTypeNonNull: PostType!
postTypeList: [PostType]
postTypeTrigram: PostType
postTypeRegexp: PostType
postTypeExact: PostType
postTypeExact: [PostType]
postTypeHash: PostType
postTypeRegexpExact: PostType
postTypeHashRegexp: PostType
Expand All @@ -406,6 +412,8 @@ input PostFilter {
score: FloatFilter
isPublished: Boolean
postType: PostType_hash
postTypeNonNull: PostType_hash
postTypeList: PostType_hash
postTypeTrigram: StringRegExpFilter
postTypeRegexp: StringRegExpFilter
postTypeExact: PostType_exact
Expand Down Expand Up @@ -442,9 +450,11 @@ input PostPatch {
score: Float
isPublished: Boolean
postType: PostType
postTypeNonNull: PostType
postTypeList: [PostType]
postTypeTrigram: PostType
postTypeRegexp: PostType
postTypeExact: PostType
postTypeExact: [PostType]
postTypeHash: PostType
postTypeRegexpExact: PostType
postTypeHashRegexp: PostType
Expand All @@ -469,9 +479,11 @@ input PostRef {
score: Float
isPublished: Boolean
postType: PostType
postTypeNonNull: PostType
postTypeList: [PostType]
postTypeTrigram: PostType
postTypeRegexp: PostType
postTypeExact: PostType
postTypeExact: [PostType]
postTypeHash: PostType
postTypeRegexpExact: PostType
postTypeHashRegexp: PostType
Expand Down
10 changes: 5 additions & 5 deletions graphql/schema/testdata/schemagen/output/union.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ interface Character {
name: String! @search(by: [exact])
friends(filter: CharacterFilter, order: CharacterOrder, first: Int, offset: Int): [Character]
enemyOf(filter: ResidentFilter): Resident
appearsIn(first: Int, offset: Int): [Episode!]! @search
appearsIn: [Episode!]! @search
friendsAggregate(filter: CharacterFilter): CharacterAggregateResult
}

Expand All @@ -16,7 +16,7 @@ type Human implements Character {
name: String! @search(by: [exact])
friends(filter: CharacterFilter, order: CharacterOrder, first: Int, offset: Int): [Character]
enemyOf(filter: ResidentFilter): Resident
appearsIn(first: Int, offset: Int): [Episode!]! @search
appearsIn: [Episode!]! @search
starships(filter: StarshipFilter, order: StarshipOrder, first: Int, offset: Int): [Starship]
totalCredits: Int
friendsAggregate(filter: CharacterFilter): CharacterAggregateResult
Expand All @@ -28,7 +28,7 @@ type Droid implements Character {
name: String! @search(by: [exact])
friends(filter: CharacterFilter, order: CharacterOrder, first: Int, offset: Int): [Character]
enemyOf(filter: ResidentFilter): Resident
appearsIn(first: Int, offset: Int): [Episode!]! @search
appearsIn: [Episode!]! @search
primaryFunction: String
friendsAggregate(filter: CharacterFilter): CharacterAggregateResult
}
Expand Down Expand Up @@ -569,8 +569,8 @@ input DroidRef {
}

input Episode_hash {
eq: [Episode!]!
in: [Episode!]!
eq: Episode
in: [Episode]
}

input HumanFilter {
Expand Down

0 comments on commit 2c2ceb4

Please sign in to comment.