GraphQL schema operations (#5993)

* Remove nested operations

* Improve error log

* Fix bug schema to load

* Fix ParseGraphQLSchema tests

* Fix tests

* Fix failing tests

* First verstion not complete of create class mutation

* Fix bug caused by circular dependency

* Renaming files

* Schema types should be loaded before parse classes

* Fix tests

* Create class mutation boilerplate

* Improve CreateClassSchemaInput fields names

* Remove fields

* Pointer and relation fields

* Improve pointer default type

* Class type

* Create class mutation resolver

* Schema field transformers

* Class types transformations

* First test

* Numbers test

* Boolean tests

* Date test

* Fix some get tests

* Test for created at and updated at

* File tests

* Test for objects

* Renaming reducerFabric to reducerGenerator

* Changing get tests for file and object

* Object composed queries test

* Array test

* Null field test

* Bytes test

* Geo Point test

* Polygons tests

* Remove create generic mutation

* Fix tests

* Create class test - isRequired and defaultValue will be added back later

* Enforce master key

* Fix tests

* Duplicated field test

* updateClass mutation

* Remove update generic mutation tests

* Remove update generic mutation

* deleteClass mutation

* Remove delete generic mutation tests

* Remove delete generic mutation

* class query

* Classes query

* Remove get generic query from tests

* Remove remaining generic operations and fix tests

* Fix last test

* Try to fix redis tests

* Fix postgres tests

* Update objectsMutations and objectsQueries files locations

* Rename classSchema files to schema files

* Rename ClassObject to ParseObject

* Fix names and paths

* Still some wrong names
This commit is contained in:
Antonio Davi Macedo Coelho de Castro
2019-09-01 22:11:03 -07:00
committed by GitHub
parent e404c43222
commit 5a482bd661
24 changed files with 3604 additions and 2147 deletions

View File

@@ -270,13 +270,13 @@ describe('ParseGraphQLSchema', () => {
warn: message => {
logged = true;
expect(message).toEqual(
'Query get could not be added to the auto schema because it collided with an existing field.'
'Query viewer could not be added to the auto schema because it collided with an existing field.'
);
},
},
});
await parseGraphQLSchema.load();
expect(parseGraphQLSchema.addGraphQLQuery('get', {})).toBeUndefined();
expect(parseGraphQLSchema.addGraphQLQuery('viewer', {})).toBeUndefined();
expect(logged).toBeTruthy();
});
@@ -291,12 +291,12 @@ describe('ParseGraphQLSchema', () => {
},
});
await parseGraphQLSchema.load();
delete parseGraphQLSchema.graphQLQueries.get;
delete parseGraphQLSchema.graphQLQueries.viewer;
const field = {};
expect(parseGraphQLSchema.addGraphQLQuery('get', field, true, true)).toBe(
field
);
expect(parseGraphQLSchema.graphQLQueries['get']).toBe(field);
expect(
parseGraphQLSchema.addGraphQLQuery('viewer', field, true, true)
).toBe(field);
expect(parseGraphQLSchema.graphQLQueries['viewer']).toBe(field);
});
});
@@ -363,14 +363,14 @@ describe('ParseGraphQLSchema', () => {
warn: message => {
logged = true;
expect(message).toEqual(
'Mutation create could not be added to the auto schema because it collided with an existing field.'
'Mutation signUp could not be added to the auto schema because it collided with an existing field.'
);
},
},
});
await parseGraphQLSchema.load();
expect(
parseGraphQLSchema.addGraphQLMutation('create', {})
parseGraphQLSchema.addGraphQLMutation('signUp', {})
).toBeUndefined();
expect(logged).toBeTruthy();
});
@@ -386,12 +386,12 @@ describe('ParseGraphQLSchema', () => {
},
});
await parseGraphQLSchema.load();
delete parseGraphQLSchema.graphQLMutations.create;
delete parseGraphQLSchema.graphQLMutations.signUp;
const field = {};
expect(
parseGraphQLSchema.addGraphQLMutation('create', field, true, true)
parseGraphQLSchema.addGraphQLMutation('signUp', field, true, true)
).toBe(field);
expect(parseGraphQLSchema.graphQLMutations['create']).toBe(field);
expect(parseGraphQLSchema.graphQLMutations['signUp']).toBe(field);
});
});

File diff suppressed because it is too large Load Diff

View File

