Skip to content

Commit

Permalink
fix: graphQL query ignores condition equalTo with value false (pa…
Browse files Browse the repository at this point in the history
  • Loading branch information
yomybaby authored Jul 3, 2022
1 parent 6e68656 commit 7f5a15d
Show file tree
Hide file tree
Showing 3 changed files with 154 additions and 16 deletions.
124 changes: 124 additions & 0 deletions spec/ParseGraphQLServer.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -9516,6 +9516,130 @@ describe('ParseGraphQLServer', () => {
}
});

it('should support where argument on object field that contains false boolean value or 0 number value', async () => {
try {
const someObjectFieldValue1 = {
foo: { bar: true, baz: 100 },
};

const someObjectFieldValue2 = {
foo: { bar: false, baz: 0 },
};

const object1 = new Parse.Object('SomeClass');
await object1.save({
someObjectField: someObjectFieldValue1,
});
const object2 = new Parse.Object('SomeClass');
await object2.save({
someObjectField: someObjectFieldValue2,
});

const whereToObject1 = {
someObjectField: {
equalTo: { key: 'foo.bar', value: true },
notEqualTo: { key: 'foo.baz', value: 0 },
},
};
const whereToObject2 = {
someObjectField: {
notEqualTo: { key: 'foo.bar', value: true },
equalTo: { key: 'foo.baz', value: 0 },
},
};

const whereToAll = {
someObjectField: {
lessThan: { key: 'foo.baz', value: 101 },
},
};

const whereToNone = {
someObjectField: {
notEqualTo: { key: 'foo.bar', value: true },
equalTo: { key: 'foo.baz', value: 1 },
},
};

const queryResult = await apolloClient.query({
query: gql`
query GetSomeObject(
$id1: ID!
$id2: ID!
$whereToObject1: SomeClassWhereInput
$whereToObject2: SomeClassWhereInput
$whereToAll: SomeClassWhereInput
$whereToNone: SomeClassWhereInput
) {
obj1: someClass(id: $id1) {
id
someObjectField
}
obj2: someClass(id: $id2) {
id
someObjectField
}
onlyObj1: someClasses(where: $whereToObject1) {
edges {
node {
id
someObjectField
}
}
}
onlyObj2: someClasses(where: $whereToObject2) {
edges {
node {
id
someObjectField
}
}
}
all: someClasses(where: $whereToAll) {
edges {
node {
id
someObjectField
}
}
}
none: someClasses(where: $whereToNone) {
edges {
node {
id
someObjectField
}
}
}
}
`,
variables: {
id1: object1.id,
id2: object2.id,
whereToObject1,
whereToObject2,
whereToAll,
whereToNone,
},
});

const { obj1, obj2, onlyObj1, onlyObj2, all, none } = queryResult.data;

expect(obj1.someObjectField).toEqual(someObjectFieldValue1);
expect(obj2.someObjectField).toEqual(someObjectFieldValue2);

// Checks class query results
expect(onlyObj1.edges.length).toEqual(1);
expect(onlyObj1.edges[0].node.someObjectField).toEqual(someObjectFieldValue1);
expect(onlyObj2.edges.length).toEqual(1);
expect(onlyObj2.edges[0].node.someObjectField).toEqual(someObjectFieldValue2);
expect(all.edges.length).toEqual(2);
expect(none.edges.length).toEqual(0);
} catch (e) {
handleError(e);
}
});

it('should support object composed queries', async () => {
try {
const someObjectFieldValue1 = {
Expand Down
44 changes: 29 additions & 15 deletions src/Adapters/Storage/Postgres/PostgresStorageAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,22 @@ const toPostgresValue = value => {
return value;
};

const toPostgresValueCastType = value => {
const postgresValue = toPostgresValue(value);
let castType;
switch (typeof postgresValue) {
case 'number':
castType = 'double precision';
break;
case 'boolean':
castType = 'boolean';
break;
default:
castType = undefined;
}
return castType;
};

const transformValue = value => {
if (typeof value === 'object' && value.__type === 'Pointer') {
return value.objectId;
Expand Down Expand Up @@ -369,9 +385,12 @@ const buildWhereClause = ({ schema, query, index, caseInsensitive }): WhereClaus
);
} else {
if (fieldName.indexOf('.') >= 0) {
const constraintFieldName = transformDotField(fieldName);
const castType = toPostgresValueCastType(fieldValue.$ne);
const constraintFieldName = castType
? `CAST ((${transformDotField(fieldName)}) AS ${castType})`
: transformDotField(fieldName);
patterns.push(
`(${constraintFieldName} <> $${index} OR ${constraintFieldName} IS NULL)`
`(${constraintFieldName} <> $${index + 1} OR ${constraintFieldName} IS NULL)`
);
} else if (typeof fieldValue.$ne === 'object' && fieldValue.$ne.$relativeTime) {
throw new Parse.Error(
Expand Down Expand Up @@ -401,8 +420,12 @@ const buildWhereClause = ({ schema, query, index, caseInsensitive }): WhereClaus
index += 1;
} else {
if (fieldName.indexOf('.') >= 0) {
const castType = toPostgresValueCastType(fieldValue.$eq);
const constraintFieldName = castType
? `CAST ((${transformDotField(fieldName)}) AS ${castType})`
: transformDotField(fieldName);
values.push(fieldValue.$eq);
patterns.push(`${transformDotField(fieldName)} = $${index++}`);
patterns.push(`${constraintFieldName} = $${index++}`);
} else if (typeof fieldValue.$eq === 'object' && fieldValue.$eq.$relativeTime) {
throw new Parse.Error(
Parse.Error.INVALID_JSON,
Expand Down Expand Up @@ -771,20 +794,11 @@ const buildWhereClause = ({ schema, query, index, caseInsensitive }): WhereClaus
Object.keys(ParseToPosgresComparator).forEach(cmp => {
if (fieldValue[cmp] || fieldValue[cmp] === 0) {
const pgComparator = ParseToPosgresComparator[cmp];
let postgresValue = toPostgresValue(fieldValue[cmp]);
let constraintFieldName;
let postgresValue = toPostgresValue(fieldValue[cmp]);

if (fieldName.indexOf('.') >= 0) {
let castType;
switch (typeof postgresValue) {
case 'number':
castType = 'double precision';
break;
case 'boolean':
castType = 'boolean';
break;
default:
castType = undefined;
}
const castType = toPostgresValueCastType(fieldValue[cmp]);
constraintFieldName = castType
? `CAST ((${transformDotField(fieldName)}) AS ${castType})`
: transformDotField(fieldName);
Expand Down
2 changes: 1 addition & 1 deletion src/GraphQL/transformers/query.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ const transformQueryConstraintInputToParse = (
* }
* }
*/
if (fieldValue.key && fieldValue.value && parentConstraints && parentFieldName) {
if (fieldValue.key && fieldValue.value !== undefined && parentConstraints && parentFieldName) {
delete parentConstraints[parentFieldName];
parentConstraints[`${parentFieldName}.${fieldValue.key}`] = {
...parentConstraints[`${parentFieldName}.${fieldValue.key}`],
Expand Down

0 comments on commit 7f5a15d

Please sign in to comment.