Skip to content

Commit 5dfc1a0

Browse files
committed
feat: gql yoga 2 server
1 parent b84acd5 commit 5dfc1a0

9 files changed

+530
-1120
lines changed

package-lock.json

+330-1,006
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.9",
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

+129-54
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,21 @@ 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+
parseGraphQLServer.parseGraphQLSchema.schemaCache.clear();
109+
const server3 = await parseGraphQLServer._getServer();
110+
const server4 = await parseGraphQLServer._getServer();
111+
expect(server3).not.toBe(server2);
112+
expect(server3).toBe(server4);
113+
});
114+
});
115+
101116
describe('_getGraphQLOptions', () => {
102117
const req = {
103118
info: new Object(),
@@ -106,11 +121,15 @@ describe('ParseGraphQLServer', () => {
106121
};
107122

108123
it("should return schema and context with req's info, config and auth", async () => {
109-
const options = await parseGraphQLServer._getGraphQLOptions(req);
124+
const options = await parseGraphQLServer._getGraphQLOptions();
125+
expect(options.multipart).toEqual({
126+
fileSize: 20971520,
127+
});
110128
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);
129+
const contextResponse = options.context({ req });
130+
expect(contextResponse.info).toEqual(req.info);
131+
expect(contextResponse.config).toEqual(req.config);
132+
expect(contextResponse.auth).toEqual(req.auth);
114133
});
115134

116135
it('should load GraphQL schema in every call', async () => {
@@ -467,7 +486,7 @@ describe('ParseGraphQLServer', () => {
467486
}
468487
});
469488

470-
it('should be cors enabled', async () => {
489+
it('should be cors enabled and scope the response within the source origin', async () => {
471490
let checked = false;
472491
const apolloClient = new ApolloClient({
473492
link: new ApolloLink((operation, forward) => {
@@ -476,7 +495,7 @@ describe('ParseGraphQLServer', () => {
476495
const {
477496
response: { headers },
478497
} = context;
479-
expect(headers.get('access-control-allow-origin')).toEqual('*');
498+
expect(headers.get('access-control-allow-origin')).toEqual('http://someorigin.com');
480499
checked = true;
481500
return response;
482501
});
@@ -504,14 +523,25 @@ describe('ParseGraphQLServer', () => {
504523
});
505524

506525
it('should handle Parse headers', async () => {
507-
let checked = false;
526+
const test = {
527+
context: ({ req: { info, config, auth } }) => {
528+
expect(req.info).toBeDefined();
529+
expect(req.config).toBeDefined();
530+
expect(req.auth).toBeDefined();
531+
return {
532+
info,
533+
config,
534+
auth,
535+
};
536+
},
537+
};
538+
const contextSpy = spyOn(test, 'context');
508539
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);
540+
parseGraphQLServer._getGraphQLOptions = async () => {
541+
return {
542+
schema: await parseGraphQLServer.parseGraphQLSchema.load(),
543+
context: test.context,
544+
};
515545
};
516546
const health = (
517547
await apolloClient.query({
@@ -523,7 +553,7 @@ describe('ParseGraphQLServer', () => {
523553
})
524554
).data.health;
525555
expect(health).toBeTruthy();
526-
expect(checked).toBeTruthy();
556+
expect(contextSpy).toHaveBeenCalledTimes(1);
527557
parseGraphQLServer._getGraphQLOptions = originalGetGraphQLOptions;
528558
});
529559
});
@@ -6786,7 +6816,7 @@ describe('ParseGraphQLServer', () => {
67866816

67876817
expect(queryResult.data.customers.edges.length).toEqual(1);
67886818
} catch (e) {
6789-
console.log(JSON.stringify(e));
6819+
console.error(JSON.stringify(e));
67906820
}
67916821
});
67926822
});
@@ -9107,15 +9137,15 @@ describe('ParseGraphQLServer', () => {
91079137
'operations',
91089138
JSON.stringify({
91099139
query: `
9110-
mutation CreateFile($input: CreateFileInput!) {
9111-
createFile(input: $input) {
9112-
fileInfo {
9113-
name
9114-
url
9140+
mutation CreateFile($input: CreateFileInput!) {
9141+
createFile(input: $input) {
9142+
fileInfo {
9143+
name
9144+
url
9145+
}
91159146
}
91169147
}
9117-
}
9118-
`,
9148+
`,
91199149
variables: {
91209150
input: {
91219151
upload: null,
@@ -9176,46 +9206,46 @@ describe('ParseGraphQLServer', () => {
91769206
'operations',
91779207
JSON.stringify({
91789208
query: `
9179-
mutation CreateSomeObject(
9180-
$fields1: CreateSomeClassFieldsInput
9181-
$fields2: CreateSomeClassFieldsInput
9182-
$fields3: CreateSomeClassFieldsInput
9183-
) {
9184-
createSomeClass1: createSomeClass(
9185-
input: { fields: $fields1 }
9209+
mutation CreateSomeObject(
9210+
$fields1: CreateSomeClassFieldsInput
9211+
$fields2: CreateSomeClassFieldsInput
9212+
$fields3: CreateSomeClassFieldsInput
91869213
) {
9187-
someClass {
9188-
id
9189-
someField {
9190-
name
9191-
url
9214+
createSomeClass1: createSomeClass(
9215+
input: { fields: $fields1 }
9216+
) {
9217+
someClass {
9218+
id
9219+
someField {
9220+
name
9221+
url
9222+
}
91929223
}
91939224
}
9194-
}
9195-
createSomeClass2: createSomeClass(
9196-
input: { fields: $fields2 }
9197-
) {
9198-
someClass {
9199-
id
9200-
someField {
9201-
name
9202-
url
9225+
createSomeClass2: createSomeClass(
9226+
input: { fields: $fields2 }
9227+
) {
9228+
someClass {
9229+
id
9230+
someField {
9231+
name
9232+
url
9233+
}
92039234
}
92049235
}
9205-
}
9206-
createSomeClass3: createSomeClass(
9207-
input: { fields: $fields3 }
9208-
) {
9209-
someClass {
9210-
id
9211-
someField {
9212-
name
9213-
url
9236+
createSomeClass3: createSomeClass(
9237+
input: { fields: $fields3 }
9238+
) {
9239+
someClass {
9240+
id
9241+
someField {
9242+
name
9243+
url
9244+
}
92149245
}
92159246
}
92169247
}
9217-
}
9218-
`,
9248+
`,
92199249
variables: {
92209250
fields1: {
92219251
someField: { file: someFieldValue },
@@ -9344,6 +9374,51 @@ describe('ParseGraphQLServer', () => {
93449374
}
93459375
});
93469376

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