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:
committed by
GitHub
parent
e404c43222
commit
5a482bd661
@@ -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
@@ -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) {
|
||||
|
||||
@@ -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]) => {
|
||||
|
||||
39
src/GraphQL/helpers/objectsMutations.js
Normal file
39
src/GraphQL/helpers/objectsMutations.js
Normal 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 };
|
||||
127
src/GraphQL/helpers/objectsQueries.js
Normal file
127
src/GraphQL/helpers/objectsQueries.js
Normal 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 };
|
||||
@@ -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 };
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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
|
||||
|
||||
146
src/GraphQL/loaders/schemaMutations.js
Normal file
146
src/GraphQL/loaders/schemaMutations.js
Normal 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 };
|
||||
86
src/GraphQL/loaders/schemaQueries.js
Normal file
86
src/GraphQL/loaders/schemaQueries.js
Normal 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 };
|
||||
448
src/GraphQL/loaders/schemaTypes.js
Normal file
448
src/GraphQL/loaders/schemaTypes.js
Normal 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,
|
||||
};
|
||||
@@ -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();
|
||||
|
||||
@@ -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) {
|
||||
|
||||
46
src/GraphQL/transformers/constraintType.js
Normal file
46
src/GraphQL/transformers/constraintType.js
Normal 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 };
|
||||
62
src/GraphQL/transformers/inputType.js
Normal file
62
src/GraphQL/transformers/inputType.js
Normal 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 };
|
||||
@@ -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',
|
||||
|
||||
65
src/GraphQL/transformers/outputType.js
Normal file
65
src/GraphQL/transformers/outputType.js
Normal 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 };
|
||||
147
src/GraphQL/transformers/schemaFields.js
Normal file
147
src/GraphQL/transformers/schemaFields.js
Normal 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 };
|
||||
Reference in New Issue
Block a user