Skip to content

Commit 1a43fd7

Browse files
committed
feat: graphql yoga 2.0
1 parent 0294d45 commit 1a43fd7

9 files changed

+526
-1132
lines changed

package-lock.json

+330-1,016
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+2-5
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,11 @@
1919
],
2020
"license": "BSD-3-Clause",
2121
"dependencies": {
22-
"@apollo/client": "3.5.10",
23-
"@apollographql/graphql-playground-html": "1.6.29",
24-
"@graphql-tools/links": "8.2.11",
2522
"@graphql-tools/stitch": "6.2.4",
2623
"@graphql-tools/utils": "6.2.4",
24+
"@graphql-yoga/node": "2.5.0",
2725
"@parse/fs-files-adapter": "1.2.2",
2826
"@parse/push-adapter": "4.1.2",
29-
"apollo-server-express": "2.25.2",
3027
"bcryptjs": "2.4.3",
3128
"body-parser": "1.20.0",
3229
"commander": "5.1.0",
@@ -38,7 +35,6 @@
3835
"graphql-list-fields": "2.0.2",
3936
"graphql-relay": "0.7.0",
4037
"graphql-tag": "2.12.6",
41-
"graphql-upload": "11.0.0",
4238
"intersect": "1.0.1",
4339
"jsonwebtoken": "8.5.1",
4440
"jwks-rsa": "2.0.5",
@@ -63,6 +59,7 @@
6359
},
6460
"devDependencies": {
6561
"@actions/core": "1.2.6",
62+
"@apollo/client": "3.6.1",
6663
"@babel/cli": "7.10.0",
6764
"@babel/core": "7.10.0",
6865
"@babel/plugin-proposal-object-rest-spread": "7.10.0",

spec/ParseGraphQLServer.spec.js

+132-54
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,24 @@ describe('ParseGraphQLServer', () => {
9898
});
9999
});
100100

