Skip to content

GraphQL: Allow true GraphQL Schema Customization #6360

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Feb 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
374 changes: 274 additions & 100 deletions spec/ParseGraphQLServer.spec.js

Large diffs are not rendered by default.

59 changes: 50 additions & 9 deletions src/GraphQL/ParseGraphQLSchema.js
Original file line number Diff line number Diff line change
Expand Up @@ -197,19 +197,60 @@ class ParseGraphQLSchema {
if (this.graphQLCustomTypeDefs) {
schemaDirectives.load(this);

this.graphQLSchema = mergeSchemas({
schemas: [
this.graphQLSchemaDirectivesDefinitions,
this.graphQLAutoSchema,
this.graphQLCustomTypeDefs,
],
mergeDirectives: true,
});
if (typeof this.graphQLCustomTypeDefs.getTypeMap === 'function') {
const customGraphQLSchemaTypeMap = this.graphQLCustomTypeDefs.getTypeMap();
Object.values(customGraphQLSchemaTypeMap).forEach(
customGraphQLSchemaType => {
if (
!customGraphQLSchemaType ||
!customGraphQLSchemaType.name ||
customGraphQLSchemaType.name.startsWith('__')
) {
return;
}
const autoGraphQLSchemaType = this.graphQLAutoSchema.getType(
customGraphQLSchemaType.name
);
if (autoGraphQLSchemaType) {
autoGraphQLSchemaType._fields = {
...autoGraphQLSchemaType._fields,
...customGraphQLSchemaType._fields,
};
}
}
);
this.graphQLSchema = mergeSchemas({
schemas: [
this.graphQLSchemaDirectivesDefinitions,
this.graphQLCustomTypeDefs,
this.graphQLAutoSchema,
],
mergeDirectives: true,
});
} else if (typeof this.graphQLCustomTypeDefs === 'function') {
this.graphQLSchema = await this.graphQLCustomTypeDefs({
directivesDefinitionsSchema: this.graphQLSchemaDirectivesDefinitions,
autoSchema: this.graphQLAutoSchema,
mergeSchemas,
});
} else {
this.graphQLSchema = mergeSchemas({
schemas: [
this.graphQLSchemaDirectivesDefinitions,
this.graphQLAutoSchema,
this.graphQLCustomTypeDefs,
],
mergeDirectives: true,
});
}

const graphQLSchemaTypeMap = this.graphQLSchema.getTypeMap();
Object.keys(graphQLSchemaTypeMap).forEach(graphQLSchemaTypeName => {
const graphQLSchemaType = graphQLSchemaTypeMap[graphQLSchemaTypeName];
if (typeof graphQLSchemaType.getFields === 'function') {
if (
typeof graphQLSchemaType.getFields === 'function' &&
this.graphQLCustomTypeDefs.definitions
) {
const graphQLCustomTypeDef = this.graphQLCustomTypeDefs.definitions.find(
definition => definition.name.value === graphQLSchemaTypeName
);
Expand Down
21 changes: 17 additions & 4 deletions src/GraphQL/helpers/objectsQueries.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ import { offsetToCursor, cursorToOffset } from 'graphql-relay';
import rest from '../../rest';
import { transformQueryInputToParse } from '../transformers/query';

const needToGetAllKeys = (fields, keys) =>
keys
? !!keys.split(',').find(keyName => !fields[keyName.split('.')[0]])
: true;
Copy link
Contributor

@omairvaiyani omairvaiyani Feb 5, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this expected, no keys means there are custom fields?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bad naming yes, i fixed it !


const getObject = async (
className,
objectId,
Expand All @@ -12,10 +17,11 @@ const getObject = async (
includeReadPreference,
config,
auth,
info
info,
parseClass
) => {
const options = {};
if (keys) {
if (!needToGetAllKeys(parseClass.fields, keys)) {
options.keys = keys;
}
if (include) {
Expand Down Expand Up @@ -133,7 +139,14 @@ const findObjects = async (
// Silently replace the limit on the query with the max configured
options.limit = config.maxLimit;
}
if (keys) {
if (
!needToGetAllKeys(
parseClasses.find(
({ className: parseClassName }) => className === parseClassName
).fields,
keys
)
) {
options.keys = keys;
}
if (includeAll === true) {
Expand Down Expand Up @@ -313,4 +326,4 @@ const calculateSkipAndLimit = (
};
};

export { getObject, findObjects, calculateSkipAndLimit };
export { getObject, findObjects, calculateSkipAndLimit, needToGetAllKeys };
5 changes: 4 additions & 1 deletion src/GraphQL/loaders/defaultRelaySchema.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ const load = parseGraphQLSchema => {
undefined,
config,
auth,
info
info,
parseGraphQLSchema.parseClasses.find(
({ className }) => type === className
)
)),
};
} catch (e) {
Expand Down
48 changes: 42 additions & 6 deletions src/GraphQL/loaders/parseClassMutations.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,12 @@ const load = function(
include,
['id', 'objectId', 'createdAt', 'updatedAt']
);
const needToGetAllKeys = objectsQueries.needToGetAllKeys(
parseClass.fields,
keys
);
let optimizedObject = {};
if (needGet) {
if (needGet && !needToGetAllKeys) {
optimizedObject = await objectsQueries.getObject(
className,
createdObject.objectId,
Expand All @@ -123,7 +127,21 @@ const load = function(
undefined,
config,
auth,
info
info,
parseClass
);
} else if (needToGetAllKeys) {
optimizedObject = await objectsQueries.getObject(
className,
createdObject.objectId,
undefined,
include,
undefined,
undefined,
config,
auth,
info,
parseClass
);
}
return {
Expand Down Expand Up @@ -212,9 +230,12 @@ const load = function(
include,
['id', 'objectId', 'updatedAt']
);

const needToGetAllKeys = objectsQueries.needToGetAllKeys(
parseClass.fields,
keys
);
let optimizedObject = {};
if (needGet) {
if (needGet && !needToGetAllKeys) {
optimizedObject = await objectsQueries.getObject(
className,
id,
Expand All @@ -224,7 +245,21 @@ const load = function(
undefined,
config,
auth,
info
info,
parseClass
);
} else if (needToGetAllKeys) {
optimizedObject = await objectsQueries.getObject(
className,
id,
undefined,
include,
undefined,
undefined,
config,
auth,
info,
parseClass
);
}
return {
Expand Down Expand Up @@ -301,7 +336,8 @@ const load = function(
undefined,
config,
auth,
info
info,
parseClass
);
}
await objectsMutations.deleteObject(
Expand Down
11 changes: 6 additions & 5 deletions src/GraphQL/loaders/parseClassQueries.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const getParseClassQueryConfig = function(
return (parseClassConfig && parseClassConfig.query) || {};
};

const getQuery = async (className, _source, args, context, queryInfo) => {
const getQuery = async (parseClass, _source, args, context, queryInfo) => {
let { id } = args;
const { options } = args;
const { readPreference, includeReadPreference } = options || {};
Expand All @@ -23,22 +23,23 @@ const getQuery = async (className, _source, args, context, queryInfo) => {

const globalIdObject = fromGlobalId(id);

if (globalIdObject.type === className) {
if (globalIdObject.type === parseClass.className) {
id = globalIdObject.id;
}

const { keys, include } = extractKeysAndInclude(selectedFields);

return await objectsQueries.getObject(
className,
parseClass.className,
id,
keys,
include,
readPreference,
includeReadPreference,
config,
auth,
info
info,
parseClass
);
};

Expand Down Expand Up @@ -79,7 +80,7 @@ const load = function(
),
async resolve(_source, args, context, queryInfo) {
try {
return await getQuery(className, _source, args, context, queryInfo);
return await getQuery(parseClass, _source, args, context, queryInfo);
} catch (e) {
parseGraphQLSchema.handleError(e);
}
Expand Down
2 changes: 1 addition & 1 deletion src/GraphQL/loaders/parseClassTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -436,7 +436,7 @@ const load = (
);
const parseOrder = order && order.join(',');

return await objectsQueries.findObjects(
return objectsQueries.findObjects(
source[field].className,
{
$relatedTo: {
Expand Down
4 changes: 3 additions & 1 deletion src/ParseServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -262,10 +262,12 @@ class ParseServer {

if (options.mountGraphQL === true || options.mountPlayground === true) {
let graphQLCustomTypeDefs = undefined;
if (options.graphQLSchema) {
if (typeof options.graphQLSchema === 'string') {
graphQLCustomTypeDefs = parse(
fs.readFileSync(options.graphQLSchema, 'utf8')
);
} else if (typeof options.graphQLSchema === 'object') {
graphQLCustomTypeDefs = options.graphQLSchema;
}

const parseGraphQLServer = new ParseGraphQLServer(this, {
Expand Down