GraphQL: Renaming Types/Inputs (#5883)

* Renaming GraphQL Types/Inputs

* Add Native Type to avoid collision

* Use pluralize for renaming

* Fixing tests

* Improve name collision management - tests passsing

* Renaming few more default types

* Rename file input

* Reverting fields types to not collide with the relay spec types
Improver users mutations

* Adding ArrayResult to the reserved list

* Fixing tests

* Add more unit tests to ParseGraphQLSchema

* Test transformClassNameToGraphQL

* Name collision tests
This commit is contained in:
Antoine Cormouls
2019-08-15 23:23:41 +02:00
committed by Antonio Davi Macedo Coelho de Castro
parent cf6e79ee75
commit 59b0221fec
20 changed files with 1505 additions and 864 deletions

27
package-lock.json generated
View File

@@ -1214,11 +1214,21 @@
"integrity": "sha512-J+YW09/vZRuK2/04SykW31xgMTtA/XTm4mSaLoJ79EW31SWstNUzfti9seu0MdWRafgdmRzUn+qCJ/MqXvQZRg==",
"requires": {
"@parse/node-gcm": "^1.0.0",
"apn": "github:parse-community/node-apn#semver:^v3.0.2-parse",
"apn": "github:parse-community/node-apn#3bc4fb20b68d53d3f3b7057cadf5a37ba6a45dc0",
"npmlog": "^4.0.2",
"parse": "^1.11.1"
},
"dependencies": {
"apn": {
"version": "github:parse-community/node-apn#3bc4fb20b68d53d3f3b7057cadf5a37ba6a45dc0",
"from": "github:parse-community/node-apn#3bc4fb20b68d53d3f3b7057cadf5a37ba6a45dc0",
"requires": {
"debug": "^3.1.0",
"jsonwebtoken": "^8.1.0",
"node-forge": "^0.7.1",
"verror": "^1.10.0"
}
},
"parse": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/parse/-/parse-1.11.1.tgz",
@@ -1668,16 +1678,6 @@
}
}
},
"apn": {
"version": "github:parse-community/node-apn#3bc4fb20b68d53d3f3b7057cadf5a37ba6a45dc0",
"from": "github:parse-community/node-apn#semver:^v3.0.2-parse",
"requires": {
"debug": "^3.1.0",
"jsonwebtoken": "^8.1.0",
"node-forge": "^0.7.1",
"verror": "^1.10.0"
}
},
"apollo-cache": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/apollo-cache/-/apollo-cache-1.3.2.tgz",
@@ -9075,6 +9075,11 @@
"semver-compare": "^1.0.0"
}
},
"pluralize": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz",
"integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA=="
},
"posix-character-classes": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",

View File

@@ -45,6 +45,7 @@
"node-rsa": "1.0.5",
"parse": "2.6.0",
"pg-promise": "9.0.1",
"pluralize": "^8.0.0",
"redis": "2.8.0",
"semver": "6.3.0",
"subscriptions-transport-ws": "0.9.16",

View File

@@ -0,0 +1,11 @@
const {
transformClassNameToGraphQL,
} = require('../lib/GraphQL/transformers/className');
describe('transformClassNameToGraphQL', () => {
it('should remove starting _ and tansform first letter to upper case', () => {
expect(
['_User', '_user', 'User', 'user'].map(transformClassNameToGraphQL)
).toEqual(['User', 'User', 'User', 'User']);
});
});

View File

@@ -1,3 +1,4 @@
const { GraphQLObjectType } = require('graphql');
const defaultLogger = require('../lib/logger').default;
const { ParseGraphQLSchema } = require('../lib/GraphQL/ParseGraphQLSchema');
@@ -118,4 +119,428 @@ describe('ParseGraphQLSchema', () => {
);
});
});
describe('addGraphQLType', () => {
it('should not load and warn duplicated types', async () => {
let logged = false;
const parseGraphQLSchema = new ParseGraphQLSchema({
databaseController,
parseGraphQLController,
log: {
warn: message => {
logged = true;
expect(message).toEqual(
'Type SomeClass could not be added to the auto schema because it collided with an existing type.'
);
},
},
});
await parseGraphQLSchema.load();
const type = new GraphQLObjectType({ name: 'SomeClass' });
expect(parseGraphQLSchema.addGraphQLType(type)).toBe(type);
expect(parseGraphQLSchema.graphQLTypes).toContain(type);
expect(
parseGraphQLSchema.addGraphQLType(
new GraphQLObjectType({ name: 'SomeClass' })
)
).toBeUndefined();
expect(logged).toBeTruthy();
});
it('should throw error when required', async () => {
const parseGraphQLSchema = new ParseGraphQLSchema({
databaseController,
parseGraphQLController,
log: {
warn: () => {
fail('Should not warn');
},
},
});
await parseGraphQLSchema.load();
const type = new GraphQLObjectType({ name: 'SomeClass' });
expect(parseGraphQLSchema.addGraphQLType(type, true)).toBe(type);
expect(parseGraphQLSchema.graphQLTypes).toContain(type);
expect(() =>
parseGraphQLSchema.addGraphQLType(
new GraphQLObjectType({ name: 'SomeClass' }),
true
)
).toThrowError(
'Type SomeClass could not be added to the auto schema because it collided with an existing type.'
);
});
it('should warn reserved name collision', async () => {
let logged = false;
const parseGraphQLSchema = new ParseGraphQLSchema({
databaseController,
parseGraphQLController,
log: {
warn: message => {
logged = true;
expect(message).toEqual(
'Type String could not be added to the auto schema because it collided with an existing type.'
);
},
},
});
await parseGraphQLSchema.load();
expect(
parseGraphQLSchema.addGraphQLType(
new GraphQLObjectType({ name: 'String' })
)
).toBeUndefined();
expect(logged).toBeTruthy();
});
it('should ignore collision when necessary', async () => {
const parseGraphQLSchema = new ParseGraphQLSchema({
databaseController,
parseGraphQLController,
log: {
warn: () => {
fail('Should not warn');
},
},
});
await parseGraphQLSchema.load();
const type = new GraphQLObjectType({ name: 'String' });
expect(parseGraphQLSchema.addGraphQLType(type, true, true)).toBe(type);
expect(parseGraphQLSchema.graphQLTypes).toContain(type);
});
});
describe('addGraphQLObjectQuery', () => {
it('should not load and warn duplicated queries', async () => {
let logged = false;
const parseGraphQLSchema = new ParseGraphQLSchema({
databaseController,
parseGraphQLController,
log: {
warn: message => {
logged = true;
expect(message).toEqual(
'Object query someClasses could not be added to the auto schema because it collided with an existing field.'
);
},
},
});
await parseGraphQLSchema.load();
const field = {};
expect(
parseGraphQLSchema.addGraphQLObjectQuery('someClasses', field)
).toBe(field);
expect(parseGraphQLSchema.graphQLObjectsQueries['someClasses']).toBe(
field
);
expect(
parseGraphQLSchema.addGraphQLObjectQuery('someClasses', {})
).toBeUndefined();
expect(logged).toBeTruthy();
});
it('should throw error when required', async () => {
const parseGraphQLSchema = new ParseGraphQLSchema({
databaseController,
parseGraphQLController,
log: {
warn: () => {
fail('Should not warn');
},
},
});
await parseGraphQLSchema.load();
const field = {};
expect(
parseGraphQLSchema.addGraphQLObjectQuery('someClasses', field)
).toBe(field);
expect(parseGraphQLSchema.graphQLObjectsQueries['someClasses']).toBe(
field
);
expect(() =>
parseGraphQLSchema.addGraphQLObjectQuery('someClasses', {}, true)
).toThrowError(
'Object query someClasses could not be added to the auto schema because it collided with an existing field.'
);
});
it('should warn reserved name collision', async () => {
let logged = false;
const parseGraphQLSchema = new ParseGraphQLSchema({
databaseController,
parseGraphQLController,
log: {
warn: message => {
logged = true;
expect(message).toEqual(
'Object query get could not be added to the auto schema because it collided with an existing field.'
);
},
},
});
await parseGraphQLSchema.load();
expect(
parseGraphQLSchema.addGraphQLObjectQuery('get', {})
).toBeUndefined();
expect(logged).toBeTruthy();
});
it('should ignore collision when necessary', async () => {
const parseGraphQLSchema = new ParseGraphQLSchema({
databaseController,
parseGraphQLController,
log: {
warn: () => {
fail('Should not warn');
},
},
});
await parseGraphQLSchema.load();
delete parseGraphQLSchema.graphQLObjectsQueries.get;
const field = {};
expect(
parseGraphQLSchema.addGraphQLObjectQuery('get', field, true, true)
).toBe(field);
expect(parseGraphQLSchema.graphQLObjectsQueries['get']).toBe(field);
});
});
describe('addGraphQLObjectMutation', () => {
it('should not load and warn duplicated mutations', async () => {
let logged = false;
const parseGraphQLSchema = new ParseGraphQLSchema({
databaseController,
parseGraphQLController,
log: {
warn: message => {
logged = true;
expect(message).toEqual(
'Object mutation createSomeClass could not be added to the auto schema because it collided with an existing field.'
);
},
},
});
await parseGraphQLSchema.load();
const field = {};
expect(
parseGraphQLSchema.addGraphQLObjectMutation('createSomeClass', field)
).toBe(field);
expect(
parseGraphQLSchema.graphQLObjectsMutations['createSomeClass']
).toBe(field);
expect(
parseGraphQLSchema.addGraphQLObjectMutation('createSomeClass', {})
).toBeUndefined();
expect(logged).toBeTruthy();
});
it('should throw error when required', async () => {
const parseGraphQLSchema = new ParseGraphQLSchema({
databaseController,
parseGraphQLController,
log: {
warn: () => {
fail('Should not warn');
},
},
});
await parseGraphQLSchema.load();
const field = {};
expect(
parseGraphQLSchema.addGraphQLObjectMutation('createSomeClass', field)
).toBe(field);
expect(
parseGraphQLSchema.graphQLObjectsMutations['createSomeClass']
).toBe(field);
expect(() =>
parseGraphQLSchema.addGraphQLObjectMutation('createSomeClass', {}, true)
).toThrowError(
'Object mutation createSomeClass could not be added to the auto schema because it collided with an existing field.'
);
});
it('should warn reserved name collision', async () => {
let logged = false;
const parseGraphQLSchema = new ParseGraphQLSchema({
databaseController,
parseGraphQLController,
log: {
warn: message => {
logged = true;
expect(message).toEqual(
'Object mutation create could not be added to the auto schema because it collided with an existing field.'
);
},
},
});
await parseGraphQLSchema.load();
expect(
parseGraphQLSchema.addGraphQLObjectMutation('create', {})
).toBeUndefined();
expect(logged).toBeTruthy();
});
it('should ignore collision when necessary', async () => {
const parseGraphQLSchema = new ParseGraphQLSchema({
databaseController,
parseGraphQLController,
log: {
warn: () => {
fail('Should not warn');
},
},
});
await parseGraphQLSchema.load();
delete parseGraphQLSchema.graphQLObjectsMutations.create;
const field = {};
expect(
parseGraphQLSchema.addGraphQLObjectMutation('create', field, true, true)
).toBe(field);
expect(parseGraphQLSchema.graphQLObjectsMutations['create']).toBe(field);
});
});
describe('_getParseClassesWithConfig', () => {
it('should sort classes', () => {
const parseGraphQLSchema = new ParseGraphQLSchema({
databaseController,
parseGraphQLController,
log: {
warn: () => {
fail('Should not warn');
},
},
});
expect(
parseGraphQLSchema
._getParseClassesWithConfig(
[
{ className: 'b' },
{ className: '_b' },
{ className: 'B' },
{ className: '_B' },
{ className: 'a' },
{ className: '_a' },
{ className: 'A' },
{ className: '_A' },
],
{
classConfigs: [],
}
)
.map(item => item[0])
).toEqual([
{ className: '_A' },
{ className: '_B' },
{ className: '_a' },
{ className: '_b' },
{ className: 'A' },
{ className: 'B' },
{ className: 'a' },
{ className: 'b' },
]);
});
});
describe('name collision', () => {
it('should not generate duplicate types when colliding to default classes', async () => {
const parseGraphQLSchema = new ParseGraphQLSchema({
databaseController,
parseGraphQLController,
log: defaultLogger,
});
await parseGraphQLSchema.databaseController.schemaCache.clear();
const schema1 = await parseGraphQLSchema.load();
const types1 = parseGraphQLSchema.graphQLTypes;
const objectQueries1 = parseGraphQLSchema.graphQLObjectsQueries;
const objectMutations1 = parseGraphQLSchema.graphQLObjectsMutations;
const user = new Parse.Object('User');
await user.save();
await parseGraphQLSchema.databaseController.schemaCache.clear();
const schema2 = await parseGraphQLSchema.load();
const types2 = parseGraphQLSchema.graphQLTypes;
const objectQueries2 = parseGraphQLSchema.graphQLObjectsQueries;
const objectMutations2 = parseGraphQLSchema.graphQLObjectsMutations;
expect(schema1).not.toBe(schema2);
expect(types1).not.toBe(types2);
expect(types1.map(type => type.name).sort()).toEqual(
types2.map(type => type.name).sort()
);
expect(objectQueries1).not.toBe(objectQueries2);
expect(Object.keys(objectQueries1).sort()).toEqual(
Object.keys(objectQueries2).sort()
);
expect(objectMutations1).not.toBe(objectMutations2);
expect(Object.keys(objectMutations1).sort()).toEqual(
Object.keys(objectMutations2).sort()
);
});
it('should not generate duplicate types when colliding the same name', async () => {
const parseGraphQLSchema = new ParseGraphQLSchema({
databaseController,
parseGraphQLController,
log: defaultLogger,
});
const car1 = new Parse.Object('Car');
await car1.save();
await parseGraphQLSchema.databaseController.schemaCache.clear();
const schema1 = await parseGraphQLSchema.load();
const types1 = parseGraphQLSchema.graphQLTypes;
const objectQueries1 = parseGraphQLSchema.graphQLObjectsQueries;
const objectMutations1 = parseGraphQLSchema.graphQLObjectsMutations;
const car2 = new Parse.Object('car');
await car2.save();
await parseGraphQLSchema.databaseController.schemaCache.clear();
const schema2 = await parseGraphQLSchema.load();
const types2 = parseGraphQLSchema.graphQLTypes;
const objectQueries2 = parseGraphQLSchema.graphQLObjectsQueries;
const objectMutations2 = parseGraphQLSchema.graphQLObjectsMutations;
expect(schema1).not.toBe(schema2);
expect(types1).not.toBe(types2);
expect(types1.map(type => type.name).sort()).toEqual(
types2.map(type => type.name).sort()
);
expect(objectQueries1).not.toBe(objectQueries2);
expect(Object.keys(objectQueries1).sort()).toEqual(
Object.keys(objectQueries2).sort()
);
expect(objectMutations1).not.toBe(objectMutations2);
expect(Object.keys(objectMutations1).sort()).toEqual(
Object.keys(objectMutations2).sort()
);
});
it('should not generate duplicate queries when query name collide', async () => {
const parseGraphQLSchema = new ParseGraphQLSchema({
databaseController,
parseGraphQLController,
log: defaultLogger,
});
const car = new Parse.Object('Car');
await car.save();
await parseGraphQLSchema.databaseController.schemaCache.clear();
const schema1 = await parseGraphQLSchema.load();
const objectQueries1 = parseGraphQLSchema.graphQLObjectsQueries;
const objectMutations1 = parseGraphQLSchema.graphQLObjectsMutations;
const cars = new Parse.Object('cars');
await cars.save();
await parseGraphQLSchema.databaseController.schemaCache.clear();
const schema2 = await parseGraphQLSchema.load();
const objectQueries2 = parseGraphQLSchema.graphQLObjectsQueries;
const objectMutations2 = parseGraphQLSchema.graphQLObjectsMutations;
expect(schema1).not.toBe(schema2);
expect(objectQueries1).not.toBe(objectQueries2);
expect(Object.keys(objectQueries1).sort()).toEqual(
Object.keys(objectQueries2).sort()
);
expect(objectMutations1).not.toBe(objectMutations2);
expect(
Object.keys(objectMutations1)
.concat('createCars', 'updateCars', 'deleteCars')
.sort()
).toEqual(Object.keys(objectMutations2).sort());
});
});
});

