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==", "integrity": "sha512-J+YW09/vZRuK2/04SykW31xgMTtA/XTm4mSaLoJ79EW31SWstNUzfti9seu0MdWRafgdmRzUn+qCJ/MqXvQZRg==",
"requires": { "requires": {
"@parse/node-gcm": "^1.0.0", "@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", "npmlog": "^4.0.2",
"parse": "^1.11.1" "parse": "^1.11.1"
}, },
"dependencies": { "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": { "parse": {
"version": "1.11.1", "version": "1.11.1",
"resolved": "https://registry.npmjs.org/parse/-/parse-1.11.1.tgz", "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": { "apollo-cache": {
"version": "1.3.2", "version": "1.3.2",
"resolved": "https://registry.npmjs.org/apollo-cache/-/apollo-cache-1.3.2.tgz", "resolved": "https://registry.npmjs.org/apollo-cache/-/apollo-cache-1.3.2.tgz",
@@ -9075,6 +9075,11 @@
"semver-compare": "^1.0.0" "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": { "posix-character-classes": {
"version": "0.1.1", "version": "0.1.1",
"resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", "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", "node-rsa": "1.0.5",
"parse": "2.6.0", "parse": "2.6.0",
"pg-promise": "9.0.1", "pg-promise": "9.0.1",
"pluralize": "^8.0.0",
"redis": "2.8.0", "redis": "2.8.0",
"semver": "6.3.0", "semver": "6.3.0",
"subscriptions-transport-ws": "0.9.16", "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 defaultLogger = require('../lib/logger').default;
const { ParseGraphQLSchema } = require('../lib/GraphQL/ParseGraphQLSchema'); 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 { toGraphQLError } from './parseGraphQLUtils';
import * as schemaDirectives from './loaders/schemaDirectives'; 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 { class ParseGraphQLSchema {
databaseController: DatabaseController; databaseController: DatabaseController;
parseGraphQLController: ParseGraphQLController; parseGraphQLController: ParseGraphQLController;
@@ -60,7 +83,7 @@ class ParseGraphQLSchema {
this.parseClassesString = parseClassesString; this.parseClassesString = parseClassesString;
this.parseGraphQLConfig = parseGraphQLConfig; this.parseGraphQLConfig = parseGraphQLConfig;
this.parseClassTypes = {}; this.parseClassTypes = {};
this.meType = null; this.viewerType = null;
this.graphQLAutoSchema = null; this.graphQLAutoSchema = null;
this.graphQLSchema = null; this.graphQLSchema = null;
this.graphQLTypes = []; this.graphQLTypes = [];
@@ -92,7 +115,7 @@ class ParseGraphQLSchema {
description: 'Query is the top level type for queries.', description: 'Query is the top level type for queries.',
fields: this.graphQLQueries, fields: this.graphQLQueries,
}); });
this.graphQLTypes.push(graphQLQuery); this.addGraphQLType(graphQLQuery, true, true);
} }
let graphQLMutation = undefined; let graphQLMutation = undefined;
@@ -102,7 +125,7 @@ class ParseGraphQLSchema {
description: 'Mutation is the top level type for mutations.', description: 'Mutation is the top level type for mutations.',
fields: this.graphQLMutations, fields: this.graphQLMutations,
}); });
this.graphQLTypes.push(graphQLMutation); this.addGraphQLType(graphQLMutation, true, true);
} }
let graphQLSubscription = undefined; let graphQLSubscription = undefined;
@@ -112,7 +135,7 @@ class ParseGraphQLSchema {
description: 'Subscription is the top level type for subscriptions.', description: 'Subscription is the top level type for subscriptions.',
fields: this.graphQLSubscriptions, fields: this.graphQLSubscriptions,
}); });
this.graphQLTypes.push(graphQLSubscription); this.addGraphQLType(graphQLSubscription, true, true);
} }
this.graphQLAutoSchema = new GraphQLSchema({ this.graphQLAutoSchema = new GraphQLSchema({
@@ -172,6 +195,66 @@ class ParseGraphQLSchema {
return this.graphQLSchema; 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) { handleError(error) {
if (error instanceof Parse.Error) { if (error instanceof Parse.Error) {
this.log.error('Parse error: ', error); this.log.error('Parse error: ', error);
@@ -238,7 +321,32 @@ class ParseGraphQLSchema {
parseGraphQLConfig: ParseGraphQLConfig parseGraphQLConfig: ParseGraphQLConfig
) { ) {
const { classConfigs } = 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; let parseClassConfig;
if (classConfigs) { if (classConfigs) {
parseClassConfig = classConfigs.find( parseClassConfig = classConfigs.find(

View File

@@ -25,25 +25,31 @@ class ParseGraphQLServer {
} }
this.config = config; this.config = config;
this.parseGraphQLController = this.parseServer.config.parseGraphQLController; this.parseGraphQLController = this.parseServer.config.parseGraphQLController;
this.log =
(this.parseServer.config && this.parseServer.config.loggerController) ||
defaultLogger;
this.parseGraphQLSchema = new ParseGraphQLSchema({ this.parseGraphQLSchema = new ParseGraphQLSchema({
parseGraphQLController: this.parseGraphQLController, parseGraphQLController: this.parseGraphQLController,
databaseController: this.parseServer.config.databaseController, databaseController: this.parseServer.config.databaseController,
log: log: this.log,
(this.parseServer.config && this.parseServer.config.loggerController) ||
defaultLogger,
graphQLCustomTypeDefs: this.config.graphQLCustomTypeDefs, graphQLCustomTypeDefs: this.config.graphQLCustomTypeDefs,
}); });
} }
async _getGraphQLOptions(req) { async _getGraphQLOptions(req) {
return { try {
schema: await this.parseGraphQLSchema.load(), return {
context: { schema: await this.parseGraphQLSchema.load(),
info: req.info, context: {
config: req.config, info: req.info,
auth: req.auth, config: req.config,
}, auth: req.auth,
}; },
};
} catch (e) {
this.log.error(e);
throw e;
}
} }
applyGraphQL(app) { 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', name: 'GeoPoint',
description: 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, fields: GEO_POINT_FIELDS,
}); });
const GEO_POINT_INFO = new GraphQLObjectType({ const POLYGON_INPUT = new GraphQLList(new GraphQLNonNull(GEO_POINT_INPUT));
name: 'GeoPointInfo',
description:
'The GeoPointInfo object type is used to return the information about geo points.',
fields: GEO_POINT_FIELDS,
});
const POLYGON = new GraphQLList(new GraphQLNonNull(GEO_POINT)); const POLYGON = new GraphQLList(new GraphQLNonNull(GEO_POINT));
const POLYGON_INFO = new GraphQLList(new GraphQLNonNull(GEO_POINT_INFO));
const RELATION_OP = new GraphQLEnumType({ const RELATION_OP = new GraphQLEnumType({
name: 'RelationOp', name: 'RelationOp',
description: description:
@@ -542,10 +542,10 @@ const COUNT_ATT = {
type: new GraphQLNonNull(GraphQLInt), type: new GraphQLNonNull(GraphQLInt),
}; };
const SUBQUERY = new GraphQLInputObjectType({ const SUBQUERY_INPUT = new GraphQLInputObjectType({
name: 'Subquery', name: 'SubqueryInput',
description: 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: { fields: {
className: CLASS_NAME_ATT, className: CLASS_NAME_ATT,
where: Object.assign({}, WHERE_ATT, { where: Object.assign({}, WHERE_ATT, {
@@ -554,14 +554,14 @@ const SUBQUERY = new GraphQLInputObjectType({
}, },
}); });
const SELECT_OPERATOR = new GraphQLInputObjectType({ const SELECT_INPUT = new GraphQLInputObjectType({
name: 'SelectOperator', name: 'SelectInput',
description: 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: { fields: {
query: { query: {
description: 'This is the subquery to be executed.', description: 'This is the subquery to be executed.',
type: new GraphQLNonNull(SUBQUERY), type: new GraphQLNonNull(SUBQUERY_INPUT),
}, },
key: { key: {
description: description:
@@ -571,10 +571,10 @@ const SELECT_OPERATOR = new GraphQLInputObjectType({
}, },
}); });
const SEARCH_OPERATOR = new GraphQLInputObjectType({ const SEARCH_INPUT = new GraphQLInputObjectType({
name: 'SearchOperator', name: 'SearchInput',
description: 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: { fields: {
_term: { _term: {
description: 'This is the term to be searched.', description: 'This is the term to be searched.',
@@ -598,54 +598,54 @@ const SEARCH_OPERATOR = new GraphQLInputObjectType({
}, },
}); });
const TEXT_OPERATOR = new GraphQLInputObjectType({ const TEXT_INPUT = new GraphQLInputObjectType({
name: 'TextOperator', name: 'TextInput',
description: 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: { fields: {
_search: { _search: {
description: 'This is the search to be executed.', description: 'This is the search to be executed.',
type: new GraphQLNonNull(SEARCH_OPERATOR), type: new GraphQLNonNull(SEARCH_INPUT),
}, },
}, },
}); });
const BOX_OPERATOR = new GraphQLInputObjectType({ const BOX_INPUT = new GraphQLInputObjectType({
name: 'BoxOperator', name: 'BoxInput',
description: 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: { fields: {
bottomLeft: { bottomLeft: {
description: 'This is the bottom left coordinates of the box.', description: 'This is the bottom left coordinates of the box.',
type: new GraphQLNonNull(GEO_POINT), type: new GraphQLNonNull(GEO_POINT_INPUT),
}, },
upperRight: { upperRight: {
description: 'This is the upper right coordinates of the box.', 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({ const WITHIN_INPUT = new GraphQLInputObjectType({
name: 'WithinOperator', name: 'WithinInput',
description: 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: { fields: {
_box: { _box: {
description: 'This is the box to be specified.', description: 'This is the box to be specified.',
type: new GraphQLNonNull(BOX_OPERATOR), type: new GraphQLNonNull(BOX_INPUT),
}, },
}, },
}); });
const CENTER_SPHERE_OPERATOR = new GraphQLInputObjectType({ const CENTER_SPHERE_INPUT = new GraphQLInputObjectType({
name: 'CenterSphereOperator', name: 'CenterSphereInput',
description: 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: { fields: {
center: { center: {
description: 'This is the center of the sphere.', description: 'This is the center of the sphere.',
type: new GraphQLNonNull(GEO_POINT), type: new GraphQLNonNull(GEO_POINT_INPUT),
}, },
distance: { distance: {
description: 'This is the radius of the sphere.', description: 'This is the radius of the sphere.',
@@ -654,30 +654,30 @@ const CENTER_SPHERE_OPERATOR = new GraphQLInputObjectType({
}, },
}); });
const GEO_WITHIN_OPERATOR = new GraphQLInputObjectType({ const GEO_WITHIN_INPUT = new GraphQLInputObjectType({
name: 'GeoWithinOperator', name: 'GeoWithinInput',
description: 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: { fields: {
_polygon: { _polygon: {
description: 'This is the polygon to be specified.', description: 'This is the polygon to be specified.',
type: POLYGON, type: POLYGON_INPUT,
}, },
_centerSphere: { _centerSphere: {
description: 'This is the sphere to be specified.', description: 'This is the sphere to be specified.',
type: CENTER_SPHERE_OPERATOR, type: CENTER_SPHERE_INPUT,
}, },
}, },
}); });
const GEO_INTERSECTS = new GraphQLInputObjectType({ const GEO_INTERSECTS_INPUT = new GraphQLInputObjectType({
name: 'GeoIntersectsOperator', name: 'GeoIntersectsInput',
description: 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: { fields: {
_point: { _point: {
description: 'This is the point to be specified.', description: 'This is the point to be specified.',
type: GEO_POINT, type: GEO_POINT_INPUT,
}, },
}, },
}); });
@@ -739,13 +739,13 @@ const _exists = {
const _select = { const _select = {
description: 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.', '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 = { const _dontSelect = {
description: 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.', '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 = { const _regex = {
@@ -760,10 +760,10 @@ const _options = {
type: GraphQLString, type: GraphQLString,
}; };
const STRING_CONSTRAINT = new GraphQLInputObjectType({ const STRING_WHERE_INPUT = new GraphQLInputObjectType({
name: 'StringConstraint', name: 'StringWhereInput',
description: 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: { fields: {
_eq: _eq(GraphQLString), _eq: _eq(GraphQLString),
_ne: _ne(GraphQLString), _ne: _ne(GraphQLString),
@@ -781,15 +781,15 @@ const STRING_CONSTRAINT = new GraphQLInputObjectType({
_text: { _text: {
description: description:
'This is the $text operator to specify a full text search constraint.', 'This is the $text operator to specify a full text search constraint.',
type: TEXT_OPERATOR, type: TEXT_INPUT,
}, },
}, },
}); });
const NUMBER_CONSTRAINT = new GraphQLInputObjectType({ const NUMBER_WHERE_INPUT = new GraphQLInputObjectType({
name: 'NumberConstraint', name: 'NumberWhereInput',
description: 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: { fields: {
_eq: _eq(GraphQLFloat), _eq: _eq(GraphQLFloat),
_ne: _ne(GraphQLFloat), _ne: _ne(GraphQLFloat),
@@ -805,10 +805,10 @@ const NUMBER_CONSTRAINT = new GraphQLInputObjectType({
}, },
}); });
const BOOLEAN_CONSTRAINT = new GraphQLInputObjectType({ const BOOLEAN_WHERE_INPUT = new GraphQLInputObjectType({
name: 'BooleanConstraint', name: 'BooleanWhereInput',
description: 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: { fields: {
_eq: _eq(GraphQLBoolean), _eq: _eq(GraphQLBoolean),
_ne: _ne(GraphQLBoolean), _ne: _ne(GraphQLBoolean),
@@ -818,10 +818,10 @@ const BOOLEAN_CONSTRAINT = new GraphQLInputObjectType({
}, },
}); });
const ARRAY_CONSTRAINT = new GraphQLInputObjectType({ const ARRAY_WHERE_INPUT = new GraphQLInputObjectType({
name: 'ArrayConstraint', name: 'ArrayWhereInput',
description: 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: { fields: {
_eq: _eq(ANY), _eq: _eq(ANY),
_ne: _ne(ANY), _ne: _ne(ANY),
@@ -847,8 +847,8 @@ const ARRAY_CONSTRAINT = new GraphQLInputObjectType({
}, },
}); });
const KEY_VALUE = new GraphQLInputObjectType({ const KEY_VALUE_INPUT = new GraphQLInputObjectType({
name: 'KeyValue', name: 'KeyValueInput',
description: 'An entry from an object, i.e., a pair of key and value.', description: 'An entry from an object, i.e., a pair of key and value.',
fields: { fields: {
_key: { _key: {
@@ -862,29 +862,29 @@ const KEY_VALUE = new GraphQLInputObjectType({
}, },
}); });
const OBJECT_CONSTRAINT = new GraphQLInputObjectType({ const OBJECT_WHERE_INPUT = new GraphQLInputObjectType({
name: 'ObjectConstraint', name: 'ObjectWhereInput',
description: 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: { fields: {
_eq: _eq(KEY_VALUE), _eq: _eq(KEY_VALUE_INPUT),
_ne: _ne(KEY_VALUE), _ne: _ne(KEY_VALUE_INPUT),
_in: _in(KEY_VALUE), _in: _in(KEY_VALUE_INPUT),
_nin: _nin(KEY_VALUE), _nin: _nin(KEY_VALUE_INPUT),
_lt: _lt(KEY_VALUE), _lt: _lt(KEY_VALUE_INPUT),
_lte: _lte(KEY_VALUE), _lte: _lte(KEY_VALUE_INPUT),
_gt: _gt(KEY_VALUE), _gt: _gt(KEY_VALUE_INPUT),
_gte: _gte(KEY_VALUE), _gte: _gte(KEY_VALUE_INPUT),
_exists, _exists,
_select, _select,
_dontSelect, _dontSelect,
}, },
}); });
const DATE_CONSTRAINT = new GraphQLInputObjectType({ const DATE_WHERE_INPUT = new GraphQLInputObjectType({
name: 'DateConstraint', name: 'DateWhereInput',
description: 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: { fields: {
_eq: _eq(DATE), _eq: _eq(DATE),
_ne: _ne(DATE), _ne: _ne(DATE),
@@ -900,10 +900,10 @@ const DATE_CONSTRAINT = new GraphQLInputObjectType({
}, },
}); });
const BYTES_CONSTRAINT = new GraphQLInputObjectType({ const BYTES_WHERE_INPUT = new GraphQLInputObjectType({
name: 'BytesConstraint', name: 'BytesWhereInput',
description: 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: { fields: {
_eq: _eq(BYTES), _eq: _eq(BYTES),
_ne: _ne(BYTES), _ne: _ne(BYTES),
@@ -919,10 +919,10 @@ const BYTES_CONSTRAINT = new GraphQLInputObjectType({
}, },
}); });
const FILE_CONSTRAINT = new GraphQLInputObjectType({ const FILE_WHERE_INPUT = new GraphQLInputObjectType({
name: 'FileConstraint', name: 'FileWhereInput',
description: 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: { fields: {
_eq: _eq(FILE), _eq: _eq(FILE),
_ne: _ne(FILE), _ne: _ne(FILE),
@@ -940,16 +940,16 @@ const FILE_CONSTRAINT = new GraphQLInputObjectType({
}, },
}); });
const GEO_POINT_CONSTRAINT = new GraphQLInputObjectType({ const GEO_POINT_WHERE_INPUT = new GraphQLInputObjectType({
name: 'GeoPointConstraint', name: 'GeoPointWhereInput',
description: 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: { fields: {
_exists, _exists,
_nearSphere: { _nearSphere: {
description: 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.', '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: { _maxDistance: {
description: description:
@@ -974,26 +974,26 @@ const GEO_POINT_CONSTRAINT = new GraphQLInputObjectType({
_within: { _within: {
description: 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.', '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: { _geoWithin: {
description: 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.', '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({ const POLYGON_WHERE_INPUT = new GraphQLInputObjectType({
name: 'PolygonConstraint', name: 'PolygonWhereInput',
description: 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: { fields: {
_exists, _exists,
_geoIntersects: { _geoIntersects: {
description: 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.', '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 => { const load = parseGraphQLSchema => {
parseGraphQLSchema.graphQLTypes.push(GraphQLUpload); parseGraphQLSchema.addGraphQLType(GraphQLUpload, true);
parseGraphQLSchema.graphQLTypes.push(ANY); parseGraphQLSchema.addGraphQLType(ANY, true);
parseGraphQLSchema.graphQLTypes.push(OBJECT); parseGraphQLSchema.addGraphQLType(OBJECT, true);
parseGraphQLSchema.graphQLTypes.push(DATE); parseGraphQLSchema.addGraphQLType(DATE, true);
parseGraphQLSchema.graphQLTypes.push(BYTES); parseGraphQLSchema.addGraphQLType(BYTES, true);
parseGraphQLSchema.graphQLTypes.push(FILE); parseGraphQLSchema.addGraphQLType(FILE, true);
parseGraphQLSchema.graphQLTypes.push(FILE_INFO); parseGraphQLSchema.addGraphQLType(FILE_INFO, true);
parseGraphQLSchema.graphQLTypes.push(GEO_POINT); parseGraphQLSchema.addGraphQLType(GEO_POINT_INPUT, true);
parseGraphQLSchema.graphQLTypes.push(GEO_POINT_INFO); parseGraphQLSchema.addGraphQLType(GEO_POINT, true);
parseGraphQLSchema.graphQLTypes.push(RELATION_OP); parseGraphQLSchema.addGraphQLType(RELATION_OP, true);
parseGraphQLSchema.graphQLTypes.push(CREATE_RESULT); parseGraphQLSchema.addGraphQLType(CREATE_RESULT, true);
parseGraphQLSchema.graphQLTypes.push(UPDATE_RESULT); parseGraphQLSchema.addGraphQLType(UPDATE_RESULT, true);
parseGraphQLSchema.graphQLTypes.push(CLASS); parseGraphQLSchema.addGraphQLType(CLASS, true);
parseGraphQLSchema.graphQLTypes.push(READ_PREFERENCE); parseGraphQLSchema.addGraphQLType(READ_PREFERENCE, true);
parseGraphQLSchema.graphQLTypes.push(SUBQUERY); parseGraphQLSchema.addGraphQLType(SUBQUERY_INPUT, true);
parseGraphQLSchema.graphQLTypes.push(SELECT_OPERATOR); parseGraphQLSchema.addGraphQLType(SELECT_INPUT, true);
parseGraphQLSchema.graphQLTypes.push(SEARCH_OPERATOR); parseGraphQLSchema.addGraphQLType(SEARCH_INPUT, true);
parseGraphQLSchema.graphQLTypes.push(TEXT_OPERATOR); parseGraphQLSchema.addGraphQLType(TEXT_INPUT, true);
parseGraphQLSchema.graphQLTypes.push(BOX_OPERATOR); parseGraphQLSchema.addGraphQLType(BOX_INPUT, true);
parseGraphQLSchema.graphQLTypes.push(WITHIN_OPERATOR); parseGraphQLSchema.addGraphQLType(WITHIN_INPUT, true);
parseGraphQLSchema.graphQLTypes.push(CENTER_SPHERE_OPERATOR); parseGraphQLSchema.addGraphQLType(CENTER_SPHERE_INPUT, true);
parseGraphQLSchema.graphQLTypes.push(GEO_WITHIN_OPERATOR); parseGraphQLSchema.addGraphQLType(GEO_WITHIN_INPUT, true);
parseGraphQLSchema.graphQLTypes.push(GEO_INTERSECTS); parseGraphQLSchema.addGraphQLType(GEO_INTERSECTS_INPUT, true);
parseGraphQLSchema.graphQLTypes.push(STRING_CONSTRAINT); parseGraphQLSchema.addGraphQLType(STRING_WHERE_INPUT, true);
parseGraphQLSchema.graphQLTypes.push(NUMBER_CONSTRAINT); parseGraphQLSchema.addGraphQLType(NUMBER_WHERE_INPUT, true);
parseGraphQLSchema.graphQLTypes.push(BOOLEAN_CONSTRAINT); parseGraphQLSchema.addGraphQLType(BOOLEAN_WHERE_INPUT, true);
parseGraphQLSchema.graphQLTypes.push(ARRAY_CONSTRAINT); parseGraphQLSchema.addGraphQLType(ARRAY_WHERE_INPUT, true);
parseGraphQLSchema.graphQLTypes.push(OBJECT_CONSTRAINT); parseGraphQLSchema.addGraphQLType(KEY_VALUE_INPUT, true);
parseGraphQLSchema.graphQLTypes.push(DATE_CONSTRAINT); parseGraphQLSchema.addGraphQLType(OBJECT_WHERE_INPUT, true);
parseGraphQLSchema.graphQLTypes.push(BYTES_CONSTRAINT); parseGraphQLSchema.addGraphQLType(DATE_WHERE_INPUT, true);
parseGraphQLSchema.graphQLTypes.push(FILE_CONSTRAINT); parseGraphQLSchema.addGraphQLType(BYTES_WHERE_INPUT, true);
parseGraphQLSchema.graphQLTypes.push(GEO_POINT_CONSTRAINT); parseGraphQLSchema.addGraphQLType(FILE_WHERE_INPUT, true);
parseGraphQLSchema.graphQLTypes.push(POLYGON_CONSTRAINT); parseGraphQLSchema.addGraphQLType(GEO_POINT_WHERE_INPUT, true);
parseGraphQLSchema.graphQLTypes.push(FIND_RESULT); parseGraphQLSchema.addGraphQLType(POLYGON_WHERE_INPUT, true);
parseGraphQLSchema.graphQLTypes.push(SIGN_UP_RESULT); parseGraphQLSchema.addGraphQLType(FIND_RESULT, true);
parseGraphQLSchema.graphQLTypes.push(ELEMENT); parseGraphQLSchema.addGraphQLType(SIGN_UP_RESULT, true);
parseGraphQLSchema.addGraphQLType(ELEMENT, true);
}; };
export { export {
@@ -1128,10 +1129,10 @@ export {
FILE, FILE,
FILE_INFO, FILE_INFO,
GEO_POINT_FIELDS, GEO_POINT_FIELDS,
GEO_POINT_INPUT,
GEO_POINT, GEO_POINT,
GEO_POINT_INFO, POLYGON_INPUT,
POLYGON, POLYGON,
POLYGON_INFO,
RELATION_OP, RELATION_OP,
CLASS_NAME_ATT, CLASS_NAME_ATT,
FIELDS_ATT, FIELDS_ATT,
@@ -1157,15 +1158,15 @@ export {
SKIP_ATT, SKIP_ATT,
LIMIT_ATT, LIMIT_ATT,
COUNT_ATT, COUNT_ATT,
SUBQUERY, SUBQUERY_INPUT,
SELECT_OPERATOR, SELECT_INPUT,
SEARCH_OPERATOR, SEARCH_INPUT,
TEXT_OPERATOR, TEXT_INPUT,
BOX_OPERATOR, BOX_INPUT,
WITHIN_OPERATOR, WITHIN_INPUT,
CENTER_SPHERE_OPERATOR, CENTER_SPHERE_INPUT,
GEO_WITHIN_OPERATOR, GEO_WITHIN_INPUT,
GEO_INTERSECTS, GEO_INTERSECTS_INPUT,
_eq, _eq,
_ne, _ne,
_lt, _lt,
@@ -1179,16 +1180,17 @@ export {
_dontSelect, _dontSelect,
_regex, _regex,
_options, _options,
STRING_CONSTRAINT, STRING_WHERE_INPUT,
NUMBER_CONSTRAINT, NUMBER_WHERE_INPUT,
BOOLEAN_CONSTRAINT, BOOLEAN_WHERE_INPUT,
ARRAY_CONSTRAINT, ARRAY_WHERE_INPUT,
OBJECT_CONSTRAINT, KEY_VALUE_INPUT,
DATE_CONSTRAINT, OBJECT_WHERE_INPUT,
BYTES_CONSTRAINT, DATE_WHERE_INPUT,
FILE_CONSTRAINT, BYTES_WHERE_INPUT,
GEO_POINT_CONSTRAINT, FILE_WHERE_INPUT,
POLYGON_CONSTRAINT, GEO_POINT_WHERE_INPUT,
POLYGON_WHERE_INPUT,
FIND_RESULT, FIND_RESULT,
SIGN_UP_RESULT, SIGN_UP_RESULT,
ARRAY_RESULT, ARRAY_RESULT,

View File

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

View File

@@ -1,33 +1,14 @@
import { GraphQLNonNull, GraphQLBoolean, GraphQLObjectType } from 'graphql'; import { GraphQLNonNull, GraphQLBoolean, GraphQLObjectType } from 'graphql';
import * as defaultGraphQLTypes from './defaultGraphQLTypes'; import * as defaultGraphQLTypes from './defaultGraphQLTypes';
import rest from '../../rest'; import rest from '../../rest';
import { transformMutationInputToParse } from '../transformers/mutation';
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);
}
});
};
const createObject = async (className, fields, config, auth, info) => { const createObject = async (className, fields, config, auth, info) => {
if (!fields) { if (!fields) {
fields = {}; fields = {};
} }
transformToParse(fields); transformMutationInputToParse(fields);
return (await rest.create(config, auth, className, fields, info.clientSDK)) return (await rest.create(config, auth, className, fields, info.clientSDK))
.response; .response;
@@ -45,7 +26,7 @@ const updateObject = async (
fields = {}; fields = {};
} }
transformToParse(fields); transformMutationInputToParse(fields);
return (await rest.update( return (await rest.update(
config, config,
@@ -63,80 +44,95 @@ const deleteObject = async (className, objectId, config, auth, info) => {
}; };
const load = parseGraphQLSchema => { const load = parseGraphQLSchema => {
parseGraphQLSchema.graphQLObjectsMutations.create = { parseGraphQLSchema.addGraphQLObjectMutation(
description: 'create',
'The create mutation can be used to create a new object of a certain class.', {
args: { description:
className: defaultGraphQLTypes.CLASS_NAME_ATT, 'The create mutation can be used to create a new object of a certain class.',
fields: defaultGraphQLTypes.FIELDS_ATT, args: {
}, className: defaultGraphQLTypes.CLASS_NAME_ATT,
type: new GraphQLNonNull(defaultGraphQLTypes.CREATE_RESULT), fields: defaultGraphQLTypes.FIELDS_ATT,
async resolve(_source, args, context) { },
try { type: new GraphQLNonNull(defaultGraphQLTypes.CREATE_RESULT),
const { className, fields } = args; async resolve(_source, args, context) {
const { config, auth, info } = context; try {
const { className, fields } = args;
const { config, auth, info } = context;
return await createObject(className, fields, config, auth, info); return await createObject(className, fields, config, auth, info);
} catch (e) { } catch (e) {
parseGraphQLSchema.handleError(e); parseGraphQLSchema.handleError(e);
} }
},
}, },
}; true,
true
);
parseGraphQLSchema.graphQLObjectsMutations.update = { parseGraphQLSchema.addGraphQLObjectMutation(
description: 'update',
'The update mutation can be used to update an object of a certain class.', {
args: { description:
className: defaultGraphQLTypes.CLASS_NAME_ATT, 'The update mutation can be used to update an object of a certain class.',
objectId: defaultGraphQLTypes.OBJECT_ID_ATT, args: {
fields: defaultGraphQLTypes.FIELDS_ATT, className: defaultGraphQLTypes.CLASS_NAME_ATT,
}, objectId: defaultGraphQLTypes.OBJECT_ID_ATT,
type: new GraphQLNonNull(defaultGraphQLTypes.UPDATE_RESULT), fields: defaultGraphQLTypes.FIELDS_ATT,
async resolve(_source, args, context) { },
try { type: new GraphQLNonNull(defaultGraphQLTypes.UPDATE_RESULT),
const { className, objectId, fields } = args; async resolve(_source, args, context) {
const { config, auth, info } = context; try {
const { className, objectId, fields } = args;
const { config, auth, info } = context;
return await updateObject( return await updateObject(
className, className,
objectId, objectId,
fields, fields,
config, config,
auth, auth,
info info
); );
} catch (e) { } catch (e) {
parseGraphQLSchema.handleError(e); parseGraphQLSchema.handleError(e);
} }
},
}, },
}; true,
true
);
parseGraphQLSchema.graphQLObjectsMutations.delete = { parseGraphQLSchema.addGraphQLObjectMutation(
description: 'delete',
'The delete mutation can be used to delete an object of a certain class.', {
args: { description:
className: defaultGraphQLTypes.CLASS_NAME_ATT, 'The delete mutation can be used to delete an object of a certain class.',
objectId: defaultGraphQLTypes.OBJECT_ID_ATT, args: {
}, className: defaultGraphQLTypes.CLASS_NAME_ATT,
type: new GraphQLNonNull(GraphQLBoolean), objectId: defaultGraphQLTypes.OBJECT_ID_ATT,
async resolve(_source, args, context) { },
try { type: new GraphQLNonNull(GraphQLBoolean),
const { className, objectId } = args; async resolve(_source, args, context) {
const { config, auth, info } = context; try {
const { className, objectId } = args;
const { config, auth, info } = context;
return await deleteObject(className, objectId, config, auth, info); return await deleteObject(className, objectId, config, auth, info);
} catch (e) { } catch (e) {
parseGraphQLSchema.handleError(e); parseGraphQLSchema.handleError(e);
} }
},
}, },
}; true,
true
);
const objectsMutation = new GraphQLObjectType({ const objectsMutation = new GraphQLObjectType({
name: 'ObjectsMutation', name: 'ObjectsMutation',
description: 'ObjectsMutation is the top level type for objects mutations.', description: 'ObjectsMutation is the top level type for objects mutations.',
fields: parseGraphQLSchema.graphQLObjectsMutations, fields: parseGraphQLSchema.graphQLObjectsMutations,
}); });
parseGraphQLSchema.graphQLTypes.push(objectsMutation); parseGraphQLSchema.addGraphQLType(objectsMutation, true, true);
parseGraphQLSchema.graphQLMutations.objects = { parseGraphQLSchema.graphQLMutations.objects = {
description: 'This is the top level for objects mutations.', 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 Parse from 'parse/node';
import * as defaultGraphQLTypes from './defaultGraphQLTypes'; import * as defaultGraphQLTypes from './defaultGraphQLTypes';
import rest from '../../rest'; import rest from '../../rest';
import { transformQueryInputToParse } from '../transformers/query';
const getObject = async ( const getObject = async (
className, className,
@@ -54,155 +55,6 @@ const getObject = async (
return response.results[0]; 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 ( const findObjects = async (
className, className,
where, where,
@@ -224,7 +76,7 @@ const findObjects = async (
where = {}; where = {};
} }
transformToParse(where); transformQueryInputToParse(where);
const options = {}; const options = {};
@@ -282,118 +134,131 @@ const findObjects = async (
}; };
const load = parseGraphQLSchema => { const load = parseGraphQLSchema => {
parseGraphQLSchema.graphQLObjectsQueries.get = { parseGraphQLSchema.addGraphQLObjectQuery(
description: 'get',
'The get query can be used to get an object of a certain class by its objectId.', {
args: { description:
className: defaultGraphQLTypes.CLASS_NAME_ATT, 'The get query can be used to get an object of a certain class by its objectId.',
objectId: defaultGraphQLTypes.OBJECT_ID_ATT, args: {
keys: defaultGraphQLTypes.KEYS_ATT, className: defaultGraphQLTypes.CLASS_NAME_ATT,
include: defaultGraphQLTypes.INCLUDE_ATT, objectId: defaultGraphQLTypes.OBJECT_ID_ATT,
readPreference: defaultGraphQLTypes.READ_PREFERENCE_ATT, keys: defaultGraphQLTypes.KEYS_ATT,
includeReadPreference: defaultGraphQLTypes.INCLUDE_READ_PREFERENCE_ATT, include: defaultGraphQLTypes.INCLUDE_ATT,
}, readPreference: defaultGraphQLTypes.READ_PREFERENCE_ATT,
type: new GraphQLNonNull(defaultGraphQLTypes.OBJECT), includeReadPreference: defaultGraphQLTypes.INCLUDE_READ_PREFERENCE_ATT,
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, type: new GraphQLNonNull(defaultGraphQLTypes.OBJECT),
limit: defaultGraphQLTypes.LIMIT_ATT, async resolve(_source, args, context) {
keys: defaultGraphQLTypes.KEYS_ATT, try {
include: defaultGraphQLTypes.INCLUDE_ATT, const {
includeAll: { className,
description: 'All pointers will be returned', objectId,
type: GraphQLBoolean, keys,
}, include,
readPreference: defaultGraphQLTypes.READ_PREFERENCE_ATT, readPreference,
includeReadPreference: defaultGraphQLTypes.INCLUDE_READ_PREFERENCE_ATT, includeReadPreference,
subqueryReadPreference: defaultGraphQLTypes.SUBQUERY_READ_PREFERENCE_ATT, } = args;
},
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( const { config, auth, info } = context;
className,
where, return await getObject(
order, className,
skip, objectId,
limit, keys,
keys, include,
include, readPreference,
includeAll, includeReadPreference,
readPreference, config,
includeReadPreference, auth,
subqueryReadPreference, info
config, );
auth, } catch (e) {
info, parseGraphQLSchema.handleError(e);
selectedFields }
); },
} 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({ const objectsQuery = new GraphQLObjectType({
name: 'ObjectsQuery', name: 'ObjectsQuery',
description: 'ObjectsQuery is the top level type for objects queries.', description: 'ObjectsQuery is the top level type for objects queries.',
fields: parseGraphQLSchema.graphQLObjectsQueries, fields: parseGraphQLSchema.graphQLObjectsQueries,
}); });
parseGraphQLSchema.graphQLTypes.push(objectsQuery); parseGraphQLSchema.addGraphQLType(objectsQuery, true, true);
parseGraphQLSchema.graphQLQueries.objects = { parseGraphQLSchema.graphQLQueries.objects = {
description: 'This is the top level for objects queries.', 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 objectsMutations from './objectsMutations';
import * as objectsQueries from './objectsQueries'; import * as objectsQueries from './objectsQueries';
import { ParseGraphQLClassConfig } from '../../Controllers/ParseGraphQLController'; import { ParseGraphQLClassConfig } from '../../Controllers/ParseGraphQLController';
import { transformClassNameToGraphQL } from '../transformers/className';
const getParseClassMutationConfig = function( const getParseClassMutationConfig = function(
parseClassConfig: ?ParseGraphQLClassConfig parseClassConfig: ?ParseGraphQLClassConfig
@@ -39,7 +40,9 @@ const load = function(
parseClass, parseClass,
parseClassConfig: ?ParseGraphQLClassConfig parseClassConfig: ?ParseGraphQLClassConfig
) { ) {
const { className } = parseClass; const className = parseClass.className;
const graphQLClassName = transformClassNameToGraphQL(className);
const { const {
create: isCreateEnabled = true, create: isCreateEnabled = true,
update: isUpdateEnabled = true, update: isUpdateEnabled = true,
@@ -52,37 +55,29 @@ const load = function(
classGraphQLOutputType, classGraphQLOutputType,
} = parseGraphQLSchema.parseClassTypes[className]; } = 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) => { const transformTypes = (inputType: 'create' | 'update', fields) => {
if (fields) { if (fields) {
const classGraphQLCreateTypeFields =
isCreateEnabled && classGraphQLCreateType
? classGraphQLCreateType.getFields()
: null;
const classGraphQLUpdateTypeFields =
isUpdateEnabled && classGraphQLUpdateType
? classGraphQLUpdateType.getFields()
: null;
Object.keys(fields).forEach(field => { Object.keys(fields).forEach(field => {
let inputTypeField; let inputTypeField;
if (inputType === 'create') { if (inputType === 'create' && classGraphQLCreateTypeFields) {
inputTypeField = classGraphQLCreateTypeFields[field]; inputTypeField = classGraphQLCreateTypeFields[field];
} else { } else if (classGraphQLUpdateTypeFields) {
inputTypeField = classGraphQLUpdateTypeFields[field]; inputTypeField = classGraphQLUpdateTypeFields[field];
} }
if (inputTypeField) { if (inputTypeField) {
switch (inputTypeField.type) { switch (inputTypeField.type) {
case defaultGraphQLTypes.GEO_POINT: case defaultGraphQLTypes.GEO_POINT_INPUT:
fields[field].__type = 'GeoPoint'; fields[field].__type = 'GeoPoint';
break; break;
case defaultGraphQLTypes.POLYGON: case defaultGraphQLTypes.POLYGON_INPUT:
fields[field] = { fields[field] = {
__type: 'Polygon', __type: 'Polygon',
coordinates: fields[field].map(geoPoint => [ coordinates: fields[field].map(geoPoint => [
@@ -98,13 +93,18 @@ const load = function(
}; };
if (isCreateEnabled) { if (isCreateEnabled) {
const createGraphQLMutationName = `create${className}`; const createGraphQLMutationName = `create${graphQLClassName}`;
parseGraphQLSchema.graphQLObjectsMutations[createGraphQLMutationName] = { parseGraphQLSchema.addGraphQLObjectMutation(createGraphQLMutationName, {
description: `The ${createGraphQLMutationName} mutation can be used to create a new object of the ${className} class.`, description: `The ${createGraphQLMutationName} mutation can be used to create a new object of the ${graphQLClassName} class.`,
args: { 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) { async resolve(_source, args, context, mutationInfo) {
try { try {
let { fields } = args; let { fields } = args;
@@ -150,18 +150,23 @@ const load = function(
parseGraphQLSchema.handleError(e); parseGraphQLSchema.handleError(e);
} }
}, },
}; });
} }
if (isUpdateEnabled) { if (isUpdateEnabled) {
const updateGraphQLMutationName = `update${className}`; const updateGraphQLMutationName = `update${graphQLClassName}`;
parseGraphQLSchema.graphQLObjectsMutations[updateGraphQLMutationName] = { parseGraphQLSchema.addGraphQLObjectMutation(updateGraphQLMutationName, {
description: `The ${updateGraphQLMutationName} mutation can be used to update an object of the ${className} class.`, description: `The ${updateGraphQLMutationName} mutation can be used to update an object of the ${graphQLClassName} class.`,
args: { args: {
objectId: defaultGraphQLTypes.OBJECT_ID_ATT, 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) { async resolve(_source, args, context, mutationInfo) {
try { try {
const { objectId, fields } = args; const { objectId, fields } = args;
@@ -205,17 +210,19 @@ const load = function(
parseGraphQLSchema.handleError(e); parseGraphQLSchema.handleError(e);
} }
}, },
}; });
} }
if (isDestroyEnabled) { if (isDestroyEnabled) {
const deleteGraphQLMutationName = `delete${className}`; const deleteGraphQLMutationName = `delete${graphQLClassName}`;
parseGraphQLSchema.graphQLObjectsMutations[deleteGraphQLMutationName] = { parseGraphQLSchema.addGraphQLObjectMutation(deleteGraphQLMutationName, {
description: `The ${deleteGraphQLMutationName} mutation can be used to delete an object of the ${className} class.`, description: `The ${deleteGraphQLMutationName} mutation can be used to delete an object of the ${graphQLClassName} class.`,
args: { args: {
objectId: defaultGraphQLTypes.OBJECT_ID_ATT, objectId: defaultGraphQLTypes.OBJECT_ID_ATT,
}, },
type: new GraphQLNonNull(classGraphQLOutputType), type: new GraphQLNonNull(
classGraphQLOutputType || defaultGraphQLTypes.OBJECT
),
async resolve(_source, args, context, mutationInfo) { async resolve(_source, args, context, mutationInfo) {
try { try {
const { objectId } = args; const { objectId } = args;
@@ -250,7 +257,7 @@ const load = function(
parseGraphQLSchema.handleError(e); parseGraphQLSchema.handleError(e);
} }
}, },
}; });
} }
}; };

View File

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

View File

@@ -14,6 +14,7 @@ import getFieldNames from 'graphql-list-fields';
import * as defaultGraphQLTypes from './defaultGraphQLTypes'; import * as defaultGraphQLTypes from './defaultGraphQLTypes';
import * as objectsQueries from './objectsQueries'; import * as objectsQueries from './objectsQueries';
import { ParseGraphQLClassConfig } from '../../Controllers/ParseGraphQLController'; import { ParseGraphQLClassConfig } from '../../Controllers/ParseGraphQLController';
import { transformClassNameToGraphQL } from '../transformers/className';
import { extractKeysAndInclude } from '../parseGraphQLUtils'; import { extractKeysAndInclude } from '../parseGraphQLUtils';
const mapInputType = (parseType, targetClass, parseClassTypes) => { const mapInputType = (parseType, targetClass, parseClassTypes) => {
@@ -31,13 +32,19 @@ const mapInputType = (parseType, targetClass, parseClassTypes) => {
case 'Date': case 'Date':
return defaultGraphQLTypes.DATE; return defaultGraphQLTypes.DATE;
case 'Pointer': case 'Pointer':
if (parseClassTypes[targetClass]) { if (
parseClassTypes[targetClass] &&
parseClassTypes[targetClass].classGraphQLScalarType
) {
return parseClassTypes[targetClass].classGraphQLScalarType; return parseClassTypes[targetClass].classGraphQLScalarType;
} else { } else {
return defaultGraphQLTypes.OBJECT; return defaultGraphQLTypes.OBJECT;
} }
case 'Relation': case 'Relation':
if (parseClassTypes[targetClass]) { if (
parseClassTypes[targetClass] &&
parseClassTypes[targetClass].classGraphQLRelationOpType
) {
return parseClassTypes[targetClass].classGraphQLRelationOpType; return parseClassTypes[targetClass].classGraphQLRelationOpType;
} else { } else {
return defaultGraphQLTypes.OBJECT; return defaultGraphQLTypes.OBJECT;
@@ -45,9 +52,9 @@ const mapInputType = (parseType, targetClass, parseClassTypes) => {
case 'File': case 'File':
return defaultGraphQLTypes.FILE; return defaultGraphQLTypes.FILE;
case 'GeoPoint': case 'GeoPoint':
return defaultGraphQLTypes.GEO_POINT; return defaultGraphQLTypes.GEO_POINT_INPUT;
case 'Polygon': case 'Polygon':
return defaultGraphQLTypes.POLYGON; return defaultGraphQLTypes.POLYGON_INPUT;
case 'Bytes': case 'Bytes':
return defaultGraphQLTypes.BYTES; return defaultGraphQLTypes.BYTES;
case 'ACL': case 'ACL':
@@ -72,13 +79,19 @@ const mapOutputType = (parseType, targetClass, parseClassTypes) => {
case 'Date': case 'Date':
return defaultGraphQLTypes.DATE; return defaultGraphQLTypes.DATE;
case 'Pointer': case 'Pointer':
if (parseClassTypes[targetClass]) { if (
parseClassTypes[targetClass] &&
parseClassTypes[targetClass].classGraphQLOutputType
) {
return parseClassTypes[targetClass].classGraphQLOutputType; return parseClassTypes[targetClass].classGraphQLOutputType;
} else { } else {
return defaultGraphQLTypes.OBJECT; return defaultGraphQLTypes.OBJECT;
} }
case 'Relation': case 'Relation':
if (parseClassTypes[targetClass]) { if (
parseClassTypes[targetClass] &&
parseClassTypes[targetClass].classGraphQLFindResultType
) {
return new GraphQLNonNull( return new GraphQLNonNull(
parseClassTypes[targetClass].classGraphQLFindResultType parseClassTypes[targetClass].classGraphQLFindResultType
); );
@@ -88,9 +101,9 @@ const mapOutputType = (parseType, targetClass, parseClassTypes) => {
case 'File': case 'File':
return defaultGraphQLTypes.FILE_INFO; return defaultGraphQLTypes.FILE_INFO;
case 'GeoPoint': case 'GeoPoint':
return defaultGraphQLTypes.GEO_POINT_INFO; return defaultGraphQLTypes.GEO_POINT;
case 'Polygon': case 'Polygon':
return defaultGraphQLTypes.POLYGON_INFO; return defaultGraphQLTypes.POLYGON;
case 'Bytes': case 'Bytes':
return defaultGraphQLTypes.BYTES; return defaultGraphQLTypes.BYTES;
case 'ACL': case 'ACL':
@@ -103,33 +116,36 @@ const mapOutputType = (parseType, targetClass, parseClassTypes) => {
const mapConstraintType = (parseType, targetClass, parseClassTypes) => { const mapConstraintType = (parseType, targetClass, parseClassTypes) => {
switch (parseType) { switch (parseType) {
case 'String': case 'String':
return defaultGraphQLTypes.STRING_CONSTRAINT; return defaultGraphQLTypes.STRING_WHERE_INPUT;
case 'Number': case 'Number':
return defaultGraphQLTypes.NUMBER_CONSTRAINT; return defaultGraphQLTypes.NUMBER_WHERE_INPUT;
case 'Boolean': case 'Boolean':
return defaultGraphQLTypes.BOOLEAN_CONSTRAINT; return defaultGraphQLTypes.BOOLEAN_WHERE_INPUT;
case 'Array': case 'Array':
return defaultGraphQLTypes.ARRAY_CONSTRAINT; return defaultGraphQLTypes.ARRAY_WHERE_INPUT;
case 'Object': case 'Object':
return defaultGraphQLTypes.OBJECT_CONSTRAINT; return defaultGraphQLTypes.OBJECT_WHERE_INPUT;
case 'Date': case 'Date':
return defaultGraphQLTypes.DATE_CONSTRAINT; return defaultGraphQLTypes.DATE_WHERE_INPUT;
case 'Pointer': case 'Pointer':
if (parseClassTypes[targetClass]) { if (
parseClassTypes[targetClass] &&
parseClassTypes[targetClass].classGraphQLConstraintType
) {
return parseClassTypes[targetClass].classGraphQLConstraintType; return parseClassTypes[targetClass].classGraphQLConstraintType;
} else { } else {
return defaultGraphQLTypes.OBJECT; return defaultGraphQLTypes.OBJECT;
} }
case 'File': case 'File':
return defaultGraphQLTypes.FILE_CONSTRAINT; return defaultGraphQLTypes.FILE_WHERE_INPUT;
case 'GeoPoint': case 'GeoPoint':
return defaultGraphQLTypes.GEO_POINT_CONSTRAINT; return defaultGraphQLTypes.GEO_POINT_WHERE_INPUT;
case 'Polygon': case 'Polygon':
return defaultGraphQLTypes.POLYGON_CONSTRAINT; return defaultGraphQLTypes.POLYGON_WHERE_INPUT;
case 'Bytes': case 'Bytes':
return defaultGraphQLTypes.BYTES_CONSTRAINT; return defaultGraphQLTypes.BYTES_WHERE_INPUT;
case 'ACL': case 'ACL':
return defaultGraphQLTypes.OBJECT_CONSTRAINT; return defaultGraphQLTypes.OBJECT_WHERE_INPUT;
case 'Relation': case 'Relation':
default: default:
return undefined; return undefined;
@@ -233,7 +249,8 @@ const load = (
parseClass, parseClass,
parseClassConfig: ?ParseGraphQLClassConfig parseClassConfig: ?ParseGraphQLClassConfig
) => { ) => {
const { className } = parseClass; const className = parseClass.className;
const graphQLClassName = transformClassNameToGraphQL(className);
const { const {
classCreateFields, classCreateFields,
classUpdateFields, classUpdateFields,
@@ -242,12 +259,12 @@ const load = (
classSortFields, classSortFields,
} = getInputFieldsAndConstraints(parseClass, parseClassConfig); } = getInputFieldsAndConstraints(parseClass, parseClassConfig);
const classGraphQLScalarTypeName = `${className}Pointer`; const classGraphQLScalarTypeName = `${graphQLClassName}Pointer`;
const parseScalarValue = value => { const parseScalarValue = value => {
if (typeof value === 'string') { if (typeof value === 'string') {
return { return {
__type: 'Pointer', __type: 'Pointer',
className, className: className,
objectId: value, objectId: value,
}; };
} else if ( } else if (
@@ -256,7 +273,7 @@ const load = (
value.className === className && value.className === className &&
typeof value.objectId === 'string' typeof value.objectId === 'string'
) { ) {
return value; return { ...value, className };
} }
throw new defaultGraphQLTypes.TypeValidationError( throw new defaultGraphQLTypes.TypeValidationError(
@@ -264,9 +281,9 @@ const load = (
classGraphQLScalarTypeName classGraphQLScalarTypeName
); );
}; };
const classGraphQLScalarType = new GraphQLScalarType({ let classGraphQLScalarType = new GraphQLScalarType({
name: classGraphQLScalarTypeName, 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, parseValue: parseScalarValue,
serialize(value) { serialize(value) {
if (typeof value === 'string') { 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 classGraphQLRelationOpTypeName = `${graphQLClassName}RelationOpInput`;
const classGraphQLRelationOpType = new GraphQLInputObjectType({ let classGraphQLRelationOpType = new GraphQLInputObjectType({
name: classGraphQLRelationOpTypeName, 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: () => ({ fields: () => ({
_op: { _op: {
description: 'This is the operation to be executed.', 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 classGraphQLCreateTypeName = `Create${graphQLClassName}FieldsInput`;
const classGraphQLCreateType = new GraphQLInputObjectType({ let classGraphQLCreateType = new GraphQLInputObjectType({
name: classGraphQLCreateTypeName, 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: () => fields: () =>
classCreateFields.reduce( classCreateFields.reduce(
(fields, field) => { (fields, field) => {
@@ -372,12 +393,14 @@ const load = (
} }
), ),
}); });
parseGraphQLSchema.graphQLTypes.push(classGraphQLCreateType); classGraphQLCreateType = parseGraphQLSchema.addGraphQLType(
classGraphQLCreateType
);
const classGraphQLUpdateTypeName = `${className}UpdateFields`; const classGraphQLUpdateTypeName = `Update${graphQLClassName}FieldsInput`;
const classGraphQLUpdateType = new GraphQLInputObjectType({ let classGraphQLUpdateType = new GraphQLInputObjectType({
name: classGraphQLUpdateTypeName, 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: () => fields: () =>
classUpdateFields.reduce( classUpdateFields.reduce(
(fields, field) => { (fields, field) => {
@@ -403,12 +426,14 @@ const load = (
} }
), ),
}); });
parseGraphQLSchema.graphQLTypes.push(classGraphQLUpdateType); classGraphQLUpdateType = parseGraphQLSchema.addGraphQLType(
classGraphQLUpdateType
);
const classGraphQLConstraintTypeName = `${className}PointerConstraint`; const classGraphQLConstraintTypeName = `${graphQLClassName}PointerWhereInput`;
const classGraphQLConstraintType = new GraphQLInputObjectType({ let classGraphQLConstraintType = new GraphQLInputObjectType({
name: classGraphQLConstraintTypeName, 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: { fields: {
_eq: defaultGraphQLTypes._eq(classGraphQLScalarType), _eq: defaultGraphQLTypes._eq(classGraphQLScalarType),
_ne: defaultGraphQLTypes._ne(classGraphQLScalarType), _ne: defaultGraphQLTypes._ne(classGraphQLScalarType),
@@ -420,21 +445,23 @@ const load = (
_inQuery: { _inQuery: {
description: 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.', '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: { _notInQuery: {
description: 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.', '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 classGraphQLConstraintsTypeName = `${graphQLClassName}WhereInput`;
const classGraphQLConstraintsType = new GraphQLInputObjectType({ let classGraphQLConstraintsType = new GraphQLInputObjectType({
name: classGraphQLConstraintsTypeName, 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: () => ({ fields: () => ({
...classConstraintFields.reduce((fields, field) => { ...classConstraintFields.reduce((fields, field) => {
const type = mapConstraintType( const type = mapConstraintType(
@@ -468,12 +495,14 @@ const load = (
}, },
}), }),
}); });
parseGraphQLSchema.graphQLTypes.push(classGraphQLConstraintsType); classGraphQLConstraintsType =
parseGraphQLSchema.addGraphQLType(classGraphQLConstraintsType) ||
defaultGraphQLTypes.OBJECT;
const classGraphQLOrderTypeName = `${className}Order`; const classGraphQLOrderTypeName = `${graphQLClassName}Order`;
const classGraphQLOrderType = new GraphQLEnumType({ let classGraphQLOrderType = new GraphQLEnumType({
name: classGraphQLOrderTypeName, 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) => { values: classSortFields.reduce((sortFields, fieldConfig) => {
const { field, asc, desc } = fieldConfig; const { field, asc, desc } = fieldConfig;
const updatedSortFields = { const updatedSortFields = {
@@ -488,7 +517,9 @@ const load = (
return updatedSortFields; return updatedSortFields;
}, {}), }, {}),
}); });
parseGraphQLSchema.graphQLTypes.push(classGraphQLOrderType); classGraphQLOrderType = parseGraphQLSchema.addGraphQLType(
classGraphQLOrderType
);
const classGraphQLFindArgs = { const classGraphQLFindArgs = {
where: { where: {
@@ -498,7 +529,9 @@ const load = (
}, },
order: { order: {
description: 'The fields to be used when sorting the data fetched.', 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, skip: defaultGraphQLTypes.SKIP_ATT,
limit: defaultGraphQLTypes.LIMIT_ATT, limit: defaultGraphQLTypes.LIMIT_ATT,
@@ -507,7 +540,7 @@ const load = (
subqueryReadPreference: defaultGraphQLTypes.SUBQUERY_READ_PREFERENCE_ATT, subqueryReadPreference: defaultGraphQLTypes.SUBQUERY_READ_PREFERENCE_ATT,
}; };
const classGraphQLOutputTypeName = `${className}Class`; const classGraphQLOutputTypeName = `${graphQLClassName}`;
const outputFields = () => { const outputFields = () => {
return classOutputFields.reduce((fields, field) => { return classOutputFields.reduce((fields, field) => {
const type = mapOutputType( const type = mapOutputType(
@@ -548,14 +581,13 @@ const load = (
.filter(field => field.includes('.')) .filter(field => field.includes('.'))
.map(field => field.slice(field.indexOf('.') + 1)) .map(field => field.slice(field.indexOf('.') + 1))
); );
return await objectsQueries.findObjects( return await objectsQueries.findObjects(
source[field].className, source[field].className,
{ {
_relatedTo: { _relatedTo: {
object: { object: {
__type: 'Pointer', __type: 'Pointer',
className, className: className,
objectId: source.objectId, objectId: source.objectId,
}, },
key: field, key: field,
@@ -634,29 +666,37 @@ const load = (
} }
}, defaultGraphQLTypes.CLASS_FIELDS); }, defaultGraphQLTypes.CLASS_FIELDS);
}; };
const classGraphQLOutputType = new GraphQLObjectType({ let classGraphQLOutputType = new GraphQLObjectType({
name: classGraphQLOutputTypeName, 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], interfaces: [defaultGraphQLTypes.CLASS],
fields: outputFields, fields: outputFields,
}); });
parseGraphQLSchema.graphQLTypes.push(classGraphQLOutputType); classGraphQLOutputType = parseGraphQLSchema.addGraphQLType(
classGraphQLOutputType
);
const classGraphQLFindResultTypeName = `${className}FindResult`; const classGraphQLFindResultTypeName = `${graphQLClassName}FindResult`;
const classGraphQLFindResultType = new GraphQLObjectType({ let classGraphQLFindResultType = new GraphQLObjectType({
name: classGraphQLFindResultTypeName, 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: { fields: {
results: { results: {
description: 'This is the objects returned by the query', description: 'This is the objects returned by the query',
type: new GraphQLNonNull( type: new GraphQLNonNull(
new GraphQLList(new GraphQLNonNull(classGraphQLOutputType)) new GraphQLList(
new GraphQLNonNull(
classGraphQLOutputType || defaultGraphQLTypes.OBJECT
)
)
), ),
}, },
count: defaultGraphQLTypes.COUNT_ATT, count: defaultGraphQLTypes.COUNT_ATT,
}, },
}); });
parseGraphQLSchema.graphQLTypes.push(classGraphQLFindResultType); classGraphQLFindResultType = parseGraphQLSchema.addGraphQLType(
classGraphQLFindResultType
);
parseGraphQLSchema.parseClassTypes[className] = { parseGraphQLSchema.parseClassTypes[className] = {
classGraphQLScalarType, classGraphQLScalarType,
@@ -671,22 +711,22 @@ const load = (
}; };
if (className === '_User') { if (className === '_User') {
const meType = new GraphQLObjectType({ const viewerType = new GraphQLObjectType({
name: 'Me', name: 'Viewer',
description: `The Me object type is used in operations that involve outputting the current user data.`, description: `The Viewer object type is used in operations that involve outputting the current user data.`,
interfaces: [defaultGraphQLTypes.CLASS], interfaces: [defaultGraphQLTypes.CLASS],
fields: () => ({ fields: () => ({
...outputFields(), ...outputFields(),
sessionToken: defaultGraphQLTypes.SESSION_TOKEN_ATT, sessionToken: defaultGraphQLTypes.SESSION_TOKEN_ATT,
}), }),
}); });
parseGraphQLSchema.meType = meType; parseGraphQLSchema.viewerType = viewerType;
parseGraphQLSchema.graphQLTypes.push(meType); parseGraphQLSchema.addGraphQLType(viewerType, true, true);
const userSignUpInputTypeName = '_UserSignUpFields'; const userSignUpInputTypeName = 'SignUpFieldsInput';
const userSignUpInputType = new GraphQLInputObjectType({ const userSignUpInputType = new GraphQLInputObjectType({
name: userSignUpInputTypeName, 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: () => fields: () =>
classCreateFields.reduce((fields, field) => { classCreateFields.reduce((fields, field) => {
const type = mapInputType( const type = mapInputType(
@@ -710,8 +750,9 @@ const load = (
} }
}, {}), }, {}),
}); });
parseGraphQLSchema.addGraphQLType(userSignUpInputType, true, true);
const userLogInInputTypeName = '_UserLoginFields'; const userLogInInputTypeName = 'LogInFieldsInput';
const userLogInInputType = new GraphQLInputObjectType({ const userLogInInputType = new GraphQLInputObjectType({
name: userLogInInputTypeName, name: userLogInInputTypeName,
description: `The ${userLogInInputTypeName} input type is used to login.`, description: `The ${userLogInInputTypeName} input type is used to login.`,
@@ -726,13 +767,14 @@ const load = (
}, },
}, },
}); });
parseGraphQLSchema.addGraphQLType(userLogInInputType, true, true);
parseGraphQLSchema.parseClassTypes[ parseGraphQLSchema.parseClassTypes[
'_User' className
].signUpInputType = userSignUpInputType; ].signUpInputType = userSignUpInputType;
parseGraphQLSchema.parseClassTypes[ parseGraphQLSchema.parseClassTypes[
'_User' className
].logInInputType = userLogInInputType; ].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 UsersRouter from '../../Routers/UsersRouter';
import * as objectsMutations from './objectsMutations'; import * as objectsMutations from './objectsMutations';
import { getUserFromSessionToken } from './usersQueries'; import { getUserFromSessionToken } from './usersQueries';
@@ -19,10 +19,11 @@ const load = parseGraphQLSchema => {
type: parseGraphQLSchema.parseClassTypes['_User'].signUpInputType, type: parseGraphQLSchema.parseClassTypes['_User'].signUpInputType,
}, },
}, },
type: new GraphQLNonNull(parseGraphQLSchema.meType), type: new GraphQLNonNull(parseGraphQLSchema.viewerType),
async resolve(_source, args, context, mutationInfo) { async resolve(_source, args, context, mutationInfo) {
try { try {
const { fields } = args; const { fields } = args;
const { config, auth, info } = context; const { config, auth, info } = context;
const { sessionToken } = await objectsMutations.createObject( const { sessionToken } = await objectsMutations.createObject(
@@ -45,16 +46,16 @@ const load = parseGraphQLSchema => {
fields.logIn = { fields.logIn = {
description: 'The logIn mutation can be used to log the user in.', description: 'The logIn mutation can be used to log the user in.',
args: { args: {
input: { fields: {
description: 'This is data needed to login', description: 'This is data needed to login',
type: parseGraphQLSchema.parseClassTypes['_User'].logInInputType, type: parseGraphQLSchema.parseClassTypes['_User'].logInInputType,
}, },
}, },
type: new GraphQLNonNull(parseGraphQLSchema.meType), type: new GraphQLNonNull(parseGraphQLSchema.viewerType),
async resolve(_source, args, context) { async resolve(_source, args, context) {
try { try {
const { const {
input: { username, password }, fields: { username, password },
} = args; } = args;
const { config, auth, info } = context; const { config, auth, info } = context;
@@ -76,17 +77,24 @@ const load = parseGraphQLSchema => {
fields.logOut = { fields.logOut = {
description: 'The logOut mutation can be used to log the user out.', description: 'The logOut mutation can be used to log the user out.',
type: new GraphQLNonNull(GraphQLBoolean), type: new GraphQLNonNull(parseGraphQLSchema.viewerType),
async resolve(_source, _args, context) { async resolve(_source, _args, context, mutationInfo) {
try { try {
const { config, auth, info } = context; const { config, auth, info } = context;
const viewer = await getUserFromSessionToken(
config,
info,
mutationInfo
);
await usersRouter.handleLogOut({ await usersRouter.handleLogOut({
config, config,
auth, auth,
info, info,
}); });
return true;
return viewer;
} catch (e) { } catch (e) {
parseGraphQLSchema.handleError(e); parseGraphQLSchema.handleError(e);
} }
@@ -98,7 +106,7 @@ const load = parseGraphQLSchema => {
description: 'UsersMutation is the top level type for files mutations.', description: 'UsersMutation is the top level type for files mutations.',
fields, fields,
}); });
parseGraphQLSchema.graphQLTypes.push(usersMutation); parseGraphQLSchema.addGraphQLType(usersMutation, true, true);
parseGraphQLSchema.graphQLMutations.users = { parseGraphQLSchema.graphQLMutations.users = {
description: 'This is the top level for users mutations.', description: 'This is the top level for users mutations.',

View File

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