Skip to content

Commit ef14ca5

Browse files
douglasmuraokadavimacedo
authored andcommitted
GraphQL Object constraints (#5715)
* GraphQL Object constraints Implements the GraphQL Object constraints, which allows us to filter queries results using the `$eq`, `$lt`, `$gt`, `$in`, and other Parse supported constraints. Example: ``` query objects { findMyClass(where: { objField: { _eq: { key: 'foo.bar', value: 'hello' }, _gt: { key: 'foo.number', value: 10 }, _lt: { key: 'anotherNumber', value: 5 } } }) { results { objectId } } } ``` In the example above, we have the `findMyClass` query (automatically generated for the `MyClass` class), and a field named `objField` whose type is Object. The object below represents a valid `objField` value and would satisfy all constraints: ``` { "foo": { "bar": "hello", "number": 11 }, "anotherNumber": 4 } ``` The Object constraint is applied only when using Parse class object type queries. When using "generic" queries such as `get` and `find`, this type of constraint is not available. * Objects constraints not working on Postgres Fixes the $eq, $ne, $gt, and $lt constraints when applied on an Object type field. * Fix object constraint field name * Fix Postgres constraints indexes * fix: Object type composed constraints not working * fix: Rename key and value fields * refactor: Object constraints for generic queries * fix: Object constraints not working on Postgres
1 parent e0690d0 commit ef14ca5

14 files changed

+800
-522
lines changed

spec/AuthenticationAdapters.spec.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -1178,7 +1178,9 @@ describe('phant auth adapter', () => {
11781178
};
11791179
const { adapter } = authenticationLoader.loadAuthAdapter('phantauth', {});
11801180

1181-
spyOn(httpsRequest, 'get').and.callFake(() => Promise.resolve({ sub: 'invalidID' }));
1181+
spyOn(httpsRequest, 'get').and.callFake(() =>
1182+
Promise.resolve({ sub: 'invalidID' })
1183+
);
11821184
try {
11831185
await adapter.validateAuthData(authData);
11841186
fail();

spec/ParseGraphQLServer.spec.js

+160-8
Original file line numberDiff line numberDiff line change
@@ -5273,7 +5273,10 @@ describe('ParseGraphQLServer', () => {
52735273
});
52745274

52755275
it('should support object values', async () => {
5276-
const someFieldValue = { foo: 'bar' };
5276+
const someFieldValue = {
5277+
foo: { bar: 'baz' },
5278+
number: 10,
5279+
};
52775280

52785281
const createResult = await apolloClient.mutate({
52795282
mutation: gql`
@@ -5314,30 +5317,179 @@ describe('ParseGraphQLServer', () => {
53145317
},
53155318
});
53165319

5317-
const getResult = await apolloClient.query({
5320+
const where = {
5321+
someField: {
5322+
_eq: { _key: 'foo.bar', _value: 'baz' },
5323+
_ne: { _key: 'foo.bar', _value: 'bat' },
5324+
_gt: { _key: 'number', _value: 9 },
5325+
_lt: { _key: 'number', _value: 11 },
5326+
},
5327+
};
5328+
const queryResult = await apolloClient.query({
53185329
query: gql`
5319-
query GetSomeObject($objectId: ID!) {
5330+
query GetSomeObject(
5331+
$objectId: ID!
5332+
$where: SomeClassConstraints
5333+
$genericWhere: Object
5334+
) {
53205335
objects {
53215336
get(className: "SomeClass", objectId: $objectId)
5322-
findSomeClass(where: { someField: { _exists: true } }) {
5337+
findSomeClass(where: $where) {
53235338
results {
53245339
objectId
5340+
someField
53255341
}
53265342
}
5343+
find(className: "SomeClass", where: $genericWhere) {
5344+
results
5345+
}
53275346
}
53285347
}
53295348
`,
53305349
variables: {
53315350
objectId: createResult.data.objects.create.objectId,
5351+
where,
5352+
genericWhere: where, // where and genericWhere types are different
53325353
},
53335354
});
53345355

5335-
const { someField } = getResult.data.objects.get;
5356+
const {
5357+
get: getResult,
5358+
findSomeClass,
5359+
find,
5360+
} = queryResult.data.objects;
5361+
5362+
const { someField } = getResult;
53365363
expect(typeof someField).toEqual('object');
53375364
expect(someField).toEqual(someFieldValue);
5338-
expect(getResult.data.objects.findSomeClass.results.length).toEqual(
5339-
2
5340-
);
5365+
5366+
// Checks class query results
5367+
expect(findSomeClass.results.length).toEqual(2);
5368+
expect(findSomeClass.results[0].someField).toEqual(someFieldValue);
5369+
expect(findSomeClass.results[1].someField).toEqual(someFieldValue);
5370+
5371+
// Checks generic query results
5372+
expect(find.results.length).toEqual(2);
5373+
expect(find.results[0].someField).toEqual(someFieldValue);
5374+
expect(find.results[1].someField).toEqual(someFieldValue);
5375+
});
5376+
5377+
it('should support object composed queries', async () => {
5378+
const someFieldValue = {
5379+
lorem: 'ipsum',
5380+
number: 10,
5381+
};
5382+
const someFieldValue2 = {
5383+
foo: {
5384+
test: 'bar',
5385+
},
5386+
number: 10,
5387+
};
5388+
5389+
const createResult = await apolloClient.mutate({
5390+
mutation: gql`
5391+
mutation CreateSomeObject($fields: Object, $fields2: Object) {
5392+
objects {
5393+
create1: create(className: "SomeClass", fields: $fields) {
5394+
objectId
5395+
}
5396+
create2: create(className: "SomeClass", fields: $fields2) {
5397+
objectId
5398+
}
5399+
}
5400+
}
5401+
`,
5402+
variables: {
5403+
fields: {
5404+
someField: someFieldValue,
5405+
},
5406+
fields2: {
5407+
someField: someFieldValue2,
5408+
},
5409+
},
5410+
});
5411+
5412+
await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear();
5413+
5414+
const where = {
5415+
_and: [
5416+
{
5417+
someField: {
5418+
_gt: { _key: 'number', _value: 9 },
5419+
},
5420+
},
5421+
{
5422+
someField: {
5423+
_lt: { _key: 'number', _value: 11 },
5424+
},
5425+
},
5426+
{
5427+
_or: [
5428+
{
5429+
someField: {
5430+
_eq: { _key: 'lorem', _value: 'ipsum' },
5431+
},
5432+
},
5433+
{
5434+
someField: {
5435+
_eq: { _key: 'foo.test', _value: 'bar' },
5436+
},
5437+
},
5438+
],
5439+
},
5440+
],
5441+
};
5442+
const findResult = await apolloClient.query({
5443+
query: gql`
5444+
query FindSomeObject(
5445+
$where: SomeClassConstraints
5446+
$genericWhere: Object
5447+
) {
5448+
objects {
5449+
findSomeClass(where: $where) {
5450+
results {
5451+
objectId
5452+
someField
5453+
}
5454+
}
5455+
find(className: "SomeClass", where: $genericWhere) {
5456+
results
5457+
}
5458+
}
5459+
}
5460+
`,
5461+
variables: {
5462+
where,
5463+
genericWhere: where, // where and genericWhere types are different
5464+
},
5465+
});
5466+
5467+
const { create1, create2 } = createResult.data.objects;
5468+
const { findSomeClass, find } = findResult.data.objects;
5469+
5470+
// Checks class query results
5471+
const { results } = findSomeClass;
5472+
expect(results.length).toEqual(2);
5473+
expect(
5474+
results.find(result => result.objectId === create1.objectId)
5475+
.someField
5476+
).toEqual(someFieldValue);
5477+
expect(
5478+
results.find(result => result.objectId === create2.objectId)
5479+
.someField
5480+
).toEqual(someFieldValue2);
5481+
5482+
// Checks generic query results
5483+
const { results: genericResults } = find;
5484+
expect(genericResults.length).toEqual(2);
5485+
expect(
5486+
genericResults.find(result => result.objectId === create1.objectId)
5487+
.someField
5488+
).toEqual(someFieldValue);
5489+
expect(
5490+
genericResults.find(result => result.objectId === create2.objectId)
5491+
.someField
5492+
).toEqual(someFieldValue2);
53415493
});
53425494

53435495
it('should support array values', async () => {

spec/ParseQuery.spec.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,7 @@ describe('Parse.Query testing', () => {
369369
});
370370

371371
it('nested containedIn string with single quote', async () => {
372-
const obj = new TestObject({ nested: { foo: ["single'quote"]} });
372+
const obj = new TestObject({ nested: { foo: ["single'quote"] } });
373373
await obj.save();
374374
const query = new Parse.Query(TestObject);
375375
query.containedIn('nested.foo', ["single'quote"]);

spec/ParseWebSocketServer.spec.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
const { ParseWebSocketServer } = require('../lib/LiveQuery/ParseWebSocketServer');
1+
const {
2+
ParseWebSocketServer,
3+
} = require('../lib/LiveQuery/ParseWebSocketServer');
24

35
describe('ParseWebSocketServer', function() {
46
beforeEach(function(done) {

spec/Schema.spec.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,9 @@ describe('SchemaController', () => {
451451
)
452452
)
453453
.then(actualSchema => {
454-
expect(dd(actualSchema.classLevelPermissions, newLevelPermissions)).toEqual(undefined);
454+
expect(
455+
dd(actualSchema.classLevelPermissions, newLevelPermissions)
456+
).toEqual(undefined);
455457
done();
456458
})
457459
.catch(error => {

spec/schemas.spec.js

+14-6
Original file line numberDiff line numberDiff line change
@@ -425,8 +425,12 @@ describe('schemas', () => {
425425
foo4: { type: 'Date', required: true },
426426
foo5: { type: 'Number', defaultValue: 5 },
427427
ptr: { type: 'Pointer', targetClass: 'SomeClass', required: false },
428-
defaultFalse: { type: 'Boolean', required: true, defaultValue: false },
429-
defaultZero: { type: 'Number', defaultValue: 0 }
428+
defaultFalse: {
429+
type: 'Boolean',
430+
required: true,
431+
defaultValue: false,
432+
},
433+
defaultZero: { type: 'Number', defaultValue: 0 },
430434
},
431435
},
432436
}).then(async response => {
@@ -447,8 +451,12 @@ describe('schemas', () => {
447451
foo4: { type: 'Date', required: true },
448452
foo5: { type: 'Number', defaultValue: 5 },
449453
ptr: { type: 'Pointer', targetClass: 'SomeClass', required: false },
450-
defaultFalse: { type: 'Boolean', required: true, defaultValue: false },
451-
defaultZero: { type: 'Number', defaultValue: 0 }
454+
defaultFalse: {
455+
type: 'Boolean',
456+
required: true,
457+
defaultValue: false,
458+
},
459+
defaultZero: { type: 'Number', defaultValue: 0 },
452460
},
453461
classLevelPermissions: defaultClassLevelPermissions,
454462
});
@@ -468,8 +476,8 @@ describe('schemas', () => {
468476
expect(obj.get('foo4')).toEqual(date);
469477
expect(obj.get('foo5')).toEqual(5);
470478
expect(obj.get('ptr')).toBeUndefined();
471-
expect(obj.get('defaultFalse')).toEqual(false)
472-
expect(obj.get('defaultZero')).toEqual(0)
479+
expect(obj.get('defaultFalse')).toEqual(false);
480+
expect(obj.get('defaultZero')).toEqual(0);
473481
expect(obj.get('ptr')).toBeUndefined();
474482
done();
475483
});

0 commit comments

Comments
 (0)