GraphQL Configuration Options (#5782)

* add parse-graph-ql configuration for class schema customisation

Not yet tested - essentially an RFC

* refactor and add graphql router, controller and config cache

* fix(GraphQLController): add missing check isEnabled

* chore(GraphQLController): remove awaits from cache put

* chore(GraphQLController): remove check for if its enabled

* refactor(GraphQLController): only use cache if mounted

* chore(GraphQLController): group all validation errors and throw at once

* chore(GraphQLSchema): move transformations into controller validation

* refactor(GraphQL): improve ctrl validation and fix schema usage of config

* refactor(GraphQLSchema): remove code related to additional schema

This code has been moved into a separate feature branch.

* fix(GraphQLSchema): fix incorrect default return type for class configs

* refactor(GraphQLSchema): update staleness check code to account for config

* fix(GraphQLServer): fix regressed tests due to internal schema changes

This will be followed up with a backwards compatability fix for the `ClassFields` issue to avoid breakages for our users

* refactor: rename to ParseGraphQLController for consistency

* fix(ParseGraphQLCtrl): numerous fixes for validity checking

Also includes some minor code refactoring

* chore(GraphQL): minor syntax cleanup

* fix(SchemaController): add _GraphQLConfig to volatile classes

* refactor(ParseGraphQLServer): return update config value in setGraphQLConfig

* testing(ParseGraphQL): add test cases for new graphQLConfig

* fix(GraphQLController): fix issue where config with multiple items was not being mapped to the db

* fix(postgres): add _GraphQLConfig default schema on load

fixes failing postgres tests

* GraphQL @mock directive (#5836)

* Add mock directive
* Include tests for @mock directive

* Fix existing tests due to the change from ClassFields to ClassCreateFields

* fix(parseClassMutations): safer type transformation based on input type

* fix(parseClassMutations): only define necessary input fields

* fix(GraphQL): fix incorrect import paths
This commit is contained in:
Omair Vaiyani
2019-07-25 20:46:25 +01:00
committed by Antonio Davi Macedo Coelho de Castro
parent bbcc20fd60
commit d3810c2eba
18 changed files with 2956 additions and 290 deletions

View File

@@ -1,23 +1,58 @@
import { GraphQLNonNull, GraphQLBoolean } from 'graphql';
import * as defaultGraphQLTypes from './defaultGraphQLTypes';
import * as objectsMutations from './objectsMutations';
import { ParseGraphQLClassConfig } from '../../Controllers/ParseGraphQLController';
const load = (parseGraphQLSchema, parseClass) => {
const className = parseClass.className;
const getParseClassMutationConfig = function(
parseClassConfig: ?ParseGraphQLClassConfig
) {
return (parseClassConfig && parseClassConfig.mutation) || {};
};
const classGraphQLInputType =
parseGraphQLSchema.parseClassTypes[className].classGraphQLInputType;
const fields = {
description: 'These are the fields of the object.',
type: classGraphQLInputType,
const load = function(
parseGraphQLSchema,
parseClass,
parseClassConfig: ?ParseGraphQLClassConfig
) {
const { className } = parseClass;
const {
create: isCreateEnabled = true,
update: isUpdateEnabled = true,
destroy: isDestroyEnabled = true,
} = getParseClassMutationConfig(parseClassConfig);
const {
classGraphQLCreateType,
classGraphQLUpdateType,
} = parseGraphQLSchema.parseClassTypes[className];
const createFields = {
description: 'These are the fields used to create the object.',
type: classGraphQLCreateType,
};
const updateFields = {
description: 'These are the fields used to update the object.',
type: classGraphQLUpdateType,
};
const classGraphQLInputTypeFields = classGraphQLInputType.getFields();
const transformTypes = fields => {
const classGraphQLCreateTypeFields = isCreateEnabled
? classGraphQLCreateType.getFields()
: null;
const classGraphQLUpdateTypeFields = isUpdateEnabled
? classGraphQLUpdateType.getFields()
: null;
const transformTypes = (inputType: 'create' | 'update', fields) => {
if (fields) {
Object.keys(fields).forEach(field => {
if (classGraphQLInputTypeFields[field]) {
switch (classGraphQLInputTypeFields[field].type) {
let inputTypeField;
if (inputType === 'create') {
inputTypeField = classGraphQLCreateTypeFields[field];
} else {
inputTypeField = classGraphQLUpdateTypeFields[field];
}
if (inputTypeField) {
switch (inputTypeField.type) {
case defaultGraphQLTypes.GEO_POINT:
fields[field].__type = 'GeoPoint';
break;
@@ -36,86 +71,92 @@ const load = (parseGraphQLSchema, parseClass) => {
}
};
const createGraphQLMutationName = `create${className}`;
parseGraphQLSchema.graphQLObjectsMutations[createGraphQLMutationName] = {
description: `The ${createGraphQLMutationName} mutation can be used to create a new object of the ${className} class.`,
args: {
fields,
},
type: new GraphQLNonNull(defaultGraphQLTypes.CREATE_RESULT),
async resolve(_source, args, context) {
try {
const { fields } = args;
const { config, auth, info } = context;
if (isCreateEnabled) {
const createGraphQLMutationName = `create${className}`;
parseGraphQLSchema.graphQLObjectsMutations[createGraphQLMutationName] = {
description: `The ${createGraphQLMutationName} mutation can be used to create a new object of the ${className} class.`,
args: {
fields: createFields,
},
type: new GraphQLNonNull(defaultGraphQLTypes.CREATE_RESULT),
async resolve(_source, args, context) {
try {
const { fields } = args;
const { config, auth, info } = context;
transformTypes(fields);
transformTypes('create', fields);
return await objectsMutations.createObject(
className,
fields,
config,
auth,
info
);
} catch (e) {
parseGraphQLSchema.handleError(e);
}
},
};
return await objectsMutations.createObject(
className,
fields,
config,
auth,
info
);
} catch (e) {
parseGraphQLSchema.handleError(e);
}
},
};
}
const updateGraphQLMutationName = `update${className}`;
parseGraphQLSchema.graphQLObjectsMutations[updateGraphQLMutationName] = {
description: `The ${updateGraphQLMutationName} mutation can be used to update an object of the ${className} class.`,
args: {
objectId: defaultGraphQLTypes.OBJECT_ID_ATT,
fields,
},
type: defaultGraphQLTypes.UPDATE_RESULT,
async resolve(_source, args, context) {
try {
const { objectId, fields } = args;
const { config, auth, info } = context;
if (isUpdateEnabled) {
const updateGraphQLMutationName = `update${className}`;
parseGraphQLSchema.graphQLObjectsMutations[updateGraphQLMutationName] = {
description: `The ${updateGraphQLMutationName} mutation can be used to update an object of the ${className} class.`,
args: {
objectId: defaultGraphQLTypes.OBJECT_ID_ATT,
fields: updateFields,
},
type: defaultGraphQLTypes.UPDATE_RESULT,
async resolve(_source, args, context) {
try {
const { objectId, fields } = args;
const { config, auth, info } = context;
transformTypes(fields);
transformTypes('update', fields);
return await objectsMutations.updateObject(
className,
objectId,
fields,
config,
auth,
info
);
} catch (e) {
parseGraphQLSchema.handleError(e);
}
},
};
return await objectsMutations.updateObject(
className,
objectId,
fields,
config,
auth,
info
);
} catch (e) {
parseGraphQLSchema.handleError(e);
}
},
};
}
const deleteGraphQLMutationName = `delete${className}`;
parseGraphQLSchema.graphQLObjectsMutations[deleteGraphQLMutationName] = {
description: `The ${deleteGraphQLMutationName} mutation can be used to delete an object of the ${className} class.`,
args: {
objectId: defaultGraphQLTypes.OBJECT_ID_ATT,
},
type: new GraphQLNonNull(GraphQLBoolean),
async resolve(_source, args, context) {
try {
const { objectId } = args;
const { config, auth, info } = context;
if (isDestroyEnabled) {
const deleteGraphQLMutationName = `delete${className}`;
parseGraphQLSchema.graphQLObjectsMutations[deleteGraphQLMutationName] = {
description: `The ${deleteGraphQLMutationName} mutation can be used to delete an object of the ${className} class.`,
args: {
objectId: defaultGraphQLTypes.OBJECT_ID_ATT,
},
type: new GraphQLNonNull(GraphQLBoolean),
async resolve(_source, args, context) {
try {
const { objectId } = args;
const { config, auth, info } = context;
return await objectsMutations.deleteObject(
className,
objectId,
config,
auth,
info
);
} catch (e) {
parseGraphQLSchema.handleError(e);
}
},
};
return await objectsMutations.deleteObject(
className,
objectId,
config,
auth,
info
);
} catch (e) {
parseGraphQLSchema.handleError(e);
}
},
};
}
};
export { load };