Skip to content

Commit c7f96c9

Browse files
authored
GraphQL: Allow true GraphQL Schema Customization (#6360)
* Allow real GraphQL Schema via ParseServer.start * wip * working * tests ok * add tests about enum/input use case * Add async function based merge * Better naming * remove useless condition
1 parent d4690ca commit c7f96c9

8 files changed

+397
-127
lines changed

spec/ParseGraphQLServer.spec.js

Lines changed: 274 additions & 100 deletions
Large diffs are not rendered by default.

src/GraphQL/ParseGraphQLSchema.js

Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -197,19 +197,60 @@ class ParseGraphQLSchema {
197197
if (this.graphQLCustomTypeDefs) {
198198
schemaDirectives.load(this);
199199

200-
this.graphQLSchema = mergeSchemas({
201-
schemas: [
202-
this.graphQLSchemaDirectivesDefinitions,
203-
this.graphQLAutoSchema,
204-
this.graphQLCustomTypeDefs,
205-
],
206-
mergeDirectives: true,
207-
});
200+
if (typeof this.graphQLCustomTypeDefs.getTypeMap === 'function') {
201+
const customGraphQLSchemaTypeMap = this.graphQLCustomTypeDefs.getTypeMap();
202+
Object.values(customGraphQLSchemaTypeMap).forEach(
203+
customGraphQLSchemaType => {
204+
if (
205+
!customGraphQLSchemaType ||
206+
!customGraphQLSchemaType.name ||
207+
customGraphQLSchemaType.name.startsWith('__')
208+
) {
209+
return;
210+
}
211+
const autoGraphQLSchemaType = this.graphQLAutoSchema.getType(
212+
customGraphQLSchemaType.name
213+
);
214+
if (autoGraphQLSchemaType) {
215+
autoGraphQLSchemaType._fields = {
216+
...autoGraphQLSchemaType._fields,
217+
...customGraphQLSchemaType._fields,
218+
};
219+
}
220+
}
221+
);
222+
this.graphQLSchema = mergeSchemas({
223+
schemas: [
224+
this.graphQLSchemaDirectivesDefinitions,
225+
this.graphQLCustomTypeDefs,
226+
this.graphQLAutoSchema,
227+
],
228+
mergeDirectives: true,
229+
});
230+
} else if (typeof this.graphQLCustomTypeDefs === 'function') {
231+
this.graphQLSchema = await this.graphQLCustomTypeDefs({
232+
directivesDefinitionsSchema: this.graphQLSchemaDirectivesDefinitions,
233+
autoSchema: this.graphQLAutoSchema,
234+
mergeSchemas,
235+
});
236+
} else {
237+
this.graphQLSchema = mergeSchemas({
238+
schemas: [
239+
this.graphQLSchemaDirectivesDefinitions,
240+
this.graphQLAutoSchema,
241+
this.graphQLCustomTypeDefs,
242+
],
243+
mergeDirectives: true,
244+
});
245+
}
208246

209247
const graphQLSchemaTypeMap = this.graphQLSchema.getTypeMap();
210248
Object.keys(graphQLSchemaTypeMap).forEach(graphQLSchemaTypeName => {
211249
const graphQLSchemaType = graphQLSchemaTypeMap[graphQLSchemaTypeName];
212-
if (typeof graphQLSchemaType.getFields === 'function') {
250+
if (
251+
typeof graphQLSchemaType.getFields === 'function' &&
252+
this.graphQLCustomTypeDefs.definitions
253+
) {
213254
const graphQLCustomTypeDef = this.graphQLCustomTypeDefs.definitions.find(
214255
definition => definition.name.value === graphQLSchemaTypeName
215256
);

src/GraphQL/helpers/objectsQueries.js

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ import { offsetToCursor, cursorToOffset } from 'graphql-relay';
33
import rest from '../../rest';
44
import { transformQueryInputToParse } from '../transformers/query';
55

6+
const needToGetAllKeys = (fields, keys) =>
7+
keys
8+
? !!keys.split(',').find(keyName => !fields[keyName.split('.')[0]])
9+
: true;
10+
611
const getObject = async (
712
className,
813
objectId,
@@ -12,10 +17,11 @@ const getObject = async (
1217
includeReadPreference,
1318
config,
1419
auth,
15-
info
20+
info,
21+
parseClass
1622
) => {
1723
const options = {};
18-
if (keys) {
24+
if (!needToGetAllKeys(parseClass.fields, keys)) {
1925
options.keys = keys;
2026
}
2127
if (include) {
@@ -133,7 +139,14 @@ const findObjects = async (
133139
// Silently replace the limit on the query with the max configured
134140
options.limit = config.maxLimit;
135141
}
136-
if (keys) {
142+
if (
143+
!needToGetAllKeys(
144+
parseClasses.find(
145+
({ className: parseClassName }) => className === parseClassName
146+
).fields,
147+
keys
148+
)
149+
) {
137150
options.keys = keys;
138151
}
139152
if (includeAll === true) {
@@ -313,4 +326,4 @@ const calculateSkipAndLimit = (
313326
};
314327
};
315328

316-
export { getObject, findObjects, calculateSkipAndLimit };
329+
export { getObject, findObjects, calculateSkipAndLimit, needToGetAllKeys };

src/GraphQL/loaders/defaultRelaySchema.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,10 @@ const load = parseGraphQLSchema => {
3030
undefined,
3131
config,
3232
auth,
33-
info
33+
info,
34+
parseGraphQLSchema.parseClasses.find(
35+
({ className }) => type === className
36+
)
3437
)),
3538
};
3639
} catch (e) {

src/GraphQL/loaders/parseClassMutations.js

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,12 @@ const load = function(
112112
include,
113113
['id', 'objectId', 'createdAt', 'updatedAt']
114114
);
115+
const needToGetAllKeys = objectsQueries.needToGetAllKeys(
116+
parseClass.fields,
117+
keys
118+
);
115119
let optimizedObject = {};
116-
if (needGet) {
120+
if (needGet && !needToGetAllKeys) {
117121
optimizedObject = await objectsQueries.getObject(
118122
className,
119123
createdObject.objectId,
@@ -123,7 +127,21 @@ const load = function(
123127
undefined,
124128
config,
125129
auth,
126-
info
130+
info,
131+
parseClass
132+
);
133+
} else if (needToGetAllKeys) {
134+
optimizedObject = await objectsQueries.getObject(
135+
className,
136+
createdObject.objectId,
137+
undefined,
138+
include,
139+
undefined,
140+
undefined,
141+
config,
142+
auth,
143+
info,
144+
parseClass
127145
);
128146
}
129147
return {
@@ -212,9 +230,12 @@ const load = function(
212230
include,
213231
['id', 'objectId', 'updatedAt']
214232
);
215-
233+
const needToGetAllKeys = objectsQueries.needToGetAllKeys(
234+
parseClass.fields,
235+
keys
236+
);
216237
let optimizedObject = {};
217-
if (needGet) {
238+
if (needGet && !needToGetAllKeys) {
218239
optimizedObject = await objectsQueries.getObject(
219240
className,
220241
id,
@@ -224,7 +245,21 @@ const load = function(
224245
undefined,
225246
config,
226247
auth,
227-
info
248+
info,
249+
parseClass
250+
);
251+
} else if (needToGetAllKeys) {
252+
optimizedObject = await objectsQueries.getObject(
253+
className,
254+
id,
255+
undefined,
256+
include,
257+
undefined,
258+
undefined,
259+
config,
260+
auth,
261+
info,
262+
parseClass
228263
);
229264
}
230265
return {
@@ -301,7 +336,8 @@ const load = function(
301336
undefined,
302337
config,
303338
auth,
304-
info
339+
info,
340+
parseClass
305341
);
306342
}
307343
await objectsMutations.deleteObject(

src/GraphQL/loaders/parseClassQueries.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const getParseClassQueryConfig = function(
1414
return (parseClassConfig && parseClassConfig.query) || {};
1515
};
1616

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

2424
const globalIdObject = fromGlobalId(id);
2525

26-
if (globalIdObject.type === className) {
26+
if (globalIdObject.type === parseClass.className) {
2727
id = globalIdObject.id;
2828
}
2929

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

3232
return await objectsQueries.getObject(
33-
className,
33+
parseClass.className,
3434
id,
3535
keys,
3636
include,
3737
readPreference,
3838
includeReadPreference,
3939
config,
4040
auth,
41-
info
41+
info,
42+
parseClass
4243
);
4344
};
4445

@@ -79,7 +80,7 @@ const load = function(
7980
),
8081
async resolve(_source, args, context, queryInfo) {
8182
try {
82-
return await getQuery(className, _source, args, context, queryInfo);
83+
return await getQuery(parseClass, _source, args, context, queryInfo);
8384
} catch (e) {
8485
parseGraphQLSchema.handleError(e);
8586
}

src/GraphQL/loaders/parseClassTypes.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,7 @@ const load = (
436436
);
437437
const parseOrder = order && order.join(',');
438438

439-
return await objectsQueries.findObjects(
439+
return objectsQueries.findObjects(
440440
source[field].className,
441441
{
442442
$relatedTo: {

src/ParseServer.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,10 +262,12 @@ class ParseServer {
262262

263263
if (options.mountGraphQL === true || options.mountPlayground === true) {
264264
let graphQLCustomTypeDefs = undefined;
265-
if (options.graphQLSchema) {
265+
if (typeof options.graphQLSchema === 'string') {
266266
graphQLCustomTypeDefs = parse(
267267
fs.readFileSync(options.graphQLSchema, 'utf8')
268268
);
269+
} else if (typeof options.graphQLSchema === 'object') {
270+
graphQLCustomTypeDefs = options.graphQLSchema;
269271
}
270272

271273
const parseGraphQLServer = new ParseGraphQLServer(this, {

0 commit comments

Comments
 (0)