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

@@ -8,36 +8,57 @@ import * as parseClassQueries from './loaders/parseClassQueries';
import * as parseClassMutations from './loaders/parseClassMutations';
import * as defaultGraphQLQueries from './loaders/defaultGraphQLQueries';
import * as defaultGraphQLMutations from './loaders/defaultGraphQLMutations';
import ParseGraphQLController, {
ParseGraphQLConfig,
} from '../Controllers/ParseGraphQLController';
import DatabaseController from '../Controllers/DatabaseController';
import { toGraphQLError } from './parseGraphQLUtils';
import * as schemaDirectives from './loaders/schemaDirectives';
class ParseGraphQLSchema {
constructor(databaseController, log, graphQLCustomTypeDefs) {
databaseController: DatabaseController;
parseGraphQLController: ParseGraphQLController;
parseGraphQLConfig: ParseGraphQLConfig;
graphQLCustomTypeDefs: any;
constructor(
params: {
databaseController: DatabaseController,
parseGraphQLController: ParseGraphQLController,
log: any,
} = {}
) {
this.parseGraphQLController =
params.parseGraphQLController ||
requiredParameter('You must provide a parseGraphQLController instance!');
this.databaseController =
databaseController ||
params.databaseController ||
requiredParameter('You must provide a databaseController instance!');
this.log = log || requiredParameter('You must provide a log instance!');
this.graphQLCustomTypeDefs = graphQLCustomTypeDefs;
this.log =
params.log || requiredParameter('You must provide a log instance!');
this.graphQLCustomTypeDefs = params.graphQLCustomTypeDefs;
}
async load() {
const schemaController = await this.databaseController.loadSchema();
const parseClasses = await schemaController.getAllClasses();
const { parseGraphQLConfig } = await this._initializeSchemaAndConfig();
const parseClasses = await this._getClassesForSchema(parseGraphQLConfig);
const parseClassesString = JSON.stringify(parseClasses);
if (this.graphQLSchema) {
if (this.parseClasses === parseClasses) {
return this.graphQLSchema;
}
if (this.parseClassesString === parseClassesString) {
this.parseClasses = parseClasses;
return this.graphQLSchema;
}
if (
this.graphQLSchema &&
!this._hasSchemaInputChanged({
parseClasses,
parseClassesString,
parseGraphQLConfig,
})
) {
return this.graphQLSchema;
}
this.parseClasses = parseClasses;
this.parseClassesString = parseClassesString;
this.parseGraphQLConfig = parseGraphQLConfig;
this.parseClassTypes = {};
this.meType = null;
this.graphQLAutoSchema = null;
@@ -53,16 +74,15 @@ class ParseGraphQLSchema {
defaultGraphQLTypes.load(this);
parseClasses.forEach(parseClass => {
parseClassTypes.load(this, parseClass);
parseClassQueries.load(this, parseClass);
parseClassMutations.load(this, parseClass);
});
this._getParseClassesWithConfig(parseClasses, parseGraphQLConfig).forEach(
([parseClass, parseClassConfig]) => {
parseClassTypes.load(this, parseClass, parseClassConfig);
parseClassQueries.load(this, parseClass, parseClassConfig);
parseClassMutations.load(this, parseClass, parseClassConfig);
}
);
defaultGraphQLQueries.load(this);
defaultGraphQLMutations.load(this);
let graphQLQuery = undefined;
@@ -160,6 +180,104 @@ class ParseGraphQLSchema {
}
throw toGraphQLError(error);
}
async _initializeSchemaAndConfig() {
const [schemaController, parseGraphQLConfig] = await Promise.all([
this.databaseController.loadSchema(),
this.parseGraphQLController.getGraphQLConfig(),
]);
this.schemaController = schemaController;
return {
parseGraphQLConfig,
};
}
/**
* Gets all classes found by the `schemaController`
* minus those filtered out by the app's parseGraphQLConfig.
*/
async _getClassesForSchema(parseGraphQLConfig: ParseGraphQLConfig) {
const { enabledForClasses, disabledForClasses } = parseGraphQLConfig;
const allClasses = await this.schemaController.getAllClasses();
if (Array.isArray(enabledForClasses) || Array.isArray(disabledForClasses)) {
let includedClasses = allClasses;
if (enabledForClasses) {
includedClasses = allClasses.filter(clazz => {
return enabledForClasses.includes(clazz.className);
});
}
if (disabledForClasses) {
// Classes included in `enabledForClasses` that
// are also present in `disabledForClasses` will
// still be filtered out
includedClasses = includedClasses.filter(clazz => {
return !disabledForClasses.includes(clazz.className);
});
}
this.isUsersClassDisabled = !includedClasses.some(clazz => {
return clazz.className === '_User';
});
return includedClasses;
} else {
return allClasses;
}
}
/**
* This method returns a list of tuples
* that provide the parseClass along with
* its parseClassConfig where provided.
*/
_getParseClassesWithConfig(
parseClasses,
parseGraphQLConfig: ParseGraphQLConfig
) {
const { classConfigs } = parseGraphQLConfig;
return parseClasses.map(parseClass => {
let parseClassConfig;
if (classConfigs) {
parseClassConfig = classConfigs.find(
c => c.className === parseClass.className
);
}
return [parseClass, parseClassConfig];
});
}
/**
* Checks for changes to the parseClasses
* objects (i.e. database schema) or to
* the parseGraphQLConfig object. If no
* changes are found, return true;
*/
_hasSchemaInputChanged(params: {
parseClasses: any,
parseClassesString: string,
parseGraphQLConfig: ?ParseGraphQLConfig,
}): boolean {
const { parseClasses, parseClassesString, parseGraphQLConfig } = params;
if (
JSON.stringify(this.parseGraphQLConfig) ===
JSON.stringify(parseGraphQLConfig)
) {
if (this.parseClasses === parseClasses) {
return false;
}
if (this.parseClassesString === parseClassesString) {
this.parseClasses = parseClasses;
return false;
}
}
return true;
}
}
export { ParseGraphQLSchema };