File diff suppressed because it is too large Load Diff

View File

@@ -15,6 +15,29 @@ import DatabaseController from '../Controllers/DatabaseController';
import { toGraphQLError } from './parseGraphQLUtils';
import * as schemaDirectives from './loaders/schemaDirectives';
const RESERVED_GRAPHQL_TYPE_NAMES = [
'String',
'Boolean',
'Int',
'Float',
'ID',
'ArrayResult',
'Query',
'Mutation',
'Subscription',
'ObjectsQuery',
'UsersQuery',
'ObjectsMutation',
'FilesMutation',
'UsersMutation',
'FunctionsMutation',
'Viewer',
'SignUpFieldsInput',
'LogInFieldsInput',
];
const RESERVED_GRAPHQL_OBJECT_QUERY_NAMES = ['get', 'find'];
const RESERVED_GRAPHQL_OBJECT_MUTATION_NAMES = ['create', 'update', 'delete'];
class ParseGraphQLSchema {
databaseController: DatabaseController;
parseGraphQLController: ParseGraphQLController;
@@ -60,7 +83,7 @@ class ParseGraphQLSchema {
this.parseClassesString = parseClassesString;
this.parseGraphQLConfig = parseGraphQLConfig;
this.parseClassTypes = {};
this.meType = null;
this.viewerType = null;
this.graphQLAutoSchema = null;
this.graphQLSchema = null;
this.graphQLTypes = [];
@@ -92,7 +115,7 @@ class ParseGraphQLSchema {
description: 'Query is the top level type for queries.',
fields: this.graphQLQueries,
});
this.graphQLTypes.push(graphQLQuery);
this.addGraphQLType(graphQLQuery, true, true);
}
let graphQLMutation = undefined;
@@ -102,7 +125,7 @@ class ParseGraphQLSchema {
description: 'Mutation is the top level type for mutations.',
fields: this.graphQLMutations,
});
this.graphQLTypes.push(graphQLMutation);
this.addGraphQLType(graphQLMutation, true, true);
}
let graphQLSubscription = undefined;
@@ -112,7 +135,7 @@ class ParseGraphQLSchema {
description: 'Subscription is the top level type for subscriptions.',
fields: this.graphQLSubscriptions,
});
this.graphQLTypes.push(graphQLSubscription);
this.addGraphQLType(graphQLSubscription, true, true);
}
this.graphQLAutoSchema = new GraphQLSchema({
@@ -172,6 +195,66 @@ class ParseGraphQLSchema {
return this.graphQLSchema;
}
addGraphQLType(type, throwError = false, ignoreReserved = false) {
if (
(!ignoreReserved && RESERVED_GRAPHQL_TYPE_NAMES.includes(type.name)) ||
this.graphQLTypes.find(existingType => existingType.name === type.name)
) {
const message = `Type ${type.name} could not be added to the auto schema because it collided with an existing type.`;
if (throwError) {
throw new Error(message);
}
this.log.warn(message);
return undefined;
}
this.graphQLTypes.push(type);
return type;
}
addGraphQLObjectQuery(
fieldName,
field,
throwError = false,
ignoreReserved = false
) {
if (
(!ignoreReserved &&
RESERVED_GRAPHQL_OBJECT_QUERY_NAMES.includes(fieldName)) ||
this.graphQLObjectsQueries[fieldName]
) {
const message = `Object query ${fieldName} could not be added to the auto schema because it collided with an existing field.`;
if (throwError) {
throw new Error(message);
}
this.log.warn(message);
return undefined;
}
this.graphQLObjectsQueries[fieldName] = field;
return field;
}
addGraphQLObjectMutation(
fieldName,
field,
throwError = false,
ignoreReserved = false
) {
if (
(!ignoreReserved &&
RESERVED_GRAPHQL_OBJECT_MUTATION_NAMES.includes(fieldName)) ||
this.graphQLObjectsMutations[fieldName]
) {
const message = `Object mutation ${fieldName} could not be added to the auto schema because it collided with an existing field.`;
if (throwError) {
throw new Error(message);
}
this.log.warn(message);
return undefined;
}
this.graphQLObjectsMutations[fieldName] = field;
return field;
}
handleError(error) {
if (error instanceof Parse.Error) {
this.log.error('Parse error: ', error);
@@ -238,7 +321,32 @@ class ParseGraphQLSchema {
parseGraphQLConfig: ParseGraphQLConfig
) {
const { classConfigs } = parseGraphQLConfig;
return parseClasses.map(parseClass => {
// Make sures that the default classes and classes that
// starts with capitalized letter will be generated first.
const sortClasses = (a, b) => {
a = a.className;
b = b.className;
if (a[0] === '_') {
if (b[0] !== '_') {
return -1;
}
}
if (b[0] === '_') {
if (a[0] !== '_') {
return 1;
}
}
if (a === b) {
return 0;
} else if (a < b) {
return -1;
} else {
return 1;
}
};
return parseClasses.sort(sortClasses).map(parseClass => {
let parseClassConfig;
if (classConfigs) {
parseClassConfig = classConfigs.find(

View File

@@ -25,25 +25,31 @@ class ParseGraphQLServer {
}
this.config = config;
this.parseGraphQLController = this.parseServer.config.parseGraphQLController;
this.log =
(this.parseServer.config && this.parseServer.config.loggerController) ||
defaultLogger;
this.parseGraphQLSchema = new ParseGraphQLSchema({
parseGraphQLController: this.parseGraphQLController,
databaseController: this.parseServer.config.databaseController,
log:
(this.parseServer.config && this.parseServer.config.loggerController) ||
defaultLogger,
log: this.log,
graphQLCustomTypeDefs: this.config.graphQLCustomTypeDefs,
});
}
async _getGraphQLOptions(req) {
return {
schema: await this.parseGraphQLSchema.load(),
context: {
info: req.info,
config: req.config,
auth: req.auth,
},
};
try {
return {
schema: await this.parseGraphQLSchema.load(),
context: {
info: req.info,
config: req.config,
auth: req.auth,
},
};
} catch (e) {
this.log.error(e);
throw e;
}
}
applyGraphQL(app) {

View File

@@ -377,24 +377,24 @@ const GEO_POINT_FIELDS = {
},
};
const GEO_POINT = new GraphQLInputObjectType({
const GEO_POINT_INPUT = new GraphQLInputObjectType({
name: 'GeoPointInput',
description:
'The GeoPointInput type is used in operations that involve inputting fields of type geo point.',
fields: GEO_POINT_FIELDS,
});
const GEO_POINT = new GraphQLObjectType({
name: 'GeoPoint',
description:
'The GeoPoint input type is used in operations that involve inputting fields of type geo point.',
'The GeoPoint object type is used to return the information about geo point fields.',
fields: GEO_POINT_FIELDS,
});
const GEO_POINT_INFO = new GraphQLObjectType({
name: 'GeoPointInfo',
description:
'The GeoPointInfo object type is used to return the information about geo points.',
fields: GEO_POINT_FIELDS,
});
const POLYGON_INPUT = new GraphQLList(new GraphQLNonNull(GEO_POINT_INPUT));
const POLYGON = new GraphQLList(new GraphQLNonNull(GEO_POINT));
const POLYGON_INFO = new GraphQLList(new GraphQLNonNull(GEO_POINT_INFO));
const RELATION_OP = new GraphQLEnumType({
name: 'RelationOp',
description:
@@ -542,10 +542,10 @@ const COUNT_ATT = {
type: new GraphQLNonNull(GraphQLInt),
};
const SUBQUERY = new GraphQLInputObjectType({
name: 'Subquery',
const SUBQUERY_INPUT = new GraphQLInputObjectType({
name: 'SubqueryInput',
description:
'The Subquery input type is used to specific a different query to a different class.',
'The SubqueryInput type is used to specific a different query to a different class.',
fields: {
className: CLASS_NAME_ATT,
where: Object.assign({}, WHERE_ATT, {
@@ -554,14 +554,14 @@ const SUBQUERY = new GraphQLInputObjectType({
},
});
const SELECT_OPERATOR = new GraphQLInputObjectType({
name: 'SelectOperator',
const SELECT_INPUT = new GraphQLInputObjectType({
name: 'SelectInput',
description:
'The SelectOperator input type is used to specify a $select operation on a constraint.',
'The SelectInput type is used to specify a $select operation on a constraint.',
fields: {
query: {
description: 'This is the subquery to be executed.',
type: new GraphQLNonNull(SUBQUERY),
type: new GraphQLNonNull(SUBQUERY_INPUT),
},
key: {
description:
@@ -571,10 +571,10 @@ const SELECT_OPERATOR = new GraphQLInputObjectType({
},
});
const SEARCH_OPERATOR = new GraphQLInputObjectType({
name: 'SearchOperator',
const SEARCH_INPUT = new GraphQLInputObjectType({
name: 'SearchInput',
description:
'The SearchOperator input type is used to specifiy a $search operation on a full text search.',
'The SearchInput type is used to specifiy a $search operation on a full text search.',
fields: {
_term: {
description: 'This is the term to be searched.',
@@ -598,54 +598,54 @@ const SEARCH_OPERATOR = new GraphQLInputObjectType({
},
});
const TEXT_OPERATOR = new GraphQLInputObjectType({
name: 'TextOperator',
const TEXT_INPUT = new GraphQLInputObjectType({
name: 'TextInput',
description:
'The TextOperator input type is used to specify a $text operation on a constraint.',
'The TextInput type is used to specify a $text operation on a constraint.',
fields: {
_search: {
description: 'This is the search to be executed.',
type: new GraphQLNonNull(SEARCH_OPERATOR),
type: new GraphQLNonNull(SEARCH_INPUT),
},
},
});
const BOX_OPERATOR = new GraphQLInputObjectType({
name: 'BoxOperator',
const BOX_INPUT = new GraphQLInputObjectType({
name: 'BoxInput',
description:
'The BoxOperator input type is used to specifiy a $box operation on a within geo query.',
'The BoxInput type is used to specifiy a $box operation on a within geo query.',
fields: {
bottomLeft: {
description: 'This is the bottom left coordinates of the box.',
type: new GraphQLNonNull(GEO_POINT),
type: new GraphQLNonNull(GEO_POINT_INPUT),
},
upperRight: {
description: 'This is the upper right coordinates of the box.',
type: new GraphQLNonNull(GEO_POINT),
type: new GraphQLNonNull(GEO_POINT_INPUT),
},
},
});
const WITHIN_OPERATOR = new GraphQLInputObjectType({
name: 'WithinOperator',
const WITHIN_INPUT = new GraphQLInputObjectType({
name: 'WithinInput',
description:
'The WithinOperator input type is used to specify a $within operation on a constraint.',
'The WithinInput type is used to specify a $within operation on a constraint.',
fields: {
_box: {
description: 'This is the box to be specified.',
type: new GraphQLNonNull(BOX_OPERATOR),
type: new GraphQLNonNull(BOX_INPUT),
},
},
});
const CENTER_SPHERE_OPERATOR = new GraphQLInputObjectType({
name: 'CenterSphereOperator',
const CENTER_SPHERE_INPUT = new GraphQLInputObjectType({
name: 'CenterSphereInput',
description:
'The CenterSphereOperator input type is used to specifiy a $centerSphere operation on a geoWithin query.',
'The CenterSphereInput type is used to specifiy a $centerSphere operation on a geoWithin query.',
fields: {
center: {
description: 'This is the center of the sphere.',
type: new GraphQLNonNull(GEO_POINT),
type: new GraphQLNonNull(GEO_POINT_INPUT),
},
distance: {
description: 'This is the radius of the sphere.',
@@ -654,30 +654,30 @@ const CENTER_SPHERE_OPERATOR = new GraphQLInputObjectType({
},
});
const GEO_WITHIN_OPERATOR = new GraphQLInputObjectType({
name: 'GeoWithinOperator',
const GEO_WITHIN_INPUT = new GraphQLInputObjectType({
name: 'GeoWithinInput',
description:
'The GeoWithinOperator input type is used to specify a $geoWithin operation on a constraint.',
'The GeoWithinInput type is used to specify a $geoWithin operation on a constraint.',
fields: {
_polygon: {
description: 'This is the polygon to be specified.',
type: POLYGON,
type: POLYGON_INPUT,
},
_centerSphere: {
description: 'This is the sphere to be specified.',
type: CENTER_SPHERE_OPERATOR,
type: CENTER_SPHERE_INPUT,
},
},
});
const GEO_INTERSECTS = new GraphQLInputObjectType({
name: 'GeoIntersectsOperator',
const GEO_INTERSECTS_INPUT = new GraphQLInputObjectType({
name: 'GeoIntersectsInput',
description:
'The GeoIntersectsOperator input type is used to specify a $geoIntersects operation on a constraint.',
'The GeoIntersectsInput type is used to specify a $geoIntersects operation on a constraint.',
fields: {
_point: {
description: 'This is the point to be specified.',
type: GEO_POINT,
type: GEO_POINT_INPUT,
},
},
});
@@ -739,13 +739,13 @@ const _exists = {
const _select = {
description:
'This is the $select operator to specify a constraint to select the objects where a field equals to a key in the result of a different query.',
type: SELECT_OPERATOR,
type: SELECT_INPUT,
};
const _dontSelect = {
description:
'This is the $dontSelect operator to specify a constraint to select the objects where a field do not equal to a key in the result of a different query.',
type: SELECT_OPERATOR,
type: SELECT_INPUT,
};
const _regex = {
@@ -760,10 +760,10 @@ const _options = {
type: GraphQLString,
};
const STRING_CONSTRAINT = new GraphQLInputObjectType({
name: 'StringConstraint',
const STRING_WHERE_INPUT = new GraphQLInputObjectType({
name: 'StringWhereInput',
description:
'The StringConstraint input type is used in operations that involve filtering objects by a field of type String.',
'The StringWhereInput input type is used in operations that involve filtering objects by a field of type String.',
fields: {
_eq: _eq(GraphQLString),
_ne: _ne(GraphQLString),
@@ -781,15 +781,15 @@ const STRING_CONSTRAINT = new GraphQLInputObjectType({
_text: {
description:
'This is the $text operator to specify a full text search constraint.',
type: TEXT_OPERATOR,
type: TEXT_INPUT,
},
},
});
const NUMBER_CONSTRAINT = new GraphQLInputObjectType({
name: 'NumberConstraint',
const NUMBER_WHERE_INPUT = new GraphQLInputObjectType({
name: 'NumberWhereInput',
description:
'The NumberConstraint input type is used in operations that involve filtering objects by a field of type Number.',
'The NumberWhereInput input type is used in operations that involve filtering objects by a field of type Number.',
fields: {
_eq: _eq(GraphQLFloat),
_ne: _ne(GraphQLFloat),
@@ -805,10 +805,10 @@ const NUMBER_CONSTRAINT = new GraphQLInputObjectType({
},
});
const BOOLEAN_CONSTRAINT = new GraphQLInputObjectType({
name: 'BooleanConstraint',
const BOOLEAN_WHERE_INPUT = new GraphQLInputObjectType({
name: 'BooleanWhereInput',
description:
'The BooleanConstraint input type is used in operations that involve filtering objects by a field of type Boolean.',
'The BooleanWhereInput input type is used in operations that involve filtering objects by a field of type Boolean.',
fields: {
_eq: _eq(GraphQLBoolean),
_ne: _ne(GraphQLBoolean),
@@ -818,10 +818,10 @@ const BOOLEAN_CONSTRAINT = new GraphQLInputObjectType({
},
});
const ARRAY_CONSTRAINT = new GraphQLInputObjectType({
name: 'ArrayConstraint',
const ARRAY_WHERE_INPUT = new GraphQLInputObjectType({
name: 'ArrayWhereInput',
description:
'The ArrayConstraint input type is used in operations that involve filtering objects by a field of type Array.',
'The ArrayWhereInput input type is used in operations that involve filtering objects by a field of type Array.',
fields: {
_eq: _eq(ANY),
_ne: _ne(ANY),
@@ -847,8 +847,8 @@ const ARRAY_CONSTRAINT = new GraphQLInputObjectType({
},
});
const KEY_VALUE = new GraphQLInputObjectType({
name: 'KeyValue',
const KEY_VALUE_INPUT = new GraphQLInputObjectType({
name: 'KeyValueInput',
description: 'An entry from an object, i.e., a pair of key and value.',
fields: {
_key: {
@@ -862,29 +862,29 @@ const KEY_VALUE = new GraphQLInputObjectType({
},
});
const OBJECT_CONSTRAINT = new GraphQLInputObjectType({
name: 'ObjectConstraint',
const OBJECT_WHERE_INPUT = new GraphQLInputObjectType({
name: 'ObjectWhereInput',
description:
'The ObjectConstraint input type is used in operations that involve filtering result by a field of type Object.',
'The ObjectWhereInput input type is used in operations that involve filtering result by a field of type Object.',
fields: {
_eq: _eq(KEY_VALUE),
_ne: _ne(KEY_VALUE),
_in: _in(KEY_VALUE),
_nin: _nin(KEY_VALUE),
_lt: _lt(KEY_VALUE),
_lte: _lte(KEY_VALUE),
_gt: _gt(KEY_VALUE),
_gte: _gte(KEY_VALUE),
_eq: _eq(KEY_VALUE_INPUT),
_ne: _ne(KEY_VALUE_INPUT),
_in: _in(KEY_VALUE_INPUT),
_nin: _nin(KEY_VALUE_INPUT),
_lt: _lt(KEY_VALUE_INPUT),
_lte: _lte(KEY_VALUE_INPUT),
_gt: _gt(KEY_VALUE_INPUT),
_gte: _gte(KEY_VALUE_INPUT),
_exists,
_select,
_dontSelect,
},
});
const DATE_CONSTRAINT = new GraphQLInputObjectType({
name: 'DateConstraint',
const DATE_WHERE_INPUT = new GraphQLInputObjectType({
name: 'DateWhereInput',
description:
'The DateConstraint input type is used in operations that involve filtering objects by a field of type Date.',
'The DateWhereInput input type is used in operations that involve filtering objects by a field of type Date.',
fields: {
_eq: _eq(DATE),
_ne: _ne(DATE),
@@ -900,10 +900,10 @@ const DATE_CONSTRAINT = new GraphQLInputObjectType({
},
});
const BYTES_CONSTRAINT = new GraphQLInputObjectType({
name: 'BytesConstraint',
const BYTES_WHERE_INPUT = new GraphQLInputObjectType({
name: 'BytesWhereInput',
description:
'The BytesConstraint input type is used in operations that involve filtering objects by a field of type Bytes.',
'The BytesWhereInput input type is used in operations that involve filtering objects by a field of type Bytes.',
fields: {
_eq: _eq(BYTES),
_ne: _ne(BYTES),
@@ -919,10 +919,10 @@ const BYTES_CONSTRAINT = new GraphQLInputObjectType({
},
});
const FILE_CONSTRAINT = new GraphQLInputObjectType({
name: 'FileConstraint',
const FILE_WHERE_INPUT = new GraphQLInputObjectType({
name: 'FileWhereInput',
description:
'The FILE_CONSTRAINT input type is used in operations that involve filtering objects by a field of type File.',
'The FileWhereInput input type is used in operations that involve filtering objects by a field of type File.',
fields: {
_eq: _eq(FILE),
_ne: _ne(FILE),
@@ -940,16 +940,16 @@ const FILE_CONSTRAINT = new GraphQLInputObjectType({
},
});
const GEO_POINT_CONSTRAINT = new GraphQLInputObjectType({
name: 'GeoPointConstraint',
const GEO_POINT_WHERE_INPUT = new GraphQLInputObjectType({
name: 'GeoPointWhereInput',
description:
'The GeoPointConstraint input type is used in operations that involve filtering objects by a field of type GeoPoint.',
'The GeoPointWhereInput input type is used in operations that involve filtering objects by a field of type GeoPoint.',
fields: {
_exists,
_nearSphere: {
description:
'This is the $nearSphere operator to specify a constraint to select the objects where the values of a geo point field is near to another geo point.',
type: GEO_POINT,
type: GEO_POINT_INPUT,
},
_maxDistance: {
description:
@@ -974,26 +974,26 @@ const GEO_POINT_CONSTRAINT = new GraphQLInputObjectType({
_within: {
description:
'This is the $within operator to specify a constraint to select the objects where the values of a geo point field is within a specified box.',
type: WITHIN_OPERATOR,
type: WITHIN_INPUT,
},
_geoWithin: {
description:
'This is the $geoWithin operator to specify a constraint to select the objects where the values of a geo point field is within a specified polygon or sphere.',
type: GEO_WITHIN_OPERATOR,
type: GEO_WITHIN_INPUT,
},
},
});
const POLYGON_CONSTRAINT = new GraphQLInputObjectType({
name: 'PolygonConstraint',
const POLYGON_WHERE_INPUT = new GraphQLInputObjectType({
name: 'PolygonWhereInput',
description:
'The PolygonConstraint input type is used in operations that involve filtering objects by a field of type Polygon.',
'The PolygonWhereInput input type is used in operations that involve filtering objects by a field of type Polygon.',
fields: {
_exists,
_geoIntersects: {
description:
'This is the $geoIntersects operator to specify a constraint to select the objects where the values of a polygon field intersect a specified point.',
type: GEO_INTERSECTS,
type: GEO_INTERSECTS_INPUT,
},
},
});
@@ -1071,42 +1071,43 @@ const loadArrayResult = (parseGraphQLSchema, parseClasses) => {
};
const load = parseGraphQLSchema => {
parseGraphQLSchema.graphQLTypes.push(GraphQLUpload);
parseGraphQLSchema.graphQLTypes.push(ANY);
parseGraphQLSchema.graphQLTypes.push(OBJECT);
parseGraphQLSchema.graphQLTypes.push(DATE);
parseGraphQLSchema.graphQLTypes.push(BYTES);
parseGraphQLSchema.graphQLTypes.push(FILE);
parseGraphQLSchema.graphQLTypes.push(FILE_INFO);
parseGraphQLSchema.graphQLTypes.push(GEO_POINT);
parseGraphQLSchema.graphQLTypes.push(GEO_POINT_INFO);
parseGraphQLSchema.graphQLTypes.push(RELATION_OP);
parseGraphQLSchema.graphQLTypes.push(CREATE_RESULT);
parseGraphQLSchema.graphQLTypes.push(UPDATE_RESULT);
parseGraphQLSchema.graphQLTypes.push(CLASS);
parseGraphQLSchema.graphQLTypes.push(READ_PREFERENCE);
parseGraphQLSchema.graphQLTypes.push(SUBQUERY);
parseGraphQLSchema.graphQLTypes.push(SELECT_OPERATOR);
parseGraphQLSchema.graphQLTypes.push(SEARCH_OPERATOR);
parseGraphQLSchema.graphQLTypes.push(TEXT_OPERATOR);
parseGraphQLSchema.graphQLTypes.push(BOX_OPERATOR);
parseGraphQLSchema.graphQLTypes.push(WITHIN_OPERATOR);
parseGraphQLSchema.graphQLTypes.push(CENTER_SPHERE_OPERATOR);
parseGraphQLSchema.graphQLTypes.push(GEO_WITHIN_OPERATOR);
parseGraphQLSchema.graphQLTypes.push(GEO_INTERSECTS);
parseGraphQLSchema.graphQLTypes.push(STRING_CONSTRAINT);
parseGraphQLSchema.graphQLTypes.push(NUMBER_CONSTRAINT);
parseGraphQLSchema.graphQLTypes.push(BOOLEAN_CONSTRAINT);
parseGraphQLSchema.graphQLTypes.push(ARRAY_CONSTRAINT);
parseGraphQLSchema.graphQLTypes.push(OBJECT_CONSTRAINT);
parseGraphQLSchema.graphQLTypes.push(DATE_CONSTRAINT);
parseGraphQLSchema.graphQLTypes.push(BYTES_CONSTRAINT);
parseGraphQLSchema.graphQLTypes.push(FILE_CONSTRAINT);
parseGraphQLSchema.graphQLTypes.push(GEO_POINT_CONSTRAINT);
parseGraphQLSchema.graphQLTypes.push(POLYGON_CONSTRAINT);
parseGraphQLSchema.graphQLTypes.push(FIND_RESULT);
parseGraphQLSchema.graphQLTypes.push(SIGN_UP_RESULT);
parseGraphQLSchema.graphQLTypes.push(ELEMENT);
parseGraphQLSchema.addGraphQLType(GraphQLUpload, true);
parseGraphQLSchema.addGraphQLType(ANY, true);
parseGraphQLSchema.addGraphQLType(OBJECT, true);
parseGraphQLSchema.addGraphQLType(DATE, true);
parseGraphQLSchema.addGraphQLType(BYTES, true);
parseGraphQLSchema.addGraphQLType(FILE, true);
parseGraphQLSchema.addGraphQLType(FILE_INFO, true);
parseGraphQLSchema.addGraphQLType(GEO_POINT_INPUT, true);
parseGraphQLSchema.addGraphQLType(GEO_POINT, true);
parseGraphQLSchema.addGraphQLType(RELATION_OP, true);
parseGraphQLSchema.addGraphQLType(CREATE_RESULT, true);
parseGraphQLSchema.addGraphQLType(UPDATE_RESULT, true);
parseGraphQLSchema.addGraphQLType(CLASS, true);
parseGraphQLSchema.addGraphQLType(READ_PREFERENCE, true);
parseGraphQLSchema.addGraphQLType(SUBQUERY_INPUT, true);
parseGraphQLSchema.addGraphQLType(SELECT_INPUT, true);
parseGraphQLSchema.addGraphQLType(SEARCH_INPUT, true);
parseGraphQLSchema.addGraphQLType(TEXT_INPUT, true);
parseGraphQLSchema.addGraphQLType(BOX_INPUT, true);
parseGraphQLSchema.addGraphQLType(WITHIN_INPUT, true);
parseGraphQLSchema.addGraphQLType(CENTER_SPHERE_INPUT, true);
parseGraphQLSchema.addGraphQLType(GEO_WITHIN_INPUT, true);
parseGraphQLSchema.addGraphQLType(GEO_INTERSECTS_INPUT, true);
parseGraphQLSchema.addGraphQLType(STRING_WHERE_INPUT, true);
parseGraphQLSchema.addGraphQLType(NUMBER_WHERE_INPUT, true);
parseGraphQLSchema.addGraphQLType(BOOLEAN_WHERE_INPUT, true);
parseGraphQLSchema.addGraphQLType(ARRAY_WHERE_INPUT, true);
parseGraphQLSchema.addGraphQLType(KEY_VALUE_INPUT, true);
parseGraphQLSchema.addGraphQLType(OBJECT_WHERE_INPUT, true);
parseGraphQLSchema.addGraphQLType(DATE_WHERE_INPUT, true);
parseGraphQLSchema.addGraphQLType(BYTES_WHERE_INPUT, true);
parseGraphQLSchema.addGraphQLType(FILE_WHERE_INPUT, true);
parseGraphQLSchema.addGraphQLType(GEO_POINT_WHERE_INPUT, true);
parseGraphQLSchema.addGraphQLType(POLYGON_WHERE_INPUT, true);
parseGraphQLSchema.addGraphQLType(FIND_RESULT, true);
parseGraphQLSchema.addGraphQLType(SIGN_UP_RESULT, true);
parseGraphQLSchema.addGraphQLType(ELEMENT, true);
};
export {
@@ -1128,10 +1129,10 @@ export {
FILE,
FILE_INFO,
GEO_POINT_FIELDS,
GEO_POINT_INPUT,
GEO_POINT,
GEO_POINT_INFO,
POLYGON_INPUT,
POLYGON,
POLYGON_INFO,
RELATION_OP,
CLASS_NAME_ATT,
FIELDS_ATT,
@@ -1157,15 +1158,15 @@ export {
SKIP_ATT,
LIMIT_ATT,
COUNT_ATT,
SUBQUERY,
SELECT_OPERATOR,
SEARCH_OPERATOR,
TEXT_OPERATOR,
BOX_OPERATOR,
WITHIN_OPERATOR,
CENTER_SPHERE_OPERATOR,
GEO_WITHIN_OPERATOR,
GEO_INTERSECTS,
SUBQUERY_INPUT,
SELECT_INPUT,
SEARCH_INPUT,
TEXT_INPUT,
BOX_INPUT,
WITHIN_INPUT,
CENTER_SPHERE_INPUT,
GEO_WITHIN_INPUT,
GEO_INTERSECTS_INPUT,
_eq,
_ne,
_lt,
@@ -1179,16 +1180,17 @@ export {
_dontSelect,
_regex,
_options,
STRING_CONSTRAINT,
NUMBER_CONSTRAINT,
BOOLEAN_CONSTRAINT,
ARRAY_CONSTRAINT,
OBJECT_CONSTRAINT,
DATE_CONSTRAINT,
BYTES_CONSTRAINT,
FILE_CONSTRAINT,
GEO_POINT_CONSTRAINT,
POLYGON_CONSTRAINT,
STRING_WHERE_INPUT,
NUMBER_WHERE_INPUT,
BOOLEAN_WHERE_INPUT,
ARRAY_WHERE_INPUT,
KEY_VALUE_INPUT,
OBJECT_WHERE_INPUT,
DATE_WHERE_INPUT,
BYTES_WHERE_INPUT,
FILE_WHERE_INPUT,
GEO_POINT_WHERE_INPUT,
POLYGON_WHERE_INPUT,
FIND_RESULT,
SIGN_UP_RESULT,
ARRAY_RESULT,

View File

@@ -11,7 +11,7 @@ const load = parseGraphQLSchema => {
description:
'The create mutation can be used to create and upload a new file.',
args: {
file: {
upload: {
description: 'This is the new file to be created and uploaded',
type: new GraphQLNonNull(GraphQLUpload),
},
@@ -19,10 +19,10 @@ const load = parseGraphQLSchema => {
type: new GraphQLNonNull(defaultGraphQLTypes.FILE_INFO),
async resolve(_source, args, context) {
try {
const { file } = args;
const { upload } = args;
const { config } = context;
const { createReadStream, filename, mimetype } = await file;
const { createReadStream, filename, mimetype } = await upload;
let data = null;
if (createReadStream) {
const stream = createReadStream();
@@ -81,7 +81,7 @@ const load = parseGraphQLSchema => {
description: 'FilesMutation is the top level type for files mutations.',
fields,
});
parseGraphQLSchema.graphQLTypes.push(filesMutation);
parseGraphQLSchema.addGraphQLType(filesMutation, true, true);
parseGraphQLSchema.graphQLMutations.files = {
description: 'This is the top level for files mutations.',

View File

@@ -45,7 +45,7 @@ const load = parseGraphQLSchema => {
'FunctionsMutation is the top level type for functions mutations.',
fields,
});
parseGraphQLSchema.graphQLTypes.push(functionsMutation);
parseGraphQLSchema.addGraphQLType(functionsMutation, true, true);
parseGraphQLSchema.graphQLMutations.functions = {
description: 'This is the top level for functions mutations.',

View File

@@ -1,33 +1,14 @@
import { GraphQLNonNull, GraphQLBoolean, GraphQLObjectType } from 'graphql';
import * as defaultGraphQLTypes from './defaultGraphQLTypes';
import rest from '../../rest';
const parseMap = {
_op: '__op',
};
const transformToParse = fields => {
if (!fields || typeof fields !== 'object') {
return;
}
Object.keys(fields).forEach(fieldName => {
const fieldValue = fields[fieldName];
if (parseMap[fieldName]) {
delete fields[fieldName];
fields[parseMap[fieldName]] = fieldValue;
}
if (typeof fieldValue === 'object') {
transformToParse(fieldValue);
}
});
};
import { transformMutationInputToParse } from '../transformers/mutation';
const createObject = async (className, fields, config, auth, info) => {
if (!fields) {
fields = {};
}
transformToParse(fields);
transformMutationInputToParse(fields);
return (await rest.create(config, auth, className, fields, info.clientSDK))
.response;
@@ -45,7 +26,7 @@ const updateObject = async (
fields = {};
}
transformToParse(fields);
transformMutationInputToParse(fields);
return (await rest.update(
config,
@@ -63,80 +44,95 @@ const deleteObject = async (className, objectId, config, auth, info) => {
};
const load = parseGraphQLSchema => {
parseGraphQLSchema.graphQLObjectsMutations.create = {
description:
'The create mutation can be used to create a new object of a certain class.',
args: {
className: defaultGraphQLTypes.CLASS_NAME_ATT,
fields: defaultGraphQLTypes.FIELDS_ATT,
},
type: new GraphQLNonNull(defaultGraphQLTypes.CREATE_RESULT),
async resolve(_source, args, context) {
try {
const { className, fields } = args;
const { config, auth, info } = context;
parseGraphQLSchema.addGraphQLObjectMutation(
'create',
{
description:
'The create mutation can be used to create a new object of a certain class.',
args: {
className: defaultGraphQLTypes.CLASS_NAME_ATT,
fields: defaultGraphQLTypes.FIELDS_ATT,
},
type: new GraphQLNonNull(defaultGraphQLTypes.CREATE_RESULT),
async resolve(_source, args, context) {
try {
const { className, fields } = args;
const { config, auth, info } = context;
return await createObject(className, fields, config, auth, info);
} catch (e) {
parseGraphQLSchema.handleError(e);
}
return await createObject(className, fields, config, auth, info);
} catch (e) {
parseGraphQLSchema.handleError(e);
}
},
},
};
true,
true
);
parseGraphQLSchema.graphQLObjectsMutations.update = {
description:
'The update mutation can be used to update an object of a certain class.',
args: {
className: defaultGraphQLTypes.CLASS_NAME_ATT,
objectId: defaultGraphQLTypes.OBJECT_ID_ATT,
fields: defaultGraphQLTypes.FIELDS_ATT,
},
type: new GraphQLNonNull(defaultGraphQLTypes.UPDATE_RESULT),
async resolve(_source, args, context) {
try {
const { className, objectId, fields } = args;
const { config, auth, info } = context;
parseGraphQLSchema.addGraphQLObjectMutation(
'update',
{
description:
'The update mutation can be used to update an object of a certain class.',
args: {
className: defaultGraphQLTypes.CLASS_NAME_ATT,
objectId: defaultGraphQLTypes.OBJECT_ID_ATT,
fields: defaultGraphQLTypes.FIELDS_ATT,
},
type: new GraphQLNonNull(defaultGraphQLTypes.UPDATE_RESULT),
async resolve(_source, args, context) {
try {
const { className, objectId, fields } = args;
const { config, auth, info } = context;
return await updateObject(
className,
objectId,
fields,
config,
auth,
info
);
} catch (e) {
parseGraphQLSchema.handleError(e);
}
return await updateObject(
className,
objectId,
fields,
config,
auth,
info
);
} catch (e) {
parseGraphQLSchema.handleError(e);
}
},
},
};
true,
true
);
parseGraphQLSchema.graphQLObjectsMutations.delete = {
description:
'The delete mutation can be used to delete an object of a certain class.',
args: {
className: defaultGraphQLTypes.CLASS_NAME_ATT,
objectId: defaultGraphQLTypes.OBJECT_ID_ATT,
},
type: new GraphQLNonNull(GraphQLBoolean),
async resolve(_source, args, context) {
try {
const { className, objectId } = args;
const { config, auth, info } = context;
parseGraphQLSchema.addGraphQLObjectMutation(
'delete',
{
description:
'The delete mutation can be used to delete an object of a certain class.',
args: {
className: defaultGraphQLTypes.CLASS_NAME_ATT,
objectId: defaultGraphQLTypes.OBJECT_ID_ATT,
},
type: new GraphQLNonNull(GraphQLBoolean),
async resolve(_source, args, context) {
try {
const { className, objectId } = args;
const { config, auth, info } = context;
return await deleteObject(className, objectId, config, auth, info);
} catch (e) {
parseGraphQLSchema.handleError(e);
}
return await deleteObject(className, objectId, config, auth, info);
} catch (e) {
parseGraphQLSchema.handleError(e);
}
},
},
};
true,
true
);
const objectsMutation = new GraphQLObjectType({
name: 'ObjectsMutation',
description: 'ObjectsMutation is the top level type for objects mutations.',
fields: parseGraphQLSchema.graphQLObjectsMutations,
});
parseGraphQLSchema.graphQLTypes.push(objectsMutation);
parseGraphQLSchema.addGraphQLType(objectsMutation, true, true);
parseGraphQLSchema.graphQLMutations.objects = {
description: 'This is the top level for objects mutations.',

View File

@@ -8,6 +8,7 @@ import getFieldNames from 'graphql-list-fields';
import Parse from 'parse/node';
import * as defaultGraphQLTypes from './defaultGraphQLTypes';
import rest from '../../rest';
import { transformQueryInputToParse } from '../transformers/query';
const getObject = async (
className,
@@ -54,155 +55,6 @@ const getObject = async (
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',
};
const transformToParse = (constraints, parentFieldName, parentConstraints) => {
if (!constraints || typeof constraints !== 'object') {
return;
}
Object.keys(constraints).forEach(fieldName => {
let fieldValue = constraints[fieldName];
/**
* 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]) {
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') {
transformToParse(fieldValue, fieldName, constraints);
}
});
};
const findObjects = async (
className,
where,
@@ -224,7 +76,7 @@ const findObjects = async (
where = {};
}
transformToParse(where);
transformQueryInputToParse(where);
const options = {};
@@ -282,118 +134,131 @@ const findObjects = async (
};
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,
parseGraphQLSchema.addGraphQLObjectQuery(
'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,
},
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);
type: new GraphQLNonNull(defaultGraphQLTypes.OBJECT),
async resolve(_source, args, context) {
try {
const {
className,
objectId,
keys,
include,
readPreference,
includeReadPreference,
} = args;
return await findObjects(
className,
where,
order,
skip,
limit,
keys,
include,
includeAll,
readPreference,
includeReadPreference,
subqueryReadPreference,
config,
auth,
info,
selectedFields
);
} catch (e) {
parseGraphQLSchema.handleError(e);
}
const { config, auth, info } = context;
return await getObject(
className,
objectId,
keys,
include,
readPreference,
includeReadPreference,
config,
auth,
info
);
} catch (e) {
parseGraphQLSchema.handleError(e);
}
},
},
};
true,
true
);
parseGraphQLSchema.addGraphQLObjectQuery(
'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);
}
},
},
true,
true
);
const objectsQuery = new GraphQLObjectType({
name: 'ObjectsQuery',
description: 'ObjectsQuery is the top level type for objects queries.',
fields: parseGraphQLSchema.graphQLObjectsQueries,
});
parseGraphQLSchema.graphQLTypes.push(objectsQuery);
parseGraphQLSchema.addGraphQLType(objectsQuery, true, true);
parseGraphQLSchema.graphQLQueries.objects = {
description: 'This is the top level for objects queries.',

View File

@@ -5,6 +5,7 @@ import { extractKeysAndInclude } from '../parseGraphQLUtils';
import * as objectsMutations from './objectsMutations';
import * as objectsQueries from './objectsQueries';
import { ParseGraphQLClassConfig } from '../../Controllers/ParseGraphQLController';
import { transformClassNameToGraphQL } from '../transformers/className';
const getParseClassMutationConfig = function(
parseClassConfig: ?ParseGraphQLClassConfig
@@ -39,7 +40,9 @@ const load = function(
parseClass,
parseClassConfig: ?ParseGraphQLClassConfig
) {
const { className } = parseClass;
const className = parseClass.className;
const graphQLClassName = transformClassNameToGraphQL(className);
const {
create: isCreateEnabled = true,
update: isUpdateEnabled = true,
@@ -52,37 +55,29 @@ const load = function(
classGraphQLOutputType,
} = parseGraphQLSchema.parseClassTypes[className];
const createFields = {
description: 'These are the fields used to create the object.',
type: classGraphQLCreateType,
};
const updateFields = {
description: 'These are the fields used to update the object.',
type: classGraphQLUpdateType,
};
const classGraphQLCreateTypeFields = isCreateEnabled
? classGraphQLCreateType.getFields()
: null;
const classGraphQLUpdateTypeFields = isUpdateEnabled
? classGraphQLUpdateType.getFields()
: null;
const transformTypes = (inputType: 'create' | 'update', fields) => {
if (fields) {
const classGraphQLCreateTypeFields =
isCreateEnabled && classGraphQLCreateType
? classGraphQLCreateType.getFields()
: null;
const classGraphQLUpdateTypeFields =
isUpdateEnabled && classGraphQLUpdateType
? classGraphQLUpdateType.getFields()
: null;
Object.keys(fields).forEach(field => {
let inputTypeField;
if (inputType === 'create') {
if (inputType === 'create' && classGraphQLCreateTypeFields) {
inputTypeField = classGraphQLCreateTypeFields[field];
} else {
} else if (classGraphQLUpdateTypeFields) {
inputTypeField = classGraphQLUpdateTypeFields[field];
}
if (inputTypeField) {
switch (inputTypeField.type) {
case defaultGraphQLTypes.GEO_POINT:
case defaultGraphQLTypes.GEO_POINT_INPUT:
fields[field].__type = 'GeoPoint';
break;
case defaultGraphQLTypes.POLYGON:
case defaultGraphQLTypes.POLYGON_INPUT:
fields[field] = {
__type: 'Polygon',
coordinates: fields[field].map(geoPoint => [
@@ -98,13 +93,18 @@ const load = function(
};
if (isCreateEnabled) {
const createGraphQLMutationName = `create${className}`;
parseGraphQLSchema.graphQLObjectsMutations[createGraphQLMutationName] = {
description: `The ${createGraphQLMutationName} mutation can be used to create a new object of the ${className} class.`,
const createGraphQLMutationName = `create${graphQLClassName}`;
parseGraphQLSchema.addGraphQLObjectMutation(createGraphQLMutationName, {
description: `The ${createGraphQLMutationName} mutation can be used to create a new object of the ${graphQLClassName} class.`,
args: {
fields: createFields,
fields: {
description: 'These are the fields used to create the object.',
type: classGraphQLCreateType || defaultGraphQLTypes.OBJECT,
},
},
type: new GraphQLNonNull(classGraphQLOutputType),
type: new GraphQLNonNull(
classGraphQLOutputType || defaultGraphQLTypes.OBJECT
),
async resolve(_source, args, context, mutationInfo) {
try {
let { fields } = args;
@@ -150,18 +150,23 @@ const load = function(
parseGraphQLSchema.handleError(e);
}
},
};
});
}
if (isUpdateEnabled) {
const updateGraphQLMutationName = `update${className}`;
parseGraphQLSchema.graphQLObjectsMutations[updateGraphQLMutationName] = {
description: `The ${updateGraphQLMutationName} mutation can be used to update an object of the ${className} class.`,
const updateGraphQLMutationName = `update${graphQLClassName}`;
parseGraphQLSchema.addGraphQLObjectMutation(updateGraphQLMutationName, {
description: `The ${updateGraphQLMutationName} mutation can be used to update an object of the ${graphQLClassName} class.`,
args: {
objectId: defaultGraphQLTypes.OBJECT_ID_ATT,
fields: updateFields,
fields: {
description: 'These are the fields used to update the object.',
type: classGraphQLUpdateType || defaultGraphQLTypes.OBJECT,
},
},
type: new GraphQLNonNull(classGraphQLOutputType),
type: new GraphQLNonNull(
classGraphQLOutputType || defaultGraphQLTypes.OBJECT
),
async resolve(_source, args, context, mutationInfo) {
try {
const { objectId, fields } = args;
@@ -205,17 +210,19 @@ const load = function(
parseGraphQLSchema.handleError(e);
}
},
};
});
}
if (isDestroyEnabled) {
const deleteGraphQLMutationName = `delete${className}`;
parseGraphQLSchema.graphQLObjectsMutations[deleteGraphQLMutationName] = {
description: `The ${deleteGraphQLMutationName} mutation can be used to delete an object of the ${className} class.`,
const deleteGraphQLMutationName = `delete${graphQLClassName}`;
parseGraphQLSchema.addGraphQLObjectMutation(deleteGraphQLMutationName, {
description: `The ${deleteGraphQLMutationName} mutation can be used to delete an object of the ${graphQLClassName} class.`,
args: {
objectId: defaultGraphQLTypes.OBJECT_ID_ATT,
},
type: new GraphQLNonNull(classGraphQLOutputType),
type: new GraphQLNonNull(
classGraphQLOutputType || defaultGraphQLTypes.OBJECT
),
async resolve(_source, args, context, mutationInfo) {
try {
const { objectId } = args;
@@ -250,7 +257,7 @@ const load = function(
parseGraphQLSchema.handleError(e);
}
},
};
});
}
};

View File

@@ -1,8 +1,10 @@
import { GraphQLNonNull } from 'graphql';
import getFieldNames from 'graphql-list-fields';
import pluralize from 'pluralize';
import * as defaultGraphQLTypes from './defaultGraphQLTypes';
import * as objectsQueries from './objectsQueries';
import { ParseGraphQLClassConfig } from '../../Controllers/ParseGraphQLController';
import { transformClassNameToGraphQL } from '../transformers/className';
import { extractKeysAndInclude } from '../parseGraphQLUtils';
const getParseClassQueryConfig = function(
@@ -36,7 +38,8 @@ const load = function(
parseClass,
parseClassConfig: ?ParseGraphQLClassConfig
) {
const { className } = parseClass;
const className = parseClass.className;
const graphQLClassName = transformClassNameToGraphQL(className);
const {
get: isGetEnabled = true,
find: isFindEnabled = true,
@@ -49,15 +52,18 @@ const load = function(
} = parseGraphQLSchema.parseClassTypes[className];
if (isGetEnabled) {
const getGraphQLQueryName = `get${className}`;
parseGraphQLSchema.graphQLObjectsQueries[getGraphQLQueryName] = {
description: `The ${getGraphQLQueryName} query can be used to get an object of the ${className} class by its id.`,
const getGraphQLQueryName =
graphQLClassName.charAt(0).toLowerCase() + graphQLClassName.slice(1);
parseGraphQLSchema.addGraphQLObjectQuery(getGraphQLQueryName, {
description: `The ${getGraphQLQueryName} query can be used to get an object of the ${graphQLClassName} class by its id.`,
args: {
objectId: defaultGraphQLTypes.OBJECT_ID_ATT,
readPreference: defaultGraphQLTypes.READ_PREFERENCE_ATT,
includeReadPreference: defaultGraphQLTypes.INCLUDE_READ_PREFERENCE_ATT,
},
type: new GraphQLNonNull(classGraphQLOutputType),
type: new GraphQLNonNull(
classGraphQLOutputType || defaultGraphQLTypes.OBJECT
),
async resolve(_source, args, context, queryInfo) {
try {
return await getQuery(className, _source, args, context, queryInfo);
@@ -65,15 +71,19 @@ const load = function(
parseGraphQLSchema.handleError(e);
}
},
};
});
}
if (isFindEnabled) {
const findGraphQLQueryName = `find${className}`;
parseGraphQLSchema.graphQLObjectsQueries[findGraphQLQueryName] = {
description: `The ${findGraphQLQueryName} query can be used to find objects of the ${className} class.`,
const findGraphQLQueryName = pluralize(
graphQLClassName.charAt(0).toLowerCase() + graphQLClassName.slice(1)
);
parseGraphQLSchema.addGraphQLObjectQuery(findGraphQLQueryName, {
description: `The ${findGraphQLQueryName} query can be used to find objects of the ${graphQLClassName} class.`,
args: classGraphQLFindArgs,
type: new GraphQLNonNull(classGraphQLFindResultType),
type: new GraphQLNonNull(
classGraphQLFindResultType || defaultGraphQLTypes.FIND_RESULT
),
async resolve(_source, args, context, queryInfo) {
try {
const {
@@ -116,7 +126,7 @@ const load = function(
parseGraphQLSchema.handleError(e);
}
},
};
});
}
};

View File

@@ -14,6 +14,7 @@ import getFieldNames from 'graphql-list-fields';
import * as defaultGraphQLTypes from './defaultGraphQLTypes';
import * as objectsQueries from './objectsQueries';
import { ParseGraphQLClassConfig } from '../../Controllers/ParseGraphQLController';
import { transformClassNameToGraphQL } from '../transformers/className';
import { extractKeysAndInclude } from '../parseGraphQLUtils';
const mapInputType = (parseType, targetClass, parseClassTypes) => {
@@ -31,13 +32,19 @@ const mapInputType = (parseType, targetClass, parseClassTypes) => {
case 'Date':
return defaultGraphQLTypes.DATE;
case 'Pointer':
if (parseClassTypes[targetClass]) {
if (
parseClassTypes[targetClass] &&
parseClassTypes[targetClass].classGraphQLScalarType
) {
return parseClassTypes[targetClass].classGraphQLScalarType;
} else {
return defaultGraphQLTypes.OBJECT;
}
case 'Relation':
if (parseClassTypes[targetClass]) {
if (
parseClassTypes[targetClass] &&
parseClassTypes[targetClass].classGraphQLRelationOpType
) {
return parseClassTypes[targetClass].classGraphQLRelationOpType;
} else {
return defaultGraphQLTypes.OBJECT;
@@ -45,9 +52,9 @@ const mapInputType = (parseType, targetClass, parseClassTypes) => {
case 'File':
return defaultGraphQLTypes.FILE;
case 'GeoPoint':
return defaultGraphQLTypes.GEO_POINT;
return defaultGraphQLTypes.GEO_POINT_INPUT;
case 'Polygon':
return defaultGraphQLTypes.POLYGON;
return defaultGraphQLTypes.POLYGON_INPUT;
case 'Bytes':
return defaultGraphQLTypes.BYTES;
case 'ACL':
@@ -72,13 +79,19 @@ const mapOutputType = (parseType, targetClass, parseClassTypes) => {
case 'Date':
return defaultGraphQLTypes.DATE;
case 'Pointer':
if (parseClassTypes[targetClass]) {
if (
parseClassTypes[targetClass] &&
parseClassTypes[targetClass].classGraphQLOutputType
) {
return parseClassTypes[targetClass].classGraphQLOutputType;
} else {
return defaultGraphQLTypes.OBJECT;
}
case 'Relation':
if (parseClassTypes[targetClass]) {
if (
parseClassTypes[targetClass] &&
parseClassTypes[targetClass].classGraphQLFindResultType
) {
return new GraphQLNonNull(
parseClassTypes[targetClass].classGraphQLFindResultType
);
@@ -88,9 +101,9 @@ const mapOutputType = (parseType, targetClass, parseClassTypes) => {
case 'File':
return defaultGraphQLTypes.FILE_INFO;
case 'GeoPoint':
return defaultGraphQLTypes.GEO_POINT_INFO;
return defaultGraphQLTypes.GEO_POINT;
case 'Polygon':
return defaultGraphQLTypes.POLYGON_INFO;
return defaultGraphQLTypes.POLYGON;
case 'Bytes':
return defaultGraphQLTypes.BYTES;
case 'ACL':
@@ -103,33 +116,36 @@ const mapOutputType = (parseType, targetClass, parseClassTypes) => {
const mapConstraintType = (parseType, targetClass, parseClassTypes) => {
switch (parseType) {
case 'String':
return defaultGraphQLTypes.STRING_CONSTRAINT;
return defaultGraphQLTypes.STRING_WHERE_INPUT;
case 'Number':
return defaultGraphQLTypes.NUMBER_CONSTRAINT;
return defaultGraphQLTypes.NUMBER_WHERE_INPUT;
case 'Boolean':
return defaultGraphQLTypes.BOOLEAN_CONSTRAINT;
return defaultGraphQLTypes.BOOLEAN_WHERE_INPUT;
case 'Array':
return defaultGraphQLTypes.ARRAY_CONSTRAINT;
return defaultGraphQLTypes.ARRAY_WHERE_INPUT;
case 'Object':
return defaultGraphQLTypes.OBJECT_CONSTRAINT;
return defaultGraphQLTypes.OBJECT_WHERE_INPUT;
case 'Date':
return defaultGraphQLTypes.DATE_CONSTRAINT;
return defaultGraphQLTypes.DATE_WHERE_INPUT;
case 'Pointer':
if (parseClassTypes[targetClass]) {
if (
parseClassTypes[targetClass] &&
parseClassTypes[targetClass].classGraphQLConstraintType
) {
return parseClassTypes[targetClass].classGraphQLConstraintType;
} else {
return defaultGraphQLTypes.OBJECT;
}
case 'File':
return defaultGraphQLTypes.FILE_CONSTRAINT;
return defaultGraphQLTypes.FILE_WHERE_INPUT;
case 'GeoPoint':
return defaultGraphQLTypes.GEO_POINT_CONSTRAINT;
return defaultGraphQLTypes.GEO_POINT_WHERE_INPUT;
case 'Polygon':
return defaultGraphQLTypes.POLYGON_CONSTRAINT;
return defaultGraphQLTypes.POLYGON_WHERE_INPUT;
case 'Bytes':
return defaultGraphQLTypes.BYTES_CONSTRAINT;
return defaultGraphQLTypes.BYTES_WHERE_INPUT;
case 'ACL':
return defaultGraphQLTypes.OBJECT_CONSTRAINT;
return defaultGraphQLTypes.OBJECT_WHERE_INPUT;
case 'Relation':
default:
return undefined;
@@ -233,7 +249,8 @@ const load = (
parseClass,
parseClassConfig: ?ParseGraphQLClassConfig
) => {
const { className } = parseClass;
const className = parseClass.className;
const graphQLClassName = transformClassNameToGraphQL(className);
const {
classCreateFields,
classUpdateFields,
@@ -242,12 +259,12 @@ const load = (
classSortFields,
} = getInputFieldsAndConstraints(parseClass, parseClassConfig);
const classGraphQLScalarTypeName = `${className}Pointer`;
const classGraphQLScalarTypeName = `${graphQLClassName}Pointer`;
const parseScalarValue = value => {
if (typeof value === 'string') {
return {
__type: 'Pointer',
className,
className: className,
objectId: value,
};
} else if (
@@ -256,7 +273,7 @@ const load = (
value.className === className &&
typeof value.objectId === 'string'
) {
return value;
return { ...value, className };
}
throw new defaultGraphQLTypes.TypeValidationError(
@@ -264,9 +281,9 @@ const load = (
classGraphQLScalarTypeName
);
};
const classGraphQLScalarType = new GraphQLScalarType({
let classGraphQLScalarType = new GraphQLScalarType({
name: classGraphQLScalarTypeName,
description: `The ${classGraphQLScalarTypeName} is used in operations that involve ${className} pointers.`,
description: `The ${classGraphQLScalarTypeName} is used in operations that involve ${graphQLClassName} pointers.`,
parseValue: parseScalarValue,
serialize(value) {
if (typeof value === 'string') {
@@ -318,12 +335,14 @@ const load = (
);
},
});
parseGraphQLSchema.graphQLTypes.push(classGraphQLScalarType);
classGraphQLScalarType =
parseGraphQLSchema.addGraphQLType(classGraphQLScalarType) ||
defaultGraphQLTypes.OBJECT;
const classGraphQLRelationOpTypeName = `${className}RelationOp`;
const classGraphQLRelationOpType = new GraphQLInputObjectType({
const classGraphQLRelationOpTypeName = `${graphQLClassName}RelationOpInput`;
let classGraphQLRelationOpType = new GraphQLInputObjectType({
name: classGraphQLRelationOpTypeName,
description: `The ${classGraphQLRelationOpTypeName} input type is used in operations that involve relations with the ${className} class.`,
description: `The ${classGraphQLRelationOpTypeName} type is used in operations that involve relations with the ${graphQLClassName} class.`,
fields: () => ({
_op: {
description: 'This is the operation to be executed.',
@@ -341,12 +360,14 @@ const load = (
},
}),
});
parseGraphQLSchema.graphQLTypes.push(classGraphQLRelationOpType);
classGraphQLRelationOpType =
parseGraphQLSchema.addGraphQLType(classGraphQLRelationOpType) ||
defaultGraphQLTypes.OBJECT;
const classGraphQLCreateTypeName = `${className}CreateFields`;
const classGraphQLCreateType = new GraphQLInputObjectType({
const classGraphQLCreateTypeName = `Create${graphQLClassName}FieldsInput`;
let classGraphQLCreateType = new GraphQLInputObjectType({
name: classGraphQLCreateTypeName,
description: `The ${classGraphQLCreateTypeName} input type is used in operations that involve creation of objects in the ${className} class.`,
description: `The ${classGraphQLCreateTypeName} input type is used in operations that involve creation of objects in the ${graphQLClassName} class.`,
fields: () =>
classCreateFields.reduce(
(fields, field) => {
@@ -372,12 +393,14 @@ const load = (
}
),
});
parseGraphQLSchema.graphQLTypes.push(classGraphQLCreateType);
classGraphQLCreateType = parseGraphQLSchema.addGraphQLType(
classGraphQLCreateType
);
const classGraphQLUpdateTypeName = `${className}UpdateFields`;
const classGraphQLUpdateType = new GraphQLInputObjectType({
const classGraphQLUpdateTypeName = `Update${graphQLClassName}FieldsInput`;
let classGraphQLUpdateType = new GraphQLInputObjectType({
name: classGraphQLUpdateTypeName,
description: `The ${classGraphQLUpdateTypeName} input type is used in operations that involve creation of objects in the ${className} class.`,
description: `The ${classGraphQLUpdateTypeName} input type is used in operations that involve creation of objects in the ${graphQLClassName} class.`,
fields: () =>
classUpdateFields.reduce(
(fields, field) => {
@@ -403,12 +426,14 @@ const load = (
}
),
});
parseGraphQLSchema.graphQLTypes.push(classGraphQLUpdateType);
classGraphQLUpdateType = parseGraphQLSchema.addGraphQLType(
classGraphQLUpdateType
);
const classGraphQLConstraintTypeName = `${className}PointerConstraint`;
const classGraphQLConstraintType = new GraphQLInputObjectType({
const classGraphQLConstraintTypeName = `${graphQLClassName}PointerWhereInput`;
let classGraphQLConstraintType = new GraphQLInputObjectType({
name: classGraphQLConstraintTypeName,
description: `The ${classGraphQLConstraintTypeName} input type is used in operations that involve filtering objects by a pointer field to ${className} class.`,
description: `The ${classGraphQLConstraintTypeName} input type is used in operations that involve filtering objects by a pointer field to ${graphQLClassName} class.`,
fields: {
_eq: defaultGraphQLTypes._eq(classGraphQLScalarType),
_ne: defaultGraphQLTypes._ne(classGraphQLScalarType),
@@ -420,21 +445,23 @@ const load = (
_inQuery: {
description:
'This is the $inQuery operator to specify a constraint to select the objects where a field equals to any of the ids in the result of a different query.',
type: defaultGraphQLTypes.SUBQUERY,
type: defaultGraphQLTypes.SUBQUERY_INPUT,
},
_notInQuery: {
description:
'This is the $notInQuery operator to specify a constraint to select the objects where a field do not equal to any of the ids in the result of a different query.',
type: defaultGraphQLTypes.SUBQUERY,
type: defaultGraphQLTypes.SUBQUERY_INPUT,
},
},
});
parseGraphQLSchema.graphQLTypes.push(classGraphQLConstraintType);
classGraphQLConstraintType = parseGraphQLSchema.addGraphQLType(
classGraphQLConstraintType
);
const classGraphQLConstraintsTypeName = `${className}Constraints`;
const classGraphQLConstraintsType = new GraphQLInputObjectType({
const classGraphQLConstraintsTypeName = `${graphQLClassName}WhereInput`;
let classGraphQLConstraintsType = new GraphQLInputObjectType({
name: classGraphQLConstraintsTypeName,
description: `The ${classGraphQLConstraintsTypeName} input type is used in operations that involve filtering objects of ${className} class.`,
description: `The ${classGraphQLConstraintsTypeName} input type is used in operations that involve filtering objects of ${graphQLClassName} class.`,
fields: () => ({
...classConstraintFields.reduce((fields, field) => {
const type = mapConstraintType(
@@ -468,12 +495,14 @@ const load = (
},
}),
});
parseGraphQLSchema.graphQLTypes.push(classGraphQLConstraintsType);
classGraphQLConstraintsType =
parseGraphQLSchema.addGraphQLType(classGraphQLConstraintsType) ||
defaultGraphQLTypes.OBJECT;
const classGraphQLOrderTypeName = `${className}Order`;
const classGraphQLOrderType = new GraphQLEnumType({
const classGraphQLOrderTypeName = `${graphQLClassName}Order`;
let classGraphQLOrderType = new GraphQLEnumType({
name: classGraphQLOrderTypeName,
description: `The ${classGraphQLOrderTypeName} input type is used when sorting objects of the ${className} class.`,
description: `The ${classGraphQLOrderTypeName} input type is used when sorting objects of the ${graphQLClassName} class.`,
values: classSortFields.reduce((sortFields, fieldConfig) => {
const { field, asc, desc } = fieldConfig;
const updatedSortFields = {
@@ -488,7 +517,9 @@ const load = (
return updatedSortFields;
}, {}),
});
parseGraphQLSchema.graphQLTypes.push(classGraphQLOrderType);
classGraphQLOrderType = parseGraphQLSchema.addGraphQLType(
classGraphQLOrderType
);
const classGraphQLFindArgs = {
where: {
@@ -498,7 +529,9 @@ const load = (
},
order: {
description: 'The fields to be used when sorting the data fetched.',
type: new GraphQLList(new GraphQLNonNull(classGraphQLOrderType)),
type: classGraphQLOrderType
? new GraphQLList(new GraphQLNonNull(classGraphQLOrderType))
: GraphQLString,
},
skip: defaultGraphQLTypes.SKIP_ATT,
limit: defaultGraphQLTypes.LIMIT_ATT,
@@ -507,7 +540,7 @@ const load = (
subqueryReadPreference: defaultGraphQLTypes.SUBQUERY_READ_PREFERENCE_ATT,
};
const classGraphQLOutputTypeName = `${className}Class`;
const classGraphQLOutputTypeName = `${graphQLClassName}`;
const outputFields = () => {
return classOutputFields.reduce((fields, field) => {
const type = mapOutputType(
@@ -548,14 +581,13 @@ const load = (
.filter(field => field.includes('.'))
.map(field => field.slice(field.indexOf('.') + 1))
);
return await objectsQueries.findObjects(
source[field].className,
{
_relatedTo: {
object: {
__type: 'Pointer',
className,
className: className,
objectId: source.objectId,
},
key: field,
@@ -634,29 +666,37 @@ const load = (
}
}, defaultGraphQLTypes.CLASS_FIELDS);
};
const classGraphQLOutputType = new GraphQLObjectType({
let classGraphQLOutputType = new GraphQLObjectType({
name: classGraphQLOutputTypeName,
description: `The ${classGraphQLOutputTypeName} object type is used in operations that involve outputting objects of ${className} class.`,
description: `The ${classGraphQLOutputTypeName} object type is used in operations that involve outputting objects of ${graphQLClassName} class.`,
interfaces: [defaultGraphQLTypes.CLASS],
fields: outputFields,
});
parseGraphQLSchema.graphQLTypes.push(classGraphQLOutputType);
classGraphQLOutputType = parseGraphQLSchema.addGraphQLType(
classGraphQLOutputType
);
const classGraphQLFindResultTypeName = `${className}FindResult`;
const classGraphQLFindResultType = new GraphQLObjectType({
const classGraphQLFindResultTypeName = `${graphQLClassName}FindResult`;
let classGraphQLFindResultType = new GraphQLObjectType({
name: classGraphQLFindResultTypeName,
description: `The ${classGraphQLFindResultTypeName} object type is used in the ${className} find query to return the data of the matched objects.`,
description: `The ${classGraphQLFindResultTypeName} object type is used in the ${graphQLClassName} find query to return the data of the matched objects.`,
fields: {
results: {
description: 'This is the objects returned by the query',
type: new GraphQLNonNull(
new GraphQLList(new GraphQLNonNull(classGraphQLOutputType))
new GraphQLList(
new GraphQLNonNull(
classGraphQLOutputType || defaultGraphQLTypes.OBJECT
)
)
),
},
count: defaultGraphQLTypes.COUNT_ATT,
},
});
parseGraphQLSchema.graphQLTypes.push(classGraphQLFindResultType);
classGraphQLFindResultType = parseGraphQLSchema.addGraphQLType(
classGraphQLFindResultType
);
parseGraphQLSchema.parseClassTypes[className] = {
classGraphQLScalarType,
@@ -671,22 +711,22 @@ const load = (
};
if (className === '_User') {
const meType = new GraphQLObjectType({
name: 'Me',
description: `The Me object type is used in operations that involve outputting the current user data.`,
const viewerType = new GraphQLObjectType({
name: 'Viewer',
description: `The Viewer object type is used in operations that involve outputting the current user data.`,
interfaces: [defaultGraphQLTypes.CLASS],
fields: () => ({
...outputFields(),
sessionToken: defaultGraphQLTypes.SESSION_TOKEN_ATT,
}),
});
parseGraphQLSchema.meType = meType;
parseGraphQLSchema.graphQLTypes.push(meType);
parseGraphQLSchema.viewerType = viewerType;
parseGraphQLSchema.addGraphQLType(viewerType, true, true);
const userSignUpInputTypeName = '_UserSignUpFields';
const userSignUpInputTypeName = 'SignUpFieldsInput';
const userSignUpInputType = new GraphQLInputObjectType({
name: userSignUpInputTypeName,
description: `The ${userSignUpInputTypeName} input type is used in operations that involve inputting objects of ${className} class when signing up.`,
description: `The ${userSignUpInputTypeName} input type is used in operations that involve inputting objects of ${graphQLClassName} class when signing up.`,
fields: () =>
classCreateFields.reduce((fields, field) => {
const type = mapInputType(
@@ -710,8 +750,9 @@ const load = (
}
}, {}),
});
parseGraphQLSchema.addGraphQLType(userSignUpInputType, true, true);
const userLogInInputTypeName = '_UserLoginFields';
const userLogInInputTypeName = 'LogInFieldsInput';
const userLogInInputType = new GraphQLInputObjectType({
name: userLogInInputTypeName,
description: `The ${userLogInInputTypeName} input type is used to login.`,
@@ -726,13 +767,14 @@ const load = (
},
},
});
parseGraphQLSchema.addGraphQLType(userLogInInputType, true, true);
parseGraphQLSchema.parseClassTypes[
'_User'
className
].signUpInputType = userSignUpInputType;
parseGraphQLSchema.parseClassTypes[
'_User'
className
].logInInputType = userLogInInputType;
parseGraphQLSchema.graphQLTypes.push(userSignUpInputType);
}
};

View File

@@ -1,4 +1,4 @@
import { GraphQLBoolean, GraphQLNonNull, GraphQLObjectType } from 'graphql';
import { GraphQLNonNull, GraphQLObjectType } from 'graphql';
import UsersRouter from '../../Routers/UsersRouter';
import * as objectsMutations from './objectsMutations';
import { getUserFromSessionToken } from './usersQueries';
@@ -19,10 +19,11 @@ const load = parseGraphQLSchema => {
type: parseGraphQLSchema.parseClassTypes['_User'].signUpInputType,
},
},
type: new GraphQLNonNull(parseGraphQLSchema.meType),
type: new GraphQLNonNull(parseGraphQLSchema.viewerType),
async resolve(_source, args, context, mutationInfo) {
try {
const { fields } = args;
const { config, auth, info } = context;
const { sessionToken } = await objectsMutations.createObject(
@@ -45,16 +46,16 @@ const load = parseGraphQLSchema => {
fields.logIn = {
description: 'The logIn mutation can be used to log the user in.',
args: {
input: {
fields: {
description: 'This is data needed to login',
type: parseGraphQLSchema.parseClassTypes['_User'].logInInputType,
},
},
type: new GraphQLNonNull(parseGraphQLSchema.meType),
type: new GraphQLNonNull(parseGraphQLSchema.viewerType),
async resolve(_source, args, context) {
try {
const {
input: { username, password },
fields: { username, password },
} = args;
const { config, auth, info } = context;
@@ -76,17 +77,24 @@ const load = parseGraphQLSchema => {
fields.logOut = {
description: 'The logOut mutation can be used to log the user out.',
type: new GraphQLNonNull(GraphQLBoolean),
async resolve(_source, _args, context) {
type: new GraphQLNonNull(parseGraphQLSchema.viewerType),
async resolve(_source, _args, context, mutationInfo) {
try {
const { config, auth, info } = context;
const viewer = await getUserFromSessionToken(
config,
info,
mutationInfo
);
await usersRouter.handleLogOut({
config,
auth,
info,
});
return true;
return viewer;
} catch (e) {
parseGraphQLSchema.handleError(e);
}
@@ -98,7 +106,7 @@ const load = parseGraphQLSchema => {
description: 'UsersMutation is the top level type for files mutations.',
fields,
});
parseGraphQLSchema.graphQLTypes.push(usersMutation);
parseGraphQLSchema.addGraphQLType(usersMutation, true, true);
parseGraphQLSchema.graphQLMutations.users = {
description: 'This is the top level for users mutations.',

View File

@@ -51,9 +51,10 @@ const load = parseGraphQLSchema => {
}
const fields = {};
fields.me = {
description: 'The Me query can be used to return the current user data.',
type: new GraphQLNonNull(parseGraphQLSchema.meType),
fields.viewer = {
description:
'The viewer query can be used to return the current user data.',
type: new GraphQLNonNull(parseGraphQLSchema.viewerType),
async resolve(_source, _args, context, queryInfo) {
try {
const { config, info } = context;
@@ -69,7 +70,7 @@ const load = parseGraphQLSchema => {
description: 'UsersQuery is the top level type for users queries.',
fields,
});
parseGraphQLSchema.graphQLTypes.push(usersQuery);
parseGraphQLSchema.addGraphQLType(usersQuery, true, true);
parseGraphQLSchema.graphQLQueries.users = {
description: 'This is the top level for users queries.',

View File

@@ -0,0 +1,8 @@
const transformClassNameToGraphQL = className => {
if (className[0] === '_') {
className = className.slice(1);
}
return className[0].toUpperCase() + className.slice(1);
};
export { transformClassNameToGraphQL };

View File

@@ -0,0 +1,21 @@
const parseMap = {
_op: '__op',
};
const transformMutationInputToParse = fields => {
if (!fields || typeof fields !== 'object') {
return;
}
Object.keys(fields).forEach(fieldName => {
const fieldValue = fields[fieldName];
if (parseMap[fieldName]) {
delete fields[fieldName];
fields[parseMap[fieldName]] = fieldValue;
}
if (typeof fieldValue === 'object') {
transformMutationInputToParse(fieldValue);
}
});
};
export { transformMutationInputToParse };

View File

@@ -0,0 +1,154 @@
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',
};
const transformQueryInputToParse = (
constraints,
parentFieldName,
parentConstraints
) => {
if (!constraints || typeof constraints !== 'object') {
return;
}
Object.keys(constraints).forEach(fieldName => {
let fieldValue = constraints[fieldName];
/**
* 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]) {
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') {
transformQueryInputToParse(fieldValue, fieldName, constraints);
}
});
};
export { transformQueryInputToParse };