2019-06-19 17:19:47 -07:00
|
|
|
import {
|
|
|
|
|
GraphQLNonNull,
|
|
|
|
|
GraphQLBoolean,
|
|
|
|
|
GraphQLString,
|
|
|
|
|
GraphQLObjectType,
|
|
|
|
|
} from 'graphql';
|
|
|
|
|
import getFieldNames from 'graphql-list-fields';
|
|
|
|
|
import Parse from 'parse/node';
|
|
|
|
|
import * as defaultGraphQLTypes from './defaultGraphQLTypes';
|
|
|
|
|
import rest from '../../rest';
|
|
|
|
|
|
|
|
|
|
const getObject = async (
|
|
|
|
|
className,
|
|
|
|
|
objectId,
|
|
|
|
|
keys,
|
|
|
|
|
include,
|
|
|
|
|
readPreference,
|
|
|
|
|
includeReadPreference,
|
|
|
|
|
config,
|
|
|
|
|
auth,
|
|
|
|
|
info
|
|
|
|
|
) => {
|
|
|
|
|
const options = {};
|
|
|
|
|
if (keys) {
|
|
|
|
|
options.keys = keys;
|
|
|
|
|
}
|
|
|
|
|
if (include) {
|
|
|
|
|
options.include = include;
|
|
|
|
|
if (includeReadPreference) {
|
|
|
|
|
options.includeReadPreference = includeReadPreference;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (readPreference) {
|
|
|
|
|
options.readPreference = readPreference;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const response = await rest.get(
|
|
|
|
|
config,
|
|
|
|
|
auth,
|
|
|
|
|
className,
|
|
|
|
|
objectId,
|
|
|
|
|
options,
|
|
|
|
|
info.clientSDK
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (!response.results || response.results.length == 0) {
|
|
|
|
|
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Object not found.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (className === '_User') {
|
|
|
|
|
delete response.results[0].sessionToken;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return response.results[0];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const parseMap = {
|
|
|
|
|
_or: '$or',
|
|
|
|
|
_and: '$and',
|
|
|
|
|
_nor: '$nor',
|
|
|
|
|
_relatedTo: '$relatedTo',
|
|
|
|
|
_eq: '$eq',
|
|
|
|
|
_ne: '$ne',
|
|
|
|
|
_lt: '$lt',
|
|
|
|
|
_lte: '$lte',
|
|
|
|
|
_gt: '$gt',
|
|
|
|
|
_gte: '$gte',
|
|
|
|
|
_in: '$in',
|
|
|
|
|
_nin: '$nin',
|
|
|
|
|
_exists: '$exists',
|
|
|
|
|
_select: '$select',
|
|
|
|
|
_dontSelect: '$dontSelect',
|
|
|
|
|
_inQuery: '$inQuery',
|
|
|
|
|
_notInQuery: '$notInQuery',
|
|
|
|
|
_containedBy: '$containedBy',
|
|
|
|
|
_all: '$all',
|
|
|
|
|
_regex: '$regex',
|
|
|
|
|
_options: '$options',
|
|
|
|
|
_text: '$text',
|
|
|
|
|
_search: '$search',
|
|
|
|
|
_term: '$term',
|
|
|
|
|
_language: '$language',
|
|
|
|
|
_caseSensitive: '$caseSensitive',
|
|
|
|
|
_diacriticSensitive: '$diacriticSensitive',
|
|
|
|
|
_nearSphere: '$nearSphere',
|
|
|
|
|
_maxDistance: '$maxDistance',
|
|
|
|
|
_maxDistanceInRadians: '$maxDistanceInRadians',
|
|
|
|
|
_maxDistanceInMiles: '$maxDistanceInMiles',
|
|
|
|
|
_maxDistanceInKilometers: '$maxDistanceInKilometers',
|
|
|
|
|
_within: '$within',
|
|
|
|
|
_box: '$box',
|
|
|
|
|
_geoWithin: '$geoWithin',
|
|
|
|
|
_polygon: '$polygon',
|
|
|
|
|
_centerSphere: '$centerSphere',
|
|
|
|
|
_geoIntersects: '$geoIntersects',
|
|
|
|
|
_point: '$point',
|
|
|
|
|
};
|
|
|
|
|
|
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
2019-08-02 16:18:08 -03:00
|
|
|
const transformToParse = (constraints, parentFieldName, parentConstraints) => {
|
2019-06-19 17:19:47 -07:00
|
|
|
if (!constraints || typeof constraints !== 'object') {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
Object.keys(constraints).forEach(fieldName => {
|
|
|
|
|
let fieldValue = constraints[fieldName];
|
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
2019-08-02 16:18:08 -03:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* If we have a key-value pair, we need to change the way the constraint is structured.
|
|
|
|
|
*
|
|
|
|
|
* Example:
|
|
|
|
|
* From:
|
|
|
|
|
* {
|
|
|
|
|
* "someField": {
|
|
|
|
|
* "_lt": {
|
|
|
|
|
* "_key":"foo.bar",
|
|
|
|
|
* "_value": 100
|
|
|
|
|
* },
|
|
|
|
|
* "_gt": {
|
|
|
|
|
* "_key":"foo.bar",
|
|
|
|
|
* "_value": 10
|
|
|
|
|
* }
|
|
|
|
|
* }
|
|
|
|
|
* }
|
|
|
|
|
*
|
|
|
|
|
* To:
|
|
|
|
|
* {
|
|
|
|
|
* "someField.foo.bar": {
|
|
|
|
|
* "$lt": 100,
|
|
|
|
|
* "$gt": 10
|
|
|
|
|
* }
|
|
|
|
|
* }
|
|
|
|
|
*/
|
|
|
|
|
if (
|
|
|
|
|
fieldValue._key &&
|
|
|
|
|
fieldValue._value &&
|
|
|
|
|
parentConstraints &&
|
|
|
|
|
parentFieldName
|
|
|
|
|
) {
|
|
|
|
|
delete parentConstraints[parentFieldName];
|
|
|
|
|
parentConstraints[`${parentFieldName}.${fieldValue._key}`] = {
|
|
|
|
|
...parentConstraints[`${parentFieldName}.${fieldValue._key}`],
|
|
|
|
|
[parseMap[fieldName]]: fieldValue._value,
|
|
|
|
|
};
|
|
|
|
|
} else if (parseMap[fieldName]) {
|
2019-06-19 17:19:47 -07:00
|
|
|
delete constraints[fieldName];
|
|
|
|
|
fieldName = parseMap[fieldName];
|
|
|
|
|
constraints[fieldName] = fieldValue;
|
|
|
|
|
}
|
|
|
|
|
switch (fieldName) {
|
|
|
|
|
case '$point':
|
|
|
|
|
case '$nearSphere':
|
|
|
|
|
if (typeof fieldValue === 'object' && !fieldValue.__type) {
|
|
|
|
|
fieldValue.__type = 'GeoPoint';
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case '$box':
|
|
|
|
|
if (
|
|
|
|
|
typeof fieldValue === 'object' &&
|
|
|
|
|
fieldValue.bottomLeft &&
|
|
|
|
|
fieldValue.upperRight
|
|
|
|
|
) {
|
|
|
|
|
fieldValue = [
|
|
|
|
|
{
|
|
|
|
|
__type: 'GeoPoint',
|
|
|
|
|
...fieldValue.bottomLeft,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
__type: 'GeoPoint',
|
|
|
|
|
...fieldValue.upperRight,
|
|
|
|
|
},
|
|
|
|
|
];
|
|
|
|
|
constraints[fieldName] = fieldValue;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case '$polygon':
|
|
|
|
|
if (fieldValue instanceof Array) {
|
|
|
|
|
fieldValue.forEach(geoPoint => {
|
|
|
|
|
if (typeof geoPoint === 'object' && !geoPoint.__type) {
|
|
|
|
|
geoPoint.__type = 'GeoPoint';
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case '$centerSphere':
|
|
|
|
|
if (
|
|
|
|
|
typeof fieldValue === 'object' &&
|
|
|
|
|
fieldValue.center &&
|
|
|
|
|
fieldValue.distance
|
|
|
|
|
) {
|
|
|
|
|
fieldValue = [
|
|
|
|
|
{
|
|
|
|
|
__type: 'GeoPoint',
|
|
|
|
|
...fieldValue.center,
|
|
|
|
|
},
|
|
|
|
|
fieldValue.distance,
|
|
|
|
|
];
|
|
|
|
|
constraints[fieldName] = fieldValue;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (typeof fieldValue === 'object') {
|
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
2019-08-02 16:18:08 -03:00
|
|
|
transformToParse(fieldValue, fieldName, constraints);
|
2019-06-19 17:19:47 -07:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const findObjects = async (
|
|
|
|
|
className,
|
|
|
|
|
where,
|
|
|
|
|
order,
|
|
|
|
|
skip,
|
|
|
|
|
limit,
|
|
|
|
|
keys,
|
|
|
|
|
include,
|
|
|
|
|
includeAll,
|
|
|
|
|
readPreference,
|
|
|
|
|
includeReadPreference,
|
|
|
|
|
subqueryReadPreference,
|
|
|
|
|
config,
|
|
|
|
|
auth,
|
|
|
|
|
info,
|
|
|
|
|
selectedFields
|
|
|
|
|
) => {
|
|
|
|
|
if (!where) {
|
|
|
|
|
where = {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
transformToParse(where);
|
|
|
|
|
|
|
|
|
|
const options = {};
|
|
|
|
|
|
|
|
|
|
if (selectedFields.includes('results')) {
|
|
|
|
|
if (limit || limit === 0) {
|
|
|
|
|
options.limit = limit;
|
|
|
|
|
}
|
|
|
|
|
if (options.limit !== 0) {
|
|
|
|
|
if (order) {
|
|
|
|
|
options.order = order;
|
|
|
|
|
}
|
|
|
|
|
if (skip) {
|
|
|
|
|
options.skip = skip;
|
|
|
|
|
}
|
|
|
|
|
if (config.maxLimit && options.limit > config.maxLimit) {
|
|
|
|
|
// Silently replace the limit on the query with the max configured
|
|
|
|
|
options.limit = config.maxLimit;
|
|
|
|
|
}
|
|
|
|
|
if (keys) {
|
|
|
|
|
options.keys = keys;
|
|
|
|
|
}
|
|
|
|
|
if (includeAll === true) {
|
|
|
|
|
options.includeAll = includeAll;
|
|
|
|
|
}
|
|
|
|
|
if (!options.includeAll && include) {
|
|
|
|
|
options.include = include;
|
|
|
|
|
}
|
|
|
|
|
if ((options.includeAll || options.include) && includeReadPreference) {
|
|
|
|
|
options.includeReadPreference = includeReadPreference;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
options.limit = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (selectedFields.includes('count')) {
|
|
|
|
|
options.count = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (readPreference) {
|
|
|
|
|
options.readPreference = readPreference;
|
|
|
|
|
}
|
|
|
|
|
if (Object.keys(where).length > 0 && subqueryReadPreference) {
|
|
|
|
|
options.subqueryReadPreference = subqueryReadPreference;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return await rest.find(
|
|
|
|
|
config,
|
|
|
|
|
auth,
|
|
|
|
|
className,
|
|
|
|
|
where,
|
|
|
|
|
options,
|
|
|
|
|
info.clientSDK
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const load = parseGraphQLSchema => {
|
|
|
|
|
parseGraphQLSchema.graphQLObjectsQueries.get = {
|
|
|
|
|
description:
|
|
|
|
|
'The get query can be used to get an object of a certain class by its objectId.',
|
|
|
|
|
args: {
|
|
|
|
|
className: defaultGraphQLTypes.CLASS_NAME_ATT,
|
|
|
|
|
objectId: defaultGraphQLTypes.OBJECT_ID_ATT,
|
|
|
|
|
keys: defaultGraphQLTypes.KEYS_ATT,
|
|
|
|
|
include: defaultGraphQLTypes.INCLUDE_ATT,
|
|
|
|
|
readPreference: defaultGraphQLTypes.READ_PREFERENCE_ATT,
|
|
|
|
|
includeReadPreference: defaultGraphQLTypes.INCLUDE_READ_PREFERENCE_ATT,
|
|
|
|
|
},
|
|
|
|
|
type: new GraphQLNonNull(defaultGraphQLTypes.OBJECT),
|
|
|
|
|
async resolve(_source, args, context) {
|
|
|
|
|
try {
|
|
|
|
|
const {
|
|
|
|
|
className,
|
|
|
|
|
objectId,
|
|
|
|
|
keys,
|
|
|
|
|
include,
|
|
|
|
|
readPreference,
|
|
|
|
|
includeReadPreference,
|
|
|
|
|
} = args;
|
|
|
|
|
const { config, auth, info } = context;
|
|
|
|
|
|
|
|
|
|
return await getObject(
|
|
|
|
|
className,
|
|
|
|
|
objectId,
|
|
|
|
|
keys,
|
|
|
|
|
include,
|
|
|
|
|
readPreference,
|
|
|
|
|
includeReadPreference,
|
|
|
|
|
config,
|
|
|
|
|
auth,
|
|
|
|
|
info
|
|
|
|
|
);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
parseGraphQLSchema.handleError(e);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
parseGraphQLSchema.graphQLObjectsQueries.find = {
|
|
|
|
|
description:
|
|
|
|
|
'The find query can be used to find objects of a certain class.',
|
|
|
|
|
args: {
|
|
|
|
|
className: defaultGraphQLTypes.CLASS_NAME_ATT,
|
|
|
|
|
where: defaultGraphQLTypes.WHERE_ATT,
|
|
|
|
|
order: {
|
|
|
|
|
description:
|
|
|
|
|
'This is the order in which the objects should be returned',
|
|
|
|
|
type: GraphQLString,
|
|
|
|
|
},
|
|
|
|
|
skip: defaultGraphQLTypes.SKIP_ATT,
|
|
|
|
|
limit: defaultGraphQLTypes.LIMIT_ATT,
|
|
|
|
|
keys: defaultGraphQLTypes.KEYS_ATT,
|
|
|
|
|
include: defaultGraphQLTypes.INCLUDE_ATT,
|
|
|
|
|
includeAll: {
|
|
|
|
|
description: 'All pointers will be returned',
|
|
|
|
|
type: GraphQLBoolean,
|
|
|
|
|
},
|
|
|
|
|
readPreference: defaultGraphQLTypes.READ_PREFERENCE_ATT,
|
|
|
|
|
includeReadPreference: defaultGraphQLTypes.INCLUDE_READ_PREFERENCE_ATT,
|
|
|
|
|
subqueryReadPreference: defaultGraphQLTypes.SUBQUERY_READ_PREFERENCE_ATT,
|
|
|
|
|
},
|
|
|
|
|
type: new GraphQLNonNull(defaultGraphQLTypes.FIND_RESULT),
|
|
|
|
|
async resolve(_source, args, context, queryInfo) {
|
|
|
|
|
try {
|
|
|
|
|
const {
|
|
|
|
|
className,
|
|
|
|
|
where,
|
|
|
|
|
order,
|
|
|
|
|
skip,
|
|
|
|
|
limit,
|
|
|
|
|
keys,
|
|
|
|
|
include,
|
|
|
|
|
includeAll,
|
|
|
|
|
readPreference,
|
|
|
|
|
includeReadPreference,
|
|
|
|
|
subqueryReadPreference,
|
|
|
|
|
} = args;
|
|
|
|
|
const { config, auth, info } = context;
|
|
|
|
|
const selectedFields = getFieldNames(queryInfo);
|
|
|
|
|
|
|
|
|
|
return await findObjects(
|
|
|
|
|
className,
|
|
|
|
|
where,
|
|
|
|
|
order,
|
|
|
|
|
skip,
|
|
|
|
|
limit,
|
|
|
|
|
keys,
|
|
|
|
|
include,
|
|
|
|
|
includeAll,
|
|
|
|
|
readPreference,
|
|
|
|
|
includeReadPreference,
|
|
|
|
|
subqueryReadPreference,
|
|
|
|
|
config,
|
|
|
|
|
auth,
|
|
|
|
|
info,
|
|
|
|
|
selectedFields
|
|
|
|
|
);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
parseGraphQLSchema.handleError(e);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const objectsQuery = new GraphQLObjectType({
|
|
|
|
|
name: 'ObjectsQuery',
|
|
|
|
|
description: 'ObjectsQuery is the top level type for objects queries.',
|
|
|
|
|
fields: parseGraphQLSchema.graphQLObjectsQueries,
|
|
|
|
|
});
|
|
|
|
|
parseGraphQLSchema.graphQLTypes.push(objectsQuery);
|
|
|
|
|
|
|
|
|
|
parseGraphQLSchema.graphQLQueries.objects = {
|
|
|
|
|
description: 'This is the top level for objects queries.',
|
|
|
|
|
type: objectsQuery,
|
|
|
|
|
resolve: () => new Object(),
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export { getObject, findObjects, load };
|