@@ -431,7 +431,7 @@ describe('schemas', () => {
defaultValue: false,
},
defaultZero: { type: 'Number', defaultValue: 0 },
relation: { type: 'Relation', targetClass: 'SomeClass' }
relation: { type: 'Relation', targetClass: 'SomeClass' },
},
},
}).then(async response => {
@@ -458,7 +458,7 @@ describe('schemas', () => {
defaultValue: false,
},
defaultZero: { type: 'Number', defaultValue: 0 },
relation: { type: 'Relation', targetClass: 'SomeClass' }
relation: { type: 'Relation', targetClass: 'SomeClass' },
},
classLevelPermissions: defaultClassLevelPermissions,
});
@@ -486,7 +486,7 @@ describe('schemas', () => {
});
});
it('try to set a relation field as a required field', async (done) => {
it('try to set a relation field as a required field', async done => {
try {
await request({
url: 'http://localhost:8378/1/schemas',
@@ -497,7 +497,11 @@ describe('schemas', () => {
className: 'NewClassWithRelationRequired',
fields: {
foo: { type: 'String' },
relation: { type: 'Relation', targetClass: 'SomeClass', required: true }
relation: {
type: 'Relation',
targetClass: 'SomeClass',
required: true,
},
},
},
});
@@ -508,7 +512,7 @@ describe('schemas', () => {
done();
});
it('try to set a relation field with a default value', async (done) => {
it('try to set a relation field with a default value', async done => {
try {
await request({
url: 'http://localhost:8378/1/schemas',
@@ -519,7 +523,11 @@ describe('schemas', () => {
className: 'NewClassRelationWithOptions',
fields: {
foo: { type: 'String' },
relation: { type: 'Relation', targetClass: 'SomeClass', defaultValue: { __type: 'Relation', className: '_User' } }
relation: {
type: 'Relation',
targetClass: 'SomeClass',
defaultValue: { __type: 'Relation', className: '_User' },
},
},
},
});
@@ -530,7 +538,7 @@ describe('schemas', () => {
done();
});
it('try to update schemas with a relation field with options', async (done) => {
it('try to update schemas with a relation field with options', async done => {
await request({
url: 'http://localhost:8378/1/schemas',
method: 'POST',
@@ -539,7 +547,7 @@ describe('schemas', () => {
body: {
className: 'NewClassRelationWithOptions',
fields: {
foo: { type: 'String' }
foo: { type: 'String' },
},
},
});
@@ -552,10 +560,14 @@ describe('schemas', () => {
body: {
className: 'NewClassRelationWithOptions',
fields: {
relation: { type: 'Relation', targetClass: 'SomeClass', required: true }
relation: {
type: 'Relation',
targetClass: 'SomeClass',
required: true,
},
},
_method: "PUT"
}
_method: 'PUT',
},
});
fail('should fail');
} catch (e) {
@@ -571,10 +583,14 @@ describe('schemas', () => {
body: {
className: 'NewClassRelationWithOptions',
fields: {
relation: { type: 'Relation', targetClass: 'SomeClass', defaultValue: { __type: 'Relation', className: '_User' } }
relation: {
type: 'Relation',
targetClass: 'SomeClass',
defaultValue: { __type: 'Relation', className: '_User' },
},
},
_method: "PUT"
}
_method: 'PUT',
},
});
fail('should fail');
} catch (e) {

View File

@@ -14,6 +14,7 @@ import ParseGraphQLController, {
import DatabaseController from '../Controllers/DatabaseController';
import { toGraphQLError } from './parseGraphQLUtils';
import * as schemaDirectives from './loaders/schemaDirectives';
import * as schemaTypes from './loaders/schemaTypes';
const RESERVED_GRAPHQL_TYPE_NAMES = [
'String',
@@ -29,16 +30,16 @@ const RESERVED_GRAPHQL_TYPE_NAMES = [
'SignUpFieldsInput',
'LogInFieldsInput',
];
const RESERVED_GRAPHQL_QUERY_NAMES = ['health', 'viewer', 'get', 'find'];
const RESERVED_GRAPHQL_QUERY_NAMES = ['health', 'viewer', 'class', 'classes'];
const RESERVED_GRAPHQL_MUTATION_NAMES = [
'signUp',
'logIn',
'logOut',
'createFile',
'callCloudCode',
'create',
'update',
'delete',
'createClass',
'updateClass',
'deleteClass',
];
class ParseGraphQLSchema {
@@ -97,6 +98,7 @@ class ParseGraphQLSchema {
this.graphQLSchemaDirectives = {};
defaultGraphQLTypes.load(this);
schemaTypes.load(this);
this._getParseClassesWithConfig(parseClasses, parseGraphQLConfig).forEach(
([parseClass, parseClassConfig]) => {

View File

@@ -0,0 +1,39 @@
import rest from '../../rest';
const createObject = async (className, fields, config, auth, info) => {
if (!fields) {
fields = {};
}
return (await rest.create(config, auth, className, fields, info.clientSDK))
.response;
};
const updateObject = async (
className,
objectId,
fields,
config,
auth,
info
) => {
if (!fields) {
fields = {};
}
return (await rest.update(
config,
auth,
className,
{ objectId },
fields,
info.clientSDK
)).response;
};
const deleteObject = async (className, objectId, config, auth, info) => {
await rest.del(config, auth, className, objectId, info.clientSDK);
return true;
};
export { createObject, updateObject, deleteObject };

View File

@@ -0,0 +1,127 @@
import Parse from 'parse/node';
import rest from '../../rest';
import { transformQueryInputToParse } from '../transformers/query';
const getObject = async (
className,
objectId,
keys,
include,
readPreference,
includeReadPreference,
config,
auth,
info
) => {
const options = {};
if (keys) {
options.keys = keys;
}
if (include) {
options.include = include;
if (includeReadPreference) {
options.includeReadPreference = includeReadPreference;
}
}
if (readPreference) {
options.readPreference = readPreference;
}
const response = await rest.get(
config,
auth,
className,
objectId,
options,
info.clientSDK
);
if (!response.results || response.results.length == 0) {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Object not found.');
}
const object = response.results[0];
if (className === '_User') {
delete object.sessionToken;
}
return object;
};
const findObjects = async (
className,
where,
order,
skip,
limit,
keys,
include,
includeAll,
readPreference,
includeReadPreference,
subqueryReadPreference,
config,
auth,
info,
selectedFields
) => {
if (!where) {
where = {};
}
transformQueryInputToParse(where);
const options = {};
if (selectedFields.includes('results')) {
if (limit || limit === 0) {
options.limit = limit;
}
if (options.limit !== 0) {
if (order) {
options.order = order;
}
if (skip) {
options.skip = skip;
}
if (config.maxLimit && options.limit > config.maxLimit) {
// Silently replace the limit on the query with the max configured
options.limit = config.maxLimit;
}
if (keys) {
options.keys = keys;
}
if (includeAll === true) {
options.includeAll = includeAll;
}
if (!options.includeAll && include) {
options.include = include;
}
if ((options.includeAll || options.include) && includeReadPreference) {
options.includeReadPreference = includeReadPreference;
}
}
} else {
options.limit = 0;
}
if (selectedFields.includes('count')) {
options.count = true;
}
if (readPreference) {
options.readPreference = readPreference;
}
if (Object.keys(where).length > 0 && subqueryReadPreference) {
options.subqueryReadPreference = subqueryReadPreference;
}
return await rest.find(
config,
auth,
className,
where,
options,
info.clientSDK
);
};
export { getObject, findObjects };

View File

@@ -1,13 +1,13 @@
import * as objectsMutations from './objectsMutations';
import * as filesMutations from './filesMutations';
import * as usersMutations from './usersMutations';
import * as functionsMutations from './functionsMutations';
import * as schemaMutations from './schemaMutations';
const load = parseGraphQLSchema => {
objectsMutations.load(parseGraphQLSchema);
filesMutations.load(parseGraphQLSchema);
usersMutations.load(parseGraphQLSchema);
functionsMutations.load(parseGraphQLSchema);
schemaMutations.load(parseGraphQLSchema);
};
export { load };

View File

@@ -1,6 +1,6 @@
import { GraphQLNonNull, GraphQLBoolean } from 'graphql';
import * as objectsQueries from './objectsQueries';
import * as usersQueries from './usersQueries';
import * as schemaQueries from './schemaQueries';
const load = parseGraphQLSchema => {
parseGraphQLSchema.addGraphQLQuery(
@@ -15,8 +15,8 @@ const load = parseGraphQLSchema => {
true
);
objectsQueries.load(parseGraphQLSchema);
usersQueries.load(parseGraphQLSchema);
schemaQueries.load(parseGraphQLSchema);
};
export { load };

View File

@@ -455,17 +455,17 @@ const UPDATE_RESULT = new GraphQLObjectType({
fields: UPDATE_RESULT_FIELDS,
});
const CLASS_FIELDS = {
const PARSE_OBJECT_FIELDS = {
...CREATE_RESULT_FIELDS,
...UPDATE_RESULT_FIELDS,
...INPUT_FIELDS,
};
const CLASS = new GraphQLInterfaceType({
name: 'Class',
const PARSE_OBJECT = new GraphQLInterfaceType({
name: 'ParseObject',
description:
'The Class interface type is used as a base type for the auto generated class types.',
fields: CLASS_FIELDS,
'The ParseObject interface type is used as a base type for the auto generated object types.',
fields: PARSE_OBJECT_FIELDS,
});
const SESSION_TOKEN_ATT = {
@@ -1074,7 +1074,7 @@ const load = parseGraphQLSchema => {
parseGraphQLSchema.addGraphQLType(GEO_POINT, true);
parseGraphQLSchema.addGraphQLType(CREATE_RESULT, true);
parseGraphQLSchema.addGraphQLType(UPDATE_RESULT, true);
parseGraphQLSchema.addGraphQLType(CLASS, true);
parseGraphQLSchema.addGraphQLType(PARSE_OBJECT, true);
parseGraphQLSchema.addGraphQLType(READ_PREFERENCE, true);
parseGraphQLSchema.addGraphQLType(SUBQUERY_INPUT, true);
parseGraphQLSchema.addGraphQLType(SELECT_INPUT, true);
@@ -1137,8 +1137,8 @@ export {
CREATE_RESULT,
UPDATE_RESULT_FIELDS,
UPDATE_RESULT,
CLASS_FIELDS,
CLASS,
PARSE_OBJECT_FIELDS,
PARSE_OBJECT,
SESSION_TOKEN_ATT,
KEYS_ATT,
INCLUDE_ATT,

View File

@@ -1,127 +0,0 @@
import { GraphQLNonNull, GraphQLBoolean } from 'graphql';
import * as defaultGraphQLTypes from './defaultGraphQLTypes';
import rest from '../../rest';
const createObject = async (className, fields, config, auth, info) => {
if (!fields) {
fields = {};
}
return (await rest.create(config, auth, className, fields, info.clientSDK))
.response;
};
const updateObject = async (
className,
objectId,
fields,
config,
auth,
info
) => {
if (!fields) {
fields = {};
}
return (await rest.update(
config,
auth,
className,
{ objectId },
fields,
info.clientSDK
)).response;
};
const deleteObject = async (className, objectId, config, auth, info) => {
await rest.del(config, auth, className, objectId, info.clientSDK);
return true;
};
const load = parseGraphQLSchema => {
parseGraphQLSchema.addGraphQLMutation(
'create',
{
description:
'The create mutation can be used to create a new object of a certain class.',
args: {
className: defaultGraphQLTypes.CLASS_NAME_ATT,
fields: defaultGraphQLTypes.FIELDS_ATT,
},
type: new GraphQLNonNull(defaultGraphQLTypes.CREATE_RESULT),
async resolve(_source, args, context) {
try {
const { className, fields } = args;
const { config, auth, info } = context;
const object = await createObject(
className,
fields,
config,
auth,
info
);
return object;
} catch (e) {
parseGraphQLSchema.handleError(e);
}
},
},
true,
true
);
parseGraphQLSchema.addGraphQLMutation(
'update',
{
description:
'The update mutation can be used to update an object of a certain class.',
args: {
className: defaultGraphQLTypes.CLASS_NAME_ATT,
id: defaultGraphQLTypes.OBJECT_ID_ATT,
fields: defaultGraphQLTypes.FIELDS_ATT,
},
type: new GraphQLNonNull(defaultGraphQLTypes.UPDATE_RESULT),
async resolve(_source, args, context) {
try {
const { className, id, fields } = args;
const { config, auth, info } = context;
return await updateObject(className, id, fields, config, auth, info);
} catch (e) {
parseGraphQLSchema.handleError(e);
}
},
},
true,
true
);
parseGraphQLSchema.addGraphQLMutation(
'delete',
{
description:
'The delete mutation can be used to delete an object of a certain class.',
args: {
className: defaultGraphQLTypes.CLASS_NAME_ATT,
id: defaultGraphQLTypes.OBJECT_ID_ATT,
},
type: new GraphQLNonNull(GraphQLBoolean),
async resolve(_source, args, context) {
try {
const { className, id } = args;
const { config, auth, info } = context;
return await deleteObject(className, id, config, auth, info);
} catch (e) {
parseGraphQLSchema.handleError(e);
}
},
},
true,
true
);
};
export { createObject, updateObject, deleteObject, load };

View File

@@ -1,260 +0,0 @@
import { GraphQLNonNull, GraphQLBoolean, GraphQLString } from 'graphql';
import getFieldNames from 'graphql-list-fields';
import Parse from 'parse/node';
import * as defaultGraphQLTypes from './defaultGraphQLTypes';
import rest from '../../rest';
import { transformQueryInputToParse } from '../transformers/query';
const getObject = async (
className,
objectId,
keys,
include,
readPreference,
includeReadPreference,
config,
auth,
info
) => {
const options = {};
if (keys) {
options.keys = keys;
}
if (include) {
options.include = include;
if (includeReadPreference) {
options.includeReadPreference = includeReadPreference;
}
}
if (readPreference) {
options.readPreference = readPreference;
}
const response = await rest.get(
config,
auth,
className,
objectId,
options,
info.clientSDK
);
if (!response.results || response.results.length == 0) {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Object not found.');
}
const object = response.results[0];
if (className === '_User') {
delete object.sessionToken;
}
return object;
};
const findObjects = async (
className,
where,
order,
skip,
limit,
keys,
include,
includeAll,
readPreference,
includeReadPreference,
subqueryReadPreference,
config,
auth,
info,
selectedFields
) => {
if (!where) {
where = {};
}
transformQueryInputToParse(where);
const options = {};
if (selectedFields.includes('results')) {
if (limit || limit === 0) {
options.limit = limit;
}
if (options.limit !== 0) {
if (order) {
options.order = order;
}
if (skip) {
options.skip = skip;
}
if (config.maxLimit && options.limit > config.maxLimit) {
// Silently replace the limit on the query with the max configured
options.limit = config.maxLimit;
}
if (keys) {
options.keys = keys;
}
if (includeAll === true) {
options.includeAll = includeAll;
}
if (!options.includeAll && include) {
options.include = include;
}
if ((options.includeAll || options.include) && includeReadPreference) {
options.includeReadPreference = includeReadPreference;
}
}
} else {
options.limit = 0;
}
if (selectedFields.includes('count')) {
options.count = true;
}
if (readPreference) {
options.readPreference = readPreference;
}
if (Object.keys(where).length > 0 && subqueryReadPreference) {
options.subqueryReadPreference = subqueryReadPreference;
}
return await rest.find(
config,
auth,
className,
where,
options,
info.clientSDK
);
};
const load = parseGraphQLSchema => {
parseGraphQLSchema.addGraphQLQuery(
'get',
{
description:
'The get query can be used to get an object of a certain class by its objectId.',
args: {
className: defaultGraphQLTypes.CLASS_NAME_ATT,
id: defaultGraphQLTypes.OBJECT_ID_ATT,
keys: defaultGraphQLTypes.KEYS_ATT,
include: defaultGraphQLTypes.INCLUDE_ATT,
readPreference: defaultGraphQLTypes.READ_PREFERENCE_ATT,
includeReadPreference: defaultGraphQLTypes.INCLUDE_READ_PREFERENCE_ATT,
},
type: new GraphQLNonNull(defaultGraphQLTypes.OBJECT),
async resolve(_source, args, context) {
try {
const {
className,
id,
keys,
include,
readPreference,
includeReadPreference,
} = args;
const { config, auth, info } = context;
const object = await getObject(
className,
id,
keys,
include,
readPreference,
includeReadPreference,
config,
auth,
info
);
object.id = object.objectId;
delete object.objectId;
return object;
} catch (e) {
parseGraphQLSchema.handleError(e);
}
},
},
true,
true
);
parseGraphQLSchema.addGraphQLQuery(
'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);
const objects = await findObjects(
className,
where,
order,
skip,
limit,
keys,
include,
includeAll,
readPreference,
includeReadPreference,
subqueryReadPreference,
config,
auth,
info,
selectedFields
);
objects.results.forEach(obj => {
obj.id = obj.objectId;
delete obj.objectId;
});
return objects;
} catch (e) {
parseGraphQLSchema.handleError(e);
}
},
},
true,
true
);
};
export { getObject, findObjects, load };

View File

@@ -5,8 +5,8 @@ import {
extractKeysAndInclude,
getParseClassMutationConfig,
} from '../parseGraphQLUtils';
import * as objectsMutations from './objectsMutations';
import * as objectsQueries from './objectsQueries';
import * as objectsMutations from '../helpers/objectsMutations';
import * as objectsQueries from '../helpers/objectsQueries';
import { ParseGraphQLClassConfig } from '../../Controllers/ParseGraphQLController';
import { transformClassNameToGraphQL } from '../transformers/className';
import { transformTypes } from '../transformers/mutation';

View File

@@ -2,7 +2,7 @@ import { GraphQLNonNull } from 'graphql';
import getFieldNames from 'graphql-list-fields';
import pluralize from 'pluralize';
import * as defaultGraphQLTypes from './defaultGraphQLTypes';
import * as objectsQueries from './objectsQueries';
import * as objectsQueries from '../helpers/objectsQueries';
import { ParseGraphQLClassConfig } from '../../Controllers/ParseGraphQLController';
import { transformClassNameToGraphQL } from '../transformers/className';
import { extractKeysAndInclude } from '../parseGraphQLUtils';

View File

@@ -3,8 +3,6 @@ import {
GraphQLID,
GraphQLObjectType,
GraphQLString,
GraphQLFloat,
GraphQLBoolean,
GraphQLList,
GraphQLInputObjectType,
GraphQLNonNull,
@@ -13,149 +11,17 @@ import {
} from 'graphql';
import getFieldNames from 'graphql-list-fields';
import * as defaultGraphQLTypes from './defaultGraphQLTypes';
import * as objectsQueries from './objectsQueries';
import * as objectsQueries from '../helpers/objectsQueries';
import { ParseGraphQLClassConfig } from '../../Controllers/ParseGraphQLController';
import { transformClassNameToGraphQL } from '../transformers/className';
import { transformInputTypeToGraphQL } from '../transformers/inputType';
import { transformOutputTypeToGraphQL } from '../transformers/outputType';
import { transformConstraintTypeToGraphQL } from '../transformers/constraintType';
import {
extractKeysAndInclude,
getParseClassMutationConfig,
} from '../parseGraphQLUtils';
const mapInputType = (parseType, targetClass, parseClassTypes) => {
switch (parseType) {
case 'String':
return GraphQLString;
case 'Number':
return GraphQLFloat;
case 'Boolean':
return GraphQLBoolean;
case 'Array':
return new GraphQLList(defaultGraphQLTypes.ANY);
case 'Object':
return defaultGraphQLTypes.OBJECT;
case 'Date':
return defaultGraphQLTypes.DATE;
case 'Pointer':
if (
parseClassTypes[targetClass] &&
parseClassTypes[targetClass].classGraphQLPointerType
) {
return parseClassTypes[targetClass].classGraphQLPointerType;
} else {
return defaultGraphQLTypes.OBJECT;
}
case 'Relation':
if (
parseClassTypes[targetClass] &&
parseClassTypes[targetClass].classGraphQLRelationType
) {
return parseClassTypes[targetClass].classGraphQLRelationType;
} else {
return defaultGraphQLTypes.OBJECT;
}
case 'File':
return defaultGraphQLTypes.FILE;
case 'GeoPoint':
return defaultGraphQLTypes.GEO_POINT_INPUT;
case 'Polygon':
return defaultGraphQLTypes.POLYGON_INPUT;
case 'Bytes':
return defaultGraphQLTypes.BYTES;
case 'ACL':
return defaultGraphQLTypes.OBJECT;
default:
return undefined;
}
};
const mapOutputType = (parseType, targetClass, parseClassTypes) => {
switch (parseType) {
case 'String':
return GraphQLString;
case 'Number':
return GraphQLFloat;
case 'Boolean':
return GraphQLBoolean;
case 'Array':
return new GraphQLList(defaultGraphQLTypes.ARRAY_RESULT);
case 'Object':
return defaultGraphQLTypes.OBJECT;
case 'Date':
return defaultGraphQLTypes.DATE;
case 'Pointer':
if (
parseClassTypes[targetClass] &&
parseClassTypes[targetClass].classGraphQLOutputType
) {
return parseClassTypes[targetClass].classGraphQLOutputType;
} else {
return defaultGraphQLTypes.OBJECT;
}
case 'Relation':
if (
parseClassTypes[targetClass] &&
parseClassTypes[targetClass].classGraphQLFindResultType
) {
return new GraphQLNonNull(
parseClassTypes[targetClass].classGraphQLFindResultType
);
} else {
return new GraphQLNonNull(defaultGraphQLTypes.FIND_RESULT);
}
case 'File':
return defaultGraphQLTypes.FILE_INFO;
case 'GeoPoint':
return defaultGraphQLTypes.GEO_POINT;
case 'Polygon':
return defaultGraphQLTypes.POLYGON;
case 'Bytes':
return defaultGraphQLTypes.BYTES;
case 'ACL':
return defaultGraphQLTypes.OBJECT;
default:
return undefined;
}
};
const mapConstraintType = (parseType, targetClass, parseClassTypes) => {
switch (parseType) {
case 'String':
return defaultGraphQLTypes.STRING_WHERE_INPUT;
case 'Number':
return defaultGraphQLTypes.NUMBER_WHERE_INPUT;
case 'Boolean':
return defaultGraphQLTypes.BOOLEAN_WHERE_INPUT;
case 'Array':
return defaultGraphQLTypes.ARRAY_WHERE_INPUT;
case 'Object':
return defaultGraphQLTypes.OBJECT_WHERE_INPUT;
case 'Date':
return defaultGraphQLTypes.DATE_WHERE_INPUT;
case 'Pointer':
if (
parseClassTypes[targetClass] &&
parseClassTypes[targetClass].classGraphQLConstraintType
) {
return parseClassTypes[targetClass].classGraphQLConstraintType;
} else {
return defaultGraphQLTypes.OBJECT;
}
case 'File':
return defaultGraphQLTypes.FILE_WHERE_INPUT;
case 'GeoPoint':
return defaultGraphQLTypes.GEO_POINT_WHERE_INPUT;
case 'Polygon':
return defaultGraphQLTypes.POLYGON_WHERE_INPUT;
case 'Bytes':
return defaultGraphQLTypes.BYTES_WHERE_INPUT;
case 'ACL':
return defaultGraphQLTypes.OBJECT_WHERE_INPUT;
case 'Relation':
default:
return undefined;
}
};
const getParseClassTypeConfig = function(
parseClassConfig: ?ParseGraphQLClassConfig
) {
@@ -184,7 +50,9 @@ const getInputFieldsAndConstraints = function(
// All allowed customs fields
const classCustomFields = classFields.filter(field => {
return !Object.keys(defaultGraphQLTypes.CLASS_FIELDS).includes(field);
return !Object.keys(defaultGraphQLTypes.PARSE_OBJECT_FIELDS).includes(
field
);
});
if (allowedInputFields && allowedInputFields.create) {
@@ -357,7 +225,7 @@ const load = (
fields: () =>
classCreateFields.reduce(
(fields, field) => {
const type = mapInputType(
const type = transformInputTypeToGraphQL(
parseClass.fields[field].type,
parseClass.fields[field].targetClass,
parseGraphQLSchema.parseClassTypes
@@ -390,7 +258,7 @@ const load = (
fields: () =>
classUpdateFields.reduce(
(fields, field) => {
const type = mapInputType(
const type = transformInputTypeToGraphQL(
parseClass.fields[field].type,
parseClass.fields[field].targetClass,
parseGraphQLSchema.parseClassTypes
@@ -503,7 +371,7 @@ const load = (
fields: () => ({
...classConstraintFields.reduce((fields, field) => {
const parseField = field === 'id' ? 'objectId' : field;
const type = mapConstraintType(
const type = transformConstraintTypeToGraphQL(
parseClass.fields[parseField].type,
parseClass.fields[parseField].targetClass,
parseGraphQLSchema.parseClassTypes
@@ -582,7 +450,7 @@ const load = (
const classGraphQLOutputTypeName = `${graphQLClassName}`;
const outputFields = () => {
return classOutputFields.reduce((fields, field) => {
const type = mapOutputType(
const type = transformOutputTypeToGraphQL(
parseClass.fields[field].type,
parseClass.fields[field].targetClass,
parseGraphQLSchema.parseClassTypes
@@ -704,12 +572,12 @@ const load = (
} else {
return fields;
}
}, defaultGraphQLTypes.CLASS_FIELDS);
}, defaultGraphQLTypes.PARSE_OBJECT_FIELDS);
};
let classGraphQLOutputType = new GraphQLObjectType({
name: classGraphQLOutputTypeName,
description: `The ${classGraphQLOutputTypeName} object type is used in operations that involve outputting objects of ${graphQLClassName} class.`,
interfaces: [defaultGraphQLTypes.CLASS],
interfaces: [defaultGraphQLTypes.PARSE_OBJECT],
fields: outputFields,
});
classGraphQLOutputType = parseGraphQLSchema.addGraphQLType(
@@ -760,7 +628,7 @@ const load = (
const viewerType = new GraphQLObjectType({
name: 'Viewer',
description: `The Viewer object type is used in operations that involve outputting the current user data.`,
interfaces: [defaultGraphQLTypes.CLASS],
interfaces: [defaultGraphQLTypes.PARSE_OBJECT],
fields: () => ({
...outputFields(),
sessionToken: defaultGraphQLTypes.SESSION_TOKEN_ATT,
@@ -775,7 +643,7 @@ const load = (
description: `The ${userSignUpInputTypeName} input type is used in operations that involve inputting objects of ${graphQLClassName} class when signing up.`,
fields: () =>
classCreateFields.reduce((fields, field) => {
const type = mapInputType(
const type = transformInputTypeToGraphQL(
parseClass.fields[field].type,
parseClass.fields[field].targetClass,
parseGraphQLSchema.parseClassTypes

View File

@@ -0,0 +1,146 @@
import Parse from 'parse/node';
import { GraphQLNonNull } from 'graphql';
import * as schemaTypes from './schemaTypes';
import {
transformToParse,
transformToGraphQL,
} from '../transformers/schemaFields';
import { enforceMasterKeyAccess } from '../parseGraphQLUtils';
import { getClass } from './schemaQueries';
const load = parseGraphQLSchema => {
parseGraphQLSchema.addGraphQLMutation(
'createClass',
{
description:
'The createClass mutation can be used to create the schema for a new object class.',
args: {
name: schemaTypes.CLASS_NAME_ATT,
schemaFields: {
description: "These are the schema's fields of the object class.",
type: schemaTypes.SCHEMA_FIELDS_INPUT,
},
},
type: new GraphQLNonNull(schemaTypes.CLASS),
resolve: async (_source, args, context) => {
try {
const { name, schemaFields } = args;
const { config, auth } = context;
enforceMasterKeyAccess(auth);
if (auth.isReadOnly) {
throw new Parse.Error(
Parse.Error.OPERATION_FORBIDDEN,
"read-only masterKey isn't allowed to create a schema."
);
}
const schema = await config.database.loadSchema({ clearCache: true });
const parseClass = await schema.addClassIfNotExists(
name,
transformToParse(schemaFields)
);
return {
name: parseClass.className,
schemaFields: transformToGraphQL(parseClass.fields),
};
} catch (e) {
parseGraphQLSchema.handleError(e);
}
},
},
true,
true
);
parseGraphQLSchema.addGraphQLMutation(
'updateClass',
{
description:
'The updateClass mutation can be used to update the schema for an existing object class.',
args: {
name: schemaTypes.CLASS_NAME_ATT,
schemaFields: {
description: "These are the schema's fields of the object class.",
type: schemaTypes.SCHEMA_FIELDS_INPUT,
},
},
type: new GraphQLNonNull(schemaTypes.CLASS),
resolve: async (_source, args, context) => {
try {
const { name, schemaFields } = args;
const { config, auth } = context;
enforceMasterKeyAccess(auth);
if (auth.isReadOnly) {
throw new Parse.Error(
Parse.Error.OPERATION_FORBIDDEN,
"read-only masterKey isn't allowed to update a schema."
);
}
const schema = await config.database.loadSchema({ clearCache: true });
const existingParseClass = await getClass(name, schema);
const parseClass = await schema.updateClass(
name,
transformToParse(schemaFields, existingParseClass.fields),
undefined,
undefined,
config.database
);
return {
name: parseClass.className,
schemaFields: transformToGraphQL(parseClass.fields),
};
} catch (e) {
parseGraphQLSchema.handleError(e);
}
},
},
true,
true
);
parseGraphQLSchema.addGraphQLMutation(
'deleteClass',
{
description:
'The deleteClass mutation can be used to delete an existing object class.',
args: {
name: schemaTypes.CLASS_NAME_ATT,
},
type: new GraphQLNonNull(schemaTypes.CLASS),
resolve: async (_source, args, context) => {
try {
const { name } = args;
const { config, auth } = context;
enforceMasterKeyAccess(auth);
if (auth.isReadOnly) {
throw new Parse.Error(
Parse.Error.OPERATION_FORBIDDEN,
"read-only masterKey isn't allowed to delete a schema."
);
}
const schema = await config.database.loadSchema({ clearCache: true });
const existingParseClass = await getClass(name, schema);
await config.database.deleteSchema(name);
return {
name: existingParseClass.className,
schemaFields: transformToGraphQL(existingParseClass.fields),
};
} catch (e) {
parseGraphQLSchema.handleError(e);
}
},
},
true,
true
);
};
export { load };

View File

@@ -0,0 +1,86 @@
import Parse from 'parse/node';
import { GraphQLNonNull, GraphQLList } from 'graphql';
import { transformToGraphQL } from '../transformers/schemaFields';
import * as schemaTypes from './schemaTypes';
import { enforceMasterKeyAccess } from '../parseGraphQLUtils';
const getClass = async (name, schema) => {
try {
return await schema.getOneSchema(name, true);
} catch (e) {
if (e === undefined) {
throw new Parse.Error(
Parse.Error.INVALID_CLASS_NAME,
`Class ${name} does not exist.`
);
} else {
throw new Parse.Error(
Parse.Error.INTERNAL_SERVER_ERROR,
'Database adapter error.'
);
}
}
};
const load = parseGraphQLSchema => {
parseGraphQLSchema.addGraphQLQuery(
'class',
{
description:
'The class query can be used to retrieve an existing object class.',
args: {
name: schemaTypes.CLASS_NAME_ATT,
},
type: new GraphQLNonNull(schemaTypes.CLASS),
resolve: async (_source, args, context) => {
try {
const { name } = args;
const { config, auth } = context;
enforceMasterKeyAccess(auth);
const schema = await config.database.loadSchema({ clearCache: true });
const parseClass = await getClass(name, schema);
return {
name: parseClass.className,
schemaFields: transformToGraphQL(parseClass.fields),
};
} catch (e) {
parseGraphQLSchema.handleError(e);
}
},
},
true,
true
);
parseGraphQLSchema.addGraphQLQuery(
'classes',
{
description:
'The classes query can be used to retrieve the existing object classes.',
type: new GraphQLNonNull(
new GraphQLList(new GraphQLNonNull(schemaTypes.CLASS))
),
resolve: async (_source, _args, context) => {
try {
const { config, auth } = context;
enforceMasterKeyAccess(auth);
const schema = await config.database.loadSchema({ clearCache: true });
return (await schema.getAllClasses(true)).map(parseClass => ({
name: parseClass.className,
schemaFields: transformToGraphQL(parseClass.fields),
}));
} catch (e) {
parseGraphQLSchema.handleError(e);
}
},
},
true,
true
);
};
export { getClass, load };

View File

@@ -0,0 +1,448 @@
import {
GraphQLNonNull,
GraphQLString,
GraphQLInputObjectType,
GraphQLList,
GraphQLObjectType,
GraphQLInterfaceType,
} from 'graphql';
const SCHEMA_FIELD_NAME_ATT = {
description: 'This is the field name.',
type: new GraphQLNonNull(GraphQLString),
};
const SCHEMA_FIELD_INPUT = new GraphQLInputObjectType({
name: 'SchemaFieldInput',
description:
'The SchemaFieldInput is used to specify a field of an object class schema.',
fields: {
name: SCHEMA_FIELD_NAME_ATT,
},
});
const SCHEMA_FIELD = new GraphQLInterfaceType({
name: 'SchemaField',
description:
'The SchemaField interface type is used as a base type for the different supported fields of an object class schema.',
fields: {
name: SCHEMA_FIELD_NAME_ATT,
},
resolveType: value =>
({
String: SCHEMA_STRING_FIELD,
Number: SCHEMA_NUMBER_FIELD,
Boolean: SCHEMA_BOOLEAN_FIELD,
Array: SCHEMA_ARRAY_FIELD,
Object: SCHEMA_OBJECT_FIELD,
Date: SCHEMA_DATE_FIELD,
File: SCHEMA_FILE_FIELD,
GeoPoint: SCHEMA_GEO_POINT_FIELD,
Polygon: SCHEMA_POLYGON_FIELD,
Bytes: SCHEMA_BYTES_FIELD,
Pointer: SCHEMA_POINTER_FIELD,
Relation: SCHEMA_RELATION_FIELD,
ACL: SCHEMA_ACL_FIELD,
}[value.type]),
});
const SCHEMA_STRING_FIELD_INPUT = new GraphQLInputObjectType({
name: 'SchemaStringFieldInput',
description:
'The SchemaStringFieldInput is used to specify a field of type string for an object class schema.',
fields: {
name: SCHEMA_FIELD_NAME_ATT,
},
});
const SCHEMA_STRING_FIELD = new GraphQLObjectType({
name: 'SchemaStringField',
description:
'The SchemaStringField is used to return information of a String field.',
interfaces: [SCHEMA_FIELD],
fields: {
name: SCHEMA_FIELD_NAME_ATT,
},
});
const SCHEMA_NUMBER_FIELD_INPUT = new GraphQLInputObjectType({
name: 'SchemaNumberFieldInput',
description:
'The SchemaNumberFieldInput is used to specify a field of type number for an object class schema.',
fields: {
name: SCHEMA_FIELD_NAME_ATT,
},
});
const SCHEMA_NUMBER_FIELD = new GraphQLObjectType({
name: 'SchemaNumberField',
description:
'The SchemaNumberField is used to return information of a Number field.',
interfaces: [SCHEMA_FIELD],
fields: {
name: SCHEMA_FIELD_NAME_ATT,
},
});
const SCHEMA_BOOLEAN_FIELD_INPUT = new GraphQLInputObjectType({
name: 'SchemaBooleanFieldInput',
description:
'The SchemaBooleanFieldInput is used to specify a field of type boolean for an object class schema.',
fields: {
name: SCHEMA_FIELD_NAME_ATT,
},
});
const SCHEMA_BOOLEAN_FIELD = new GraphQLObjectType({
name: 'SchemaBooleanField',
description:
'The SchemaBooleanField is used to return information of a Boolean field.',
interfaces: [SCHEMA_FIELD],
fields: {
name: SCHEMA_FIELD_NAME_ATT,
},
});
const SCHEMA_ARRAY_FIELD_INPUT = new GraphQLInputObjectType({
name: 'SchemaArrayFieldInput',
description:
'The SchemaArrayFieldInput is used to specify a field of type array for an object class schema.',
fields: {
name: SCHEMA_FIELD_NAME_ATT,
},
});
const SCHEMA_ARRAY_FIELD = new GraphQLObjectType({
name: 'SchemaArrayField',
description:
'The SchemaArrayField is used to return information of an Array field.',
interfaces: [SCHEMA_FIELD],
fields: {
name: SCHEMA_FIELD_NAME_ATT,
},
});
const SCHEMA_OBJECT_FIELD_INPUT = new GraphQLInputObjectType({
name: 'SchemaObjectFieldInput',
description:
'The SchemaObjectFieldInput is used to specify a field of type object for an object class schema.',
fields: {
name: SCHEMA_FIELD_NAME_ATT,
},
});
const SCHEMA_OBJECT_FIELD = new GraphQLObjectType({
name: 'SchemaObjectField',
description:
'The SchemaObjectField is used to return information of an Object field.',
interfaces: [SCHEMA_FIELD],
fields: {
name: SCHEMA_FIELD_NAME_ATT,
},
});
const SCHEMA_DATE_FIELD_INPUT = new GraphQLInputObjectType({
name: 'SchemaDateFieldInput',
description:
'The SchemaDateFieldInput is used to specify a field of type date for an object class schema.',
fields: {
name: SCHEMA_FIELD_NAME_ATT,
},
});
const SCHEMA_DATE_FIELD = new GraphQLObjectType({
name: 'SchemaDateField',
description:
'The SchemaDateField is used to return information of a Date field.',
interfaces: [SCHEMA_FIELD],
fields: {
name: SCHEMA_FIELD_NAME_ATT,
},
});
const SCHEMA_FILE_FIELD_INPUT = new GraphQLInputObjectType({
name: 'SchemaFileFieldInput',
description:
'The SchemaFileFieldInput is used to specify a field of type file for an object class schema.',
fields: {
name: SCHEMA_FIELD_NAME_ATT,
},
});
const SCHEMA_FILE_FIELD = new GraphQLObjectType({
name: 'SchemaFileField',
description:
'The SchemaFileField is used to return information of a File field.',
interfaces: [SCHEMA_FIELD],
fields: {
name: SCHEMA_FIELD_NAME_ATT,
},
});
const SCHEMA_GEO_POINT_FIELD_INPUT = new GraphQLInputObjectType({
name: 'SchemaGeoPointFieldInput',
description:
'The SchemaGeoPointFieldInput is used to specify a field of type geo point for an object class schema.',
fields: {
name: SCHEMA_FIELD_NAME_ATT,
},
});
const SCHEMA_GEO_POINT_FIELD = new GraphQLObjectType({
name: 'SchemaGeoPointField',
description:
'The SchemaGeoPointField is used to return information of a Geo Point field.',
interfaces: [SCHEMA_FIELD],
fields: {
name: SCHEMA_FIELD_NAME_ATT,
},
});
const SCHEMA_POLYGON_FIELD_INPUT = new GraphQLInputObjectType({
name: 'SchemaPolygonFieldInput',
description:
'The SchemaPolygonFieldInput is used to specify a field of type polygon for an object class schema.',
fields: {
name: SCHEMA_FIELD_NAME_ATT,
},
});
const SCHEMA_POLYGON_FIELD = new GraphQLObjectType({
name: 'SchemaPolygonField',
description:
'The SchemaPolygonField is used to return information of a Polygon field.',
interfaces: [SCHEMA_FIELD],
fields: {
name: SCHEMA_FIELD_NAME_ATT,
},
});
const SCHEMA_BYTES_FIELD_INPUT = new GraphQLInputObjectType({
name: 'SchemaBytesFieldInput',
description:
'The SchemaBytesFieldInput is used to specify a field of type bytes for an object class schema.',
fields: {
name: SCHEMA_FIELD_NAME_ATT,
},
});
const SCHEMA_BYTES_FIELD = new GraphQLObjectType({
name: 'SchemaBytesField',
description:
'The SchemaBytesField is used to return information of a Bytes field.',
interfaces: [SCHEMA_FIELD],
fields: {
name: SCHEMA_FIELD_NAME_ATT,
},
});
const TARGET_CLASS_ATT = {
description: 'This is the name of the target class for the field.',
type: new GraphQLNonNull(GraphQLString),
};
const SCHEMA_POINTER_FIELD_INPUT = new GraphQLInputObjectType({
name: 'PointerFieldInput',
description:
'The PointerFieldInput is used to specify a field of type pointer for an object class schema.',
fields: {
name: SCHEMA_FIELD_NAME_ATT,
targetClassName: TARGET_CLASS_ATT,
},
});
const SCHEMA_POINTER_FIELD = new GraphQLObjectType({
name: 'SchemaPointerField',
description:
'The SchemaPointerField is used to return information of a Pointer field.',
interfaces: [SCHEMA_FIELD],
fields: {
name: SCHEMA_FIELD_NAME_ATT,
targetClassName: TARGET_CLASS_ATT,
},
});
const SCHEMA_RELATION_FIELD_INPUT = new GraphQLInputObjectType({
name: 'RelationFieldInput',
description:
'The RelationFieldInput is used to specify a field of type relation for an object class schema.',
fields: {
name: SCHEMA_FIELD_NAME_ATT,
targetClassName: TARGET_CLASS_ATT,
},
});
const SCHEMA_RELATION_FIELD = new GraphQLObjectType({
name: 'SchemaRelationField',
description:
'The SchemaRelationField is used to return information of a Relation field.',
interfaces: [SCHEMA_FIELD],
fields: {
name: SCHEMA_FIELD_NAME_ATT,
targetClassName: TARGET_CLASS_ATT,
},
});
const SCHEMA_ACL_FIELD = new GraphQLObjectType({
name: 'SchemaACLField',
description:
'The SchemaACLField is used to return information of an ACL field.',
interfaces: [SCHEMA_FIELD],
fields: {
name: SCHEMA_FIELD_NAME_ATT,
},
});
const SCHEMA_FIELDS_INPUT = new GraphQLInputObjectType({
name: 'SchemaFieldsInput',
description: `The CreateClassSchemaInput type is used to specify the schema for a new object class to be created.`,
fields: {
addStrings: {
description:
'These are the String fields to be added to the class schema.',
type: new GraphQLList(new GraphQLNonNull(SCHEMA_STRING_FIELD_INPUT)),
},
addNumbers: {
description:
'These are the Number fields to be added to the class schema.',
type: new GraphQLList(new GraphQLNonNull(SCHEMA_NUMBER_FIELD_INPUT)),
},
addBooleans: {
description:
'These are the Boolean fields to be added to the class schema.',
type: new GraphQLList(new GraphQLNonNull(SCHEMA_BOOLEAN_FIELD_INPUT)),
},
addArrays: {
description:
'These are the Array fields to be added to the class schema.',
type: new GraphQLList(new GraphQLNonNull(SCHEMA_ARRAY_FIELD_INPUT)),
},
addObjects: {
description:
'These are the Object fields to be added to the class schema.',
type: new GraphQLList(new GraphQLNonNull(SCHEMA_OBJECT_FIELD_INPUT)),
},
addDates: {
description: 'These are the Date fields to be added to the class schema.',
type: new GraphQLList(new GraphQLNonNull(SCHEMA_DATE_FIELD_INPUT)),
},
addFiles: {
description: 'These are the File fields to be added to the class schema.',
type: new GraphQLList(new GraphQLNonNull(SCHEMA_FILE_FIELD_INPUT)),
},
addGeoPoint: {
description:
'This is the Geo Point field to be added to the class schema. Currently it is supported only one GeoPoint field per Class.',
type: SCHEMA_GEO_POINT_FIELD_INPUT,
},
addPolygons: {
description:
'These are the Polygon fields to be added to the class schema.',
type: new GraphQLList(new GraphQLNonNull(SCHEMA_POLYGON_FIELD_INPUT)),
},
addBytes: {
description:
'These are the Bytes fields to be added to the class schema.',
type: new GraphQLList(new GraphQLNonNull(SCHEMA_BYTES_FIELD_INPUT)),
},
addPointers: {
description:
'These are the Pointer fields to be added to the class schema.',
type: new GraphQLList(new GraphQLNonNull(SCHEMA_POINTER_FIELD_INPUT)),
},
addRelations: {
description:
'These are the Relation fields to be added to the class schema.',
type: new GraphQLList(new GraphQLNonNull(SCHEMA_RELATION_FIELD_INPUT)),
},
remove: {
description: 'These are the fields to be removed from the class schema.',
type: new GraphQLList(new GraphQLNonNull(SCHEMA_FIELD_INPUT)),
},
},
});
const CLASS_NAME_ATT = {
description: 'This is the name of the object class.',
type: new GraphQLNonNull(GraphQLString),
};
const CLASS = new GraphQLObjectType({
name: 'Class',
description: `The Class type is used to return the information about an object class.`,
fields: {
name: CLASS_NAME_ATT,
schemaFields: {
description: "These are the schema's fields of the object class.",
type: new GraphQLNonNull(
new GraphQLList(new GraphQLNonNull(SCHEMA_FIELD))
),
},
},
});
const load = parseGraphQLSchema => {
parseGraphQLSchema.addGraphQLType(SCHEMA_FIELD_INPUT, true);
parseGraphQLSchema.addGraphQLType(SCHEMA_STRING_FIELD_INPUT, true);
parseGraphQLSchema.addGraphQLType(SCHEMA_STRING_FIELD, true);
parseGraphQLSchema.addGraphQLType(SCHEMA_NUMBER_FIELD_INPUT, true);
parseGraphQLSchema.addGraphQLType(SCHEMA_NUMBER_FIELD, true);
parseGraphQLSchema.addGraphQLType(SCHEMA_BOOLEAN_FIELD_INPUT, true);
parseGraphQLSchema.addGraphQLType(SCHEMA_BOOLEAN_FIELD, true);
parseGraphQLSchema.addGraphQLType(SCHEMA_ARRAY_FIELD_INPUT, true);
parseGraphQLSchema.addGraphQLType(SCHEMA_ARRAY_FIELD, true);
parseGraphQLSchema.addGraphQLType(SCHEMA_OBJECT_FIELD_INPUT, true);
parseGraphQLSchema.addGraphQLType(SCHEMA_OBJECT_FIELD, true);
parseGraphQLSchema.addGraphQLType(SCHEMA_DATE_FIELD_INPUT, true);
parseGraphQLSchema.addGraphQLType(SCHEMA_DATE_FIELD, true);
parseGraphQLSchema.addGraphQLType(SCHEMA_FILE_FIELD_INPUT, true);
parseGraphQLSchema.addGraphQLType(SCHEMA_FILE_FIELD, true);
parseGraphQLSchema.addGraphQLType(SCHEMA_GEO_POINT_FIELD_INPUT, true);
parseGraphQLSchema.addGraphQLType(SCHEMA_GEO_POINT_FIELD, true);
parseGraphQLSchema.addGraphQLType(SCHEMA_POLYGON_FIELD_INPUT, true);
parseGraphQLSchema.addGraphQLType(SCHEMA_POLYGON_FIELD, true);
parseGraphQLSchema.addGraphQLType(SCHEMA_BYTES_FIELD_INPUT, true);
parseGraphQLSchema.addGraphQLType(SCHEMA_BYTES_FIELD, true);
parseGraphQLSchema.addGraphQLType(SCHEMA_POINTER_FIELD_INPUT, true);
parseGraphQLSchema.addGraphQLType(SCHEMA_POINTER_FIELD, true);
parseGraphQLSchema.addGraphQLType(SCHEMA_RELATION_FIELD_INPUT, true);
parseGraphQLSchema.addGraphQLType(SCHEMA_RELATION_FIELD, true);
parseGraphQLSchema.addGraphQLType(SCHEMA_ACL_FIELD, true);
parseGraphQLSchema.addGraphQLType(SCHEMA_FIELDS_INPUT, true);
parseGraphQLSchema.addGraphQLType(CLASS, true);
};
export {
SCHEMA_FIELD_NAME_ATT,
SCHEMA_FIELD_INPUT,
SCHEMA_STRING_FIELD_INPUT,
SCHEMA_STRING_FIELD,
SCHEMA_NUMBER_FIELD_INPUT,
SCHEMA_NUMBER_FIELD,
SCHEMA_BOOLEAN_FIELD_INPUT,
SCHEMA_BOOLEAN_FIELD,
SCHEMA_ARRAY_FIELD_INPUT,
SCHEMA_ARRAY_FIELD,
SCHEMA_OBJECT_FIELD_INPUT,
SCHEMA_OBJECT_FIELD,
SCHEMA_DATE_FIELD_INPUT,
SCHEMA_DATE_FIELD,
SCHEMA_FILE_FIELD_INPUT,
SCHEMA_FILE_FIELD,
SCHEMA_GEO_POINT_FIELD_INPUT,
SCHEMA_GEO_POINT_FIELD,
SCHEMA_POLYGON_FIELD_INPUT,
SCHEMA_POLYGON_FIELD,
SCHEMA_BYTES_FIELD_INPUT,
SCHEMA_BYTES_FIELD,
TARGET_CLASS_ATT,
SCHEMA_POINTER_FIELD_INPUT,
SCHEMA_POINTER_FIELD,
SCHEMA_RELATION_FIELD_INPUT,
SCHEMA_RELATION_FIELD,
SCHEMA_ACL_FIELD,
SCHEMA_FIELDS_INPUT,
CLASS_NAME_ATT,
CLASS,
load,
};

View File

@@ -1,6 +1,6 @@
import { GraphQLNonNull } from 'graphql';
import UsersRouter from '../../Routers/UsersRouter';
import * as objectsMutations from './objectsMutations';
import * as objectsMutations from '../helpers/objectsMutations';
import { getUserFromSessionToken } from './usersQueries';
const usersRouter = new UsersRouter();

View File

@@ -1,6 +1,15 @@
import Parse from 'parse/node';
import { ApolloError } from 'apollo-server-core';
export function enforceMasterKeyAccess(auth) {
if (!auth.isMaster) {
throw new Parse.Error(
Parse.Error.OPERATION_FORBIDDEN,
'unauthorized: master key is required'
);
}
}
export function toGraphQLError(error) {
let code, message;
if (error instanceof Parse.Error) {

View File

@@ -0,0 +1,46 @@
import * as defaultGraphQLTypes from '../loaders/defaultGraphQLTypes';
const transformConstraintTypeToGraphQL = (
parseType,
targetClass,
parseClassTypes
) => {
switch (parseType) {
case 'String':
return defaultGraphQLTypes.STRING_WHERE_INPUT;
case 'Number':
return defaultGraphQLTypes.NUMBER_WHERE_INPUT;
case 'Boolean':
return defaultGraphQLTypes.BOOLEAN_WHERE_INPUT;
case 'Array':
return defaultGraphQLTypes.ARRAY_WHERE_INPUT;
case 'Object':
return defaultGraphQLTypes.OBJECT_WHERE_INPUT;
case 'Date':
return defaultGraphQLTypes.DATE_WHERE_INPUT;
case 'Pointer':
if (
parseClassTypes[targetClass] &&
parseClassTypes[targetClass].classGraphQLConstraintType
) {
return parseClassTypes[targetClass].classGraphQLConstraintType;
} else {
return defaultGraphQLTypes.OBJECT;
}
case 'File':
return defaultGraphQLTypes.FILE_WHERE_INPUT;
case 'GeoPoint':
return defaultGraphQLTypes.GEO_POINT_WHERE_INPUT;
case 'Polygon':
return defaultGraphQLTypes.POLYGON_WHERE_INPUT;
case 'Bytes':
return defaultGraphQLTypes.BYTES_WHERE_INPUT;
case 'ACL':
return defaultGraphQLTypes.OBJECT_WHERE_INPUT;
case 'Relation':
default:
return undefined;
}
};
export { transformConstraintTypeToGraphQL };

View File

@@ -0,0 +1,62 @@
import {
GraphQLString,
GraphQLFloat,
GraphQLBoolean,
GraphQLList,
} from 'graphql';
import * as defaultGraphQLTypes from '../loaders/defaultGraphQLTypes';
const transformInputTypeToGraphQL = (
parseType,
targetClass,
parseClassTypes
) => {
switch (parseType) {
case 'String':
return GraphQLString;
case 'Number':
return GraphQLFloat;
case 'Boolean':
return GraphQLBoolean;
case 'Array':
return new GraphQLList(defaultGraphQLTypes.ANY);
case 'Object':
return defaultGraphQLTypes.OBJECT;
case 'Date':
return defaultGraphQLTypes.DATE;
case 'Pointer':
if (
parseClassTypes &&
parseClassTypes[targetClass] &&
parseClassTypes[targetClass].classGraphQLPointerType
) {
return parseClassTypes[targetClass].classGraphQLPointerType;
} else {
return defaultGraphQLTypes.OBJECT;
}
case 'Relation':
if (
parseClassTypes &&
parseClassTypes[targetClass] &&
parseClassTypes[targetClass].classGraphQLRelationType
) {
return parseClassTypes[targetClass].classGraphQLRelationType;
} else {
return defaultGraphQLTypes.OBJECT;
}
case 'File':
return defaultGraphQLTypes.FILE;
case 'GeoPoint':
return defaultGraphQLTypes.GEO_POINT_INPUT;
case 'Polygon':
return defaultGraphQLTypes.POLYGON_INPUT;
case 'Bytes':
return defaultGraphQLTypes.BYTES;
case 'ACL':
return defaultGraphQLTypes.OBJECT;
default:
return undefined;
}
};
export { transformInputTypeToGraphQL };

View File

@@ -1,5 +1,5 @@
import * as defaultGraphQLTypes from '../loaders/defaultGraphQLTypes';
import * as objectsMutations from '../loaders/objectsMutations';
import * as objectsMutations from '../helpers/objectsMutations';
const transformTypes = async (
inputType: 'create' | 'update',

View File

@@ -0,0 +1,65 @@
import * as defaultGraphQLTypes from '../loaders/defaultGraphQLTypes';
import {
GraphQLString,
GraphQLFloat,
GraphQLBoolean,
GraphQLList,
GraphQLNonNull,
} from 'graphql';
const transformOutputTypeToGraphQL = (
parseType,
targetClass,
parseClassTypes
) => {
switch (parseType) {
case 'String':
return GraphQLString;
case 'Number':
return GraphQLFloat;
case 'Boolean':
return GraphQLBoolean;
case 'Array':
return new GraphQLList(defaultGraphQLTypes.ARRAY_RESULT);
case 'Object':
return defaultGraphQLTypes.OBJECT;
case 'Date':
return defaultGraphQLTypes.DATE;
case 'Pointer':
if (
parseClassTypes &&
parseClassTypes[targetClass] &&
parseClassTypes[targetClass].classGraphQLOutputType
) {
return parseClassTypes[targetClass].classGraphQLOutputType;
} else {
return defaultGraphQLTypes.OBJECT;
}
case 'Relation':
if (
parseClassTypes &&
parseClassTypes[targetClass] &&
parseClassTypes[targetClass].classGraphQLFindResultType
) {
return new GraphQLNonNull(
parseClassTypes[targetClass].classGraphQLFindResultType
);
} else {
return new GraphQLNonNull(defaultGraphQLTypes.FIND_RESULT);
}
case 'File':
return defaultGraphQLTypes.FILE_INFO;
case 'GeoPoint':
return defaultGraphQLTypes.GEO_POINT;
case 'Polygon':
return defaultGraphQLTypes.POLYGON;
case 'Bytes':
return defaultGraphQLTypes.BYTES;
case 'ACL':
return defaultGraphQLTypes.OBJECT;
default:
return undefined;
}
};
export { transformOutputTypeToGraphQL };

View File

@@ -0,0 +1,147 @@
import Parse from 'parse/node';
const transformToParse = (graphQLSchemaFields, existingFields) => {
if (!graphQLSchemaFields) {
return {};
}
let parseSchemaFields = {};
const reducerGenerator = type => (parseSchemaFields, field) => {
if (type === 'Remove') {
if (existingFields[field.name]) {
return {
...parseSchemaFields,
[field.name]: {
__op: 'Delete',
},
};
} else {
return parseSchemaFields;
}
}
if (
graphQLSchemaFields.remove &&
graphQLSchemaFields.remove.find(
removeField => removeField.name === field.name
)
) {
return parseSchemaFields;
}
if (
parseSchemaFields[field.name] ||
(existingFields && existingFields[field.name])
) {
throw new Parse.Error(
Parse.Error.INVALID_KEY_NAME,
`Duplicated field name: ${field.name}`
);
}
if (type === 'Relation' || type === 'Pointer') {
return {
...parseSchemaFields,
[field.name]: {
type,
targetClass: field.targetClassName,
},
};
}
return {
...parseSchemaFields,
[field.name]: {
type,
},
};
};
if (graphQLSchemaFields.addStrings) {
parseSchemaFields = graphQLSchemaFields.addStrings.reduce(
reducerGenerator('String'),
parseSchemaFields
);
}
if (graphQLSchemaFields.addNumbers) {
parseSchemaFields = graphQLSchemaFields.addNumbers.reduce(
reducerGenerator('Number'),
parseSchemaFields
);
}
if (graphQLSchemaFields.addBooleans) {
parseSchemaFields = graphQLSchemaFields.addBooleans.reduce(
reducerGenerator('Boolean'),
parseSchemaFields
);
}
if (graphQLSchemaFields.addArrays) {
parseSchemaFields = graphQLSchemaFields.addArrays.reduce(
reducerGenerator('Array'),
parseSchemaFields
);
}
if (graphQLSchemaFields.addObjects) {
parseSchemaFields = graphQLSchemaFields.addObjects.reduce(
reducerGenerator('Object'),
parseSchemaFields
);
}
if (graphQLSchemaFields.addDates) {
parseSchemaFields = graphQLSchemaFields.addDates.reduce(
reducerGenerator('Date'),
parseSchemaFields
);
}
if (graphQLSchemaFields.addFiles) {
parseSchemaFields = graphQLSchemaFields.addFiles.reduce(
reducerGenerator('File'),
parseSchemaFields
);
}
if (graphQLSchemaFields.addGeoPoint) {
parseSchemaFields = [graphQLSchemaFields.addGeoPoint].reduce(
reducerGenerator('GeoPoint'),
parseSchemaFields
);
}
if (graphQLSchemaFields.addPolygons) {
parseSchemaFields = graphQLSchemaFields.addPolygons.reduce(
reducerGenerator('Polygon'),
parseSchemaFields
);
}
if (graphQLSchemaFields.addBytes) {
parseSchemaFields = graphQLSchemaFields.addBytes.reduce(
reducerGenerator('Bytes'),
parseSchemaFields
);
}
if (graphQLSchemaFields.addPointers) {
parseSchemaFields = graphQLSchemaFields.addPointers.reduce(
reducerGenerator('Pointer'),
parseSchemaFields
);
}
if (graphQLSchemaFields.addRelations) {
parseSchemaFields = graphQLSchemaFields.addRelations.reduce(
reducerGenerator('Relation'),
parseSchemaFields
);
}
if (existingFields && graphQLSchemaFields.remove) {
parseSchemaFields = graphQLSchemaFields.remove.reduce(
reducerGenerator('Remove'),
parseSchemaFields
);
}
return parseSchemaFields;
};
const transformToGraphQL = parseSchemaFields => {
return Object.keys(parseSchemaFields).map(name => ({
name,
type: parseSchemaFields[name].type,
targetClassName: parseSchemaFields[name].targetClass,
}));
};
export { transformToParse, transformToGraphQL };