101+
describe('_getServer', () => {
102+
it('should only return new server on schema changes', async () => {
103+
parseGraphQLServer.server = undefined;
104+
const server1 = await parseGraphQLServer._getServer();
105+
const server2 = await parseGraphQLServer._getServer();
106+
expect(server1).toBe(server2);
107+
108+
// Trigger a schema change
109+
const obj = new Parse.Object('SomeClass');
110+
await obj.save();
111+
112+
const server3 = await parseGraphQLServer._getServer();
113+
const server4 = await parseGraphQLServer._getServer();
114+
expect(server3).not.toBe(server2);
115+
expect(server3).toBe(server4);
116+
});
117+
});
118+
101119
describe('_getGraphQLOptions', () => {
102120
const req = {
103121
info: new Object(),
@@ -106,11 +124,15 @@ describe('ParseGraphQLServer', () => {
106124
};
107125

108126
it("should return schema and context with req's info, config and auth", async () => {
109-
const options = await parseGraphQLServer._getGraphQLOptions(req);
127+
const options = await parseGraphQLServer._getGraphQLOptions();
128+
expect(options.multipart).toEqual({
129+
fileSize: 20971520,
130+
});
110131
expect(options.schema).toEqual(parseGraphQLServer.parseGraphQLSchema.graphQLSchema);
111-
expect(options.context.info).toEqual(req.info);
112-
expect(options.context.config).toEqual(req.config);
113-
expect(options.context.auth).toEqual(req.auth);
132+
const contextResponse = options.context({ req });
133+
expect(contextResponse.info).toEqual(req.info);
134+
expect(contextResponse.config).toEqual(req.config);
135+
expect(contextResponse.auth).toEqual(req.auth);
114136
});
115137

116138
it('should load GraphQL schema in every call', async () => {
@@ -467,7 +489,7 @@ describe('ParseGraphQLServer', () => {
467489
}
468490
});
469491

470-
it('should be cors enabled', async () => {
492+
it('should be cors enabled and scope the response within the source origin', async () => {
471493
let checked = false;
472494
const apolloClient = new ApolloClient({
473495
link: new ApolloLink((operation, forward) => {
@@ -476,7 +498,7 @@ describe('ParseGraphQLServer', () => {
476498
const {
477499
response: { headers },
478500
} = context;
479-
expect(headers.get('access-control-allow-origin')).toEqual('*');
501+
expect(headers.get('access-control-allow-origin')).toEqual('http://someorigin.com');
480502
checked = true;
481503
return response;
482504
});
@@ -504,14 +526,25 @@ describe('ParseGraphQLServer', () => {
504526
});
505527

506528
it('should handle Parse headers', async () => {
507-
let checked = false;
529+
const test = {
530+
context: ({ req: { info, config, auth } }) => {
531+
expect(req.info).toBeDefined();
532+
expect(req.config).toBeDefined();
533+
expect(req.auth).toBeDefined();
534+
return {
535+
info,
536+
config,
537+
auth,
538+
};
539+
},
540+
};
541+
const contextSpy = spyOn(test, 'context');
508542
const originalGetGraphQLOptions = parseGraphQLServer._getGraphQLOptions;
509-
parseGraphQLServer._getGraphQLOptions = async req => {
510-
expect(req.info).toBeDefined();
511-
expect(req.config).toBeDefined();
512-
expect(req.auth).toBeDefined();
513-
checked = true;
514-
return await originalGetGraphQLOptions.bind(parseGraphQLServer)(req);
543+
parseGraphQLServer._getGraphQLOptions = async () => {
544+
return {
545+
schema: await parseGraphQLServer.parseGraphQLSchema.load(),
546+
context: test.context,
547+
};
515548
};
516549
const health = (
517550
await apolloClient.query({
@@ -523,7 +556,7 @@ describe('ParseGraphQLServer', () => {
523556
})
524557
).data.health;
525558
expect(health).toBeTruthy();
526-
expect(checked).toBeTruthy();
559+
expect(contextSpy).toHaveBeenCalledTimes(1);
527560
parseGraphQLServer._getGraphQLOptions = originalGetGraphQLOptions;
528561
});
529562
});
@@ -6786,7 +6819,7 @@ describe('ParseGraphQLServer', () => {
67866819

67876820
expect(queryResult.data.customers.edges.length).toEqual(1);
67886821
} catch (e) {
6789-
console.log(JSON.stringify(e));
6822+
console.error(JSON.stringify(e));
67906823
}
67916824
});
67926825
});
@@ -9107,15 +9140,15 @@ describe('ParseGraphQLServer', () => {
91079140
'operations',
91089141
JSON.stringify({
91099142
query: `
9110-
mutation CreateFile($input: CreateFileInput!) {
9111-
createFile(input: $input) {
9112-
fileInfo {
9113-
name
9114-
url
9143+
mutation CreateFile($input: CreateFileInput!) {
9144+
createFile(input: $input) {
9145+
fileInfo {
9146+
name
9147+
url
9148+
}
91159149
}
91169150
}
9117-
}
9118-
`,
9151+
`,
91199152
variables: {
91209153
input: {
91219154
upload: null,
@@ -9176,46 +9209,46 @@ describe('ParseGraphQLServer', () => {
91769209
'operations',
91779210
JSON.stringify({
91789211
query: `
9179-
mutation CreateSomeObject(
9180-
$fields1: CreateSomeClassFieldsInput
9181-
$fields2: CreateSomeClassFieldsInput
9182-
$fields3: CreateSomeClassFieldsInput
9183-
) {
9184-
createSomeClass1: createSomeClass(
9185-
input: { fields: $fields1 }
9212+
mutation CreateSomeObject(
9213+
$fields1: CreateSomeClassFieldsInput
9214+
$fields2: CreateSomeClassFieldsInput
9215+
$fields3: CreateSomeClassFieldsInput
91869216
) {
9187-
someClass {
9188-
id
9189-
someField {
9190-
name
9191-
url
9217+
createSomeClass1: createSomeClass(
9218+
input: { fields: $fields1 }
9219+
) {
9220+
someClass {
9221+
id
9222+
someField {
9223+
name
9224+
url
9225+
}
91929226
}
91939227
}
9194-
}
9195-
createSomeClass2: createSomeClass(
9196-
input: { fields: $fields2 }
9197-
) {
9198-
someClass {
9199-
id
9200-
someField {
9201-
name
9202-
url
9228+
createSomeClass2: createSomeClass(
9229+
input: { fields: $fields2 }
9230+
) {
9231+
someClass {
9232+
id
9233+
someField {
9234+
name
9235+
url
9236+
}
92039237
}
92049238
}
9205-
}
9206-
createSomeClass3: createSomeClass(
9207-
input: { fields: $fields3 }
9208-
) {
9209-
someClass {
9210-
id
9211-
someField {
9212-
name
9213-
url
9239+
createSomeClass3: createSomeClass(
9240+
input: { fields: $fields3 }
9241+
) {
9242+
someClass {
9243+
id
9244+
someField {
9245+
name
9246+
url
9247+
}
92149248
}
92159249
}
92169250
}
9217-
}
9218-
`,
9251+
`,
92199252
variables: {
92209253
fields1: {
92219254
someField: { file: someFieldValue },
@@ -9344,6 +9377,51 @@ describe('ParseGraphQLServer', () => {
93449377
}
93459378
});
93469379

9380+
it('should not upload if file is too large', async () => {
9381+
parseGraphQLServer.parseServer.config.maxUploadSize = '1kb';
9382+
9383+
const body = new FormData();
9384+
body.append(
9385+
'operations',
9386+
JSON.stringify({
9387+
query: `
9388+
mutation CreateFile($input: CreateFileInput!) {
9389+
createFile(input: $input) {
9390+
fileInfo {
9391+
name
9392+
url
9393+
}
9394+
}
9395+
}
9396+
`,
9397+
variables: {
9398+
input: {
9399+
upload: null,
9400+
},
9401+
},
9402+
})
9403+
);
9404+
body.append('map', JSON.stringify({ 1: ['variables.input.upload'] }));
9405+
body.append(
9406+
'1',
9407+
Buffer.alloc(parseGraphQLServer._transformMaxUploadSizeToBytes('2kb'), 1),
9408+
{
9409+
filename: 'myFileName.txt',
9410+
contentType: 'text/plain',
9411+
}
9412+
);
9413+
9414+
const res = await fetch('http://localhost:13377/graphql', {
9415+
method: 'POST',
9416+
headers,
9417+
body,
9418+
});
9419+
9420+
const result = JSON.parse(await res.text());
9421+
expect(res.status).toEqual(500);
9422+
expect(result.errors[0].message).toEqual('File size limit exceeded: 1024 bytes');
9423+
});
9424+
93479425
it('should support object values', async () => {
93489426
try {
93499427
const someObjectFieldValue = {

src/GraphQL/ParseGraphQLSchema.js

+15-5
Original file line numberDiff line numberDiff line change
@@ -89,13 +89,14 @@ class ParseGraphQLSchema {
8989
this.graphQLCustomTypeDefs = params.graphQLCustomTypeDefs;
9090
this.appId = params.appId || requiredParameter('You must provide the appId!');
9191
this.schemaCache = SchemaCache;
92+
this.logCache = {};
9293
}
9394

9495
async load() {
9596
const { parseGraphQLConfig } = await this._initializeSchemaAndConfig();
9697
const parseClasses = await this._getClassesForSchema(parseGraphQLConfig);
9798
const functionNames = await this._getFunctionNames();
98-
const functionNamesString = JSON.stringify(functionNames);
99+
const functionNamesString = functionNames.join();
99100

100101
if (
101102
!this._hasSchemaInputChanged({
@@ -327,6 +328,14 @@ class ParseGraphQLSchema {
327328
return this.graphQLSchema;
328329
}
329330

331+
_logOnce(severity, message) {
332+
if (this.logCache[message]) {
333+
return;
334+
}
335+
this.log[severity](message);
336+
this.logCache[message] = true;
337+
}
338+
330339
addGraphQLType(type, throwError = false, ignoreReserved = false, ignoreConnection = false) {
331340
if (
332341
(!ignoreReserved && RESERVED_GRAPHQL_TYPE_NAMES.includes(type.name)) ||
@@ -337,7 +346,7 @@ class ParseGraphQLSchema {
337346
if (throwError) {
338347
throw new Error(message);
339348
}
340-
this.log.warn(message);
349+
this._logOnce('warn', message);
341350
return undefined;
342351
}
343352
this.graphQLTypes.push(type);
@@ -353,7 +362,7 @@ class ParseGraphQLSchema {
353362
if (throwError) {
354363
throw new Error(message);
355364
}
356-
this.log.warn(message);
365+
this._logOnce('warn', message);
357366
return undefined;
358367
}
359368
this.graphQLQueries[fieldName] = field;
@@ -369,7 +378,7 @@ class ParseGraphQLSchema {
369378
if (throwError) {
370379
throw new Error(message);
371380
}
372-
this.log.warn(message);
381+
this._logOnce('warn', message);
373382
return undefined;
374383
}
375384
this.graphQLMutations[fieldName] = field;
@@ -478,7 +487,8 @@ class ParseGraphQLSchema {
478487
if (/^[_a-zA-Z][_a-zA-Z0-9]*$/.test(functionName)) {
479488
return true;
480489
} else {
481-
this.log.warn(
490+
this._logOnce(
491+
'warn',
482492
`Function ${functionName} could not be added to the auto schema because GraphQL names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/.`
483493
);
484494
return false;

0 commit comments

Comments
 (0)