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

@@ -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 };