Skip to content

Commit 45feb12

Browse files
committed
feat: update graphql yoga to v3
1 parent d19acf1 commit 45feb12

12 files changed

+9171
-7961
lines changed

ci/definitionsCheck.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ const fs = require('fs').promises;
22
const { exec } = require('child_process');
33
const core = require('@actions/core');
44
const { nextTick } = require('process');
5-
const { AbortController } = require("node-abort-controller");
5+
const { AbortController } = require("@whatwg-node/fetch");
66
(async () => {
77
const [currentDefinitions, currentDocs] = await Promise.all([
88
fs.readFile('./src/Options/Definitions.js', 'utf8'),

package-lock.json

+9,127-7,809
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+4-10
Original file line numberDiff line numberDiff line change
@@ -20,23 +20,20 @@
2020
"license": "BSD-3-Clause",
2121
"dependencies": {
2222
"@babel/eslint-parser": "7.19.1",
23-
"@graphql-tools/merge": "8.3.6",
24-
"@graphql-tools/schema": "9.0.4",
25-
"@graphql-tools/utils": "8.12.0",
26-
"@graphql-yoga/node": "2.6.0",
23+
"@graphql-tools/schema": "9.0.12",
24+
"@graphql-tools/utils": "9.1.3",
2725
"@parse/fs-files-adapter": "1.2.2",
2826
"@parse/push-adapter": "4.1.2",
27+
"@whatwg-node/fetch": "0.5.3",
2928
"bcryptjs": "2.4.3",
30-
"body-parser": "1.20.1",
3129
"commander": "5.1.0",
32-
"cors": "2.8.5",
3330
"deepcopy": "2.1.0",
3431
"express": "4.18.2",
3532
"follow-redirects": "1.15.2",
3633
"graphql": "16.6.0",
3734
"graphql-list-fields": "2.0.2",
3835
"graphql-relay": "0.10.0",
39-
"graphql-tag": "2.12.6",
36+
"graphql-yoga": "3.1.1",
4037
"intersect": "1.0.1",
4138
"ip-range-check": "0.2.0",
4239
"jsonwebtoken": "8.5.1",
@@ -84,7 +81,6 @@
8481
"eslint": "8.26.0",
8582
"eslint-plugin-flowtype": "8.0.3",
8683
"flow-bin": "0.119.1",
87-
"form-data": "3.0.0",
8884
"graphql-tag": "2.12.6",
8985
"husky": "4.3.8",
9086
"jasmine": "3.5.0",
@@ -97,8 +93,6 @@
9793
"mock-mail-adapter": "file:spec/dependencies/mock-mail-adapter",
9894
"mongodb-runner": "4.8.1",
9995
"mongodb-version-list": "1.0.0",
100-
"node-fetch": "3.2.10",
101-
"node-abort-controller": "3.0.1",
10296
"nyc": "15.1.0",
10397
"prettier": "2.0.5",
10498
"semantic-release": "17.4.6",

spec/HTTPRequest.spec.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,14 @@
22

33
const httpRequest = require('../lib/request'),
44
HTTPResponse = require('../lib/request').HTTPResponse,
5-
bodyParser = require('body-parser'),
65
express = require('express');
76

87
const port = 13371;
98
const httpRequestServer = `http://localhost:${port}`;
109

1110
function startServer(done) {
1211
const app = express();
13-
app.use(bodyParser.json({ type: '*/*' }));
12+
app.use(express.json({ type: '*/*' }));
1413
app.get('/hello', function (req, res) {
1514
res.json({ response: 'OK' });
1615
});

spec/ParseGraphQLServer.spec.js

+3-86
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
const http = require('http');
22
const express = require('express');
33
const req = require('../lib/request');
4-
const fetch = (...args) => import('node-fetch').then(({ default: fetch }) => fetch(...args));
5-
const FormData = require('form-data');
4+
const { fetch, FormData } = require('@whatwg-node/fetch');
65
const ws = require('ws');
76
require('./helper');
87
const { updateCLP } = require('./support/dev');
@@ -30,6 +29,7 @@ const {
3029
GraphQLInputObjectType,
3130
GraphQLSchema,
3231
GraphQLList,
32+
GraphQLError,
3333
} = require('graphql');
3434
const { ParseServer } = require('../');
3535
const { ParseGraphQLServer } = require('../lib/GraphQL/ParseGraphQLServer');
@@ -99,54 +99,6 @@ describe('ParseGraphQLServer', () => {
9999
});
100100
});
101101

102-
describe('_getServer', () => {
103-
it('should only return new server on schema changes', async () => {
104-
parseGraphQLServer.server = undefined;
105-
const server1 = await parseGraphQLServer._getServer();
106-
const server2 = await parseGraphQLServer._getServer();
107-
expect(server1).toBe(server2);
108-
109-
// Trigger a schema change
110-
const obj = new Parse.Object('SomeClass');
111-
await obj.save();
112-
113-
const server3 = await parseGraphQLServer._getServer();
114-
const server4 = await parseGraphQLServer._getServer();
115-
expect(server3).not.toBe(server2);
116-
expect(server3).toBe(server4);
117-
});
118-
});
119-
120-
describe('_getGraphQLOptions', () => {
121-
const req = {
122-
info: new Object(),
123-
config: new Object(),
124-
auth: new Object(),
125-
};
126-
127-
it("should return schema and context with req's info, config and auth", async () => {
128-
const options = await parseGraphQLServer._getGraphQLOptions();
129-
expect(options.multipart).toEqual({
130-
fileSize: 20971520,
131-
});
132-
expect(options.schema).toEqual(parseGraphQLServer.parseGraphQLSchema.graphQLSchema);
133-
const contextResponse = options.context({ req });
134-
expect(contextResponse.info).toEqual(req.info);
135-
expect(contextResponse.config).toEqual(req.config);
136-
expect(contextResponse.auth).toEqual(req.auth);
137-
});
138-
139-
it('should load GraphQL schema in every call', async () => {
140-
const originalLoad = parseGraphQLServer.parseGraphQLSchema.load;
141-
let counter = 0;
142-
parseGraphQLServer.parseGraphQLSchema.load = () => ++counter;
143-
expect((await parseGraphQLServer._getGraphQLOptions(req)).schema).toEqual(1);
144-
expect((await parseGraphQLServer._getGraphQLOptions(req)).schema).toEqual(2);
145-
expect((await parseGraphQLServer._getGraphQLOptions(req)).schema).toEqual(3);
146-
parseGraphQLServer.parseGraphQLSchema.load = originalLoad;
147-
});
148-
});
149-
150102
describe('_transformMaxUploadSizeToBytes', () => {
151103
it('should transform to bytes', () => {
152104
expect(parseGraphQLServer._transformMaxUploadSizeToBytes('20mb')).toBe(20971520);
@@ -532,41 +484,6 @@ describe('ParseGraphQLServer', () => {
532484
expect(healthResponse.data.health).toBeTruthy();
533485
expect(checked).toBeTruthy();
534486
});
535-
536-
it('should handle Parse headers', async () => {
537-
const test = {
538-
context: ({ req: { info, config, auth } }) => {
539-
expect(req.info).toBeDefined();
540-
expect(req.config).toBeDefined();
541-
expect(req.auth).toBeDefined();
542-
return {
543-
info,
544-
config,
545-
auth,
546-
};
547-
},
548-
};
549-
const contextSpy = spyOn(test, 'context');
550-
const originalGetGraphQLOptions = parseGraphQLServer._getGraphQLOptions;
551-
parseGraphQLServer._getGraphQLOptions = async () => {
552-
return {
553-
schema: await parseGraphQLServer.parseGraphQLSchema.load(),
554-
context: test.context,
555-
};
556-
};
557-
const health = (
558-
await apolloClient.query({
559-
query: gql`
560-
query Health {
561-
health
562-
}
563-
`,
564-
})
565-
).data.health;
566-
expect(health).toBeTruthy();
567-
expect(contextSpy).toHaveBeenCalledTimes(1);
568-
parseGraphQLServer._getGraphQLOptions = originalGetGraphQLOptions;
569-
});
570487
});
571488

572489
describe('Playground', () => {
@@ -10945,7 +10862,7 @@ describe('ParseGraphQLServer', () => {
1094510862
errorQuery: {
1094610863
type: new GraphQLNonNull(GraphQLString),
1094710864
resolve: () => {
10948-
throw new Error('A test error');
10865+
throw new GraphQLError('A test error');
1094910866
},
1095010867
},
1095110868
customQueryWithAutoTypeReturn: {

spec/ParseHooks.spec.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ const request = require('../lib/request');
44
const triggers = require('../lib/triggers');
55
const HooksController = require('../lib/Controllers/HooksController').default;
66
const express = require('express');
7-
const bodyParser = require('body-parser');
87
const auth = require('../lib/Auth');
98
const Config = require('../lib/Config');
109

@@ -17,7 +16,7 @@ describe('Hooks', () => {
1716
beforeEach(done => {
1817
if (!app) {
1918
app = express();
20-
app.use(bodyParser.json({ type: '*/*' }));
19+
app.use(express.json({ type: '*/*' }));
2120
server = app.listen(port, undefined, done);
2221
} else {
2322
done();

spec/vulnerabilities.spec.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -140,11 +140,10 @@ describe('Vulnerabilities', () => {
140140

141141
it('denies creating a hook with polluted data', async () => {
142142
const express = require('express');
143-
const bodyParser = require('body-parser');
144143
const port = 34567;
145144
const hookServerURL = 'http://localhost:' + port;
146145
const app = express();
147-
app.use(bodyParser.json({ type: '*/*' }));
146+
app.use(express.json({ type: '*/*' }));
148147
const server = await new Promise(resolve => {
149148
const res = app.listen(port, undefined, () => resolve(res));
150149
});

src/GraphQL/ParseGraphQLSchema.js

+1-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import Parse from 'parse/node';
22
import { GraphQLSchema, GraphQLObjectType, DocumentNode, GraphQLNamedType } from 'graphql';
33
import { mergeSchemas } from '@graphql-tools/schema';
4-
import { mergeTypeDefs } from '@graphql-tools/merge';
54
import { isDeepStrictEqual } from 'util';
65
import requiredParameter from '../requiredParameter';
76
import * as defaultGraphQLTypes from './loaders/defaultGraphQLTypes';
@@ -284,10 +283,7 @@ class ParseGraphQLSchema {
284283
} else {
285284
this.graphQLSchema = mergeSchemas({
286285
schemas: [this.graphQLAutoSchema],
287-
typeDefs: mergeTypeDefs([
288-
this.graphQLCustomTypeDefs,
289-
this.graphQLSchemaDirectivesDefinitions,
290-
]),
286+
typeDefs: [this.graphQLCustomTypeDefs, this.graphQLSchemaDirectivesDefinitions],
291287
});
292288
this.graphQLSchema = this.graphQLSchemaDirectives(this.graphQLSchema);
293289
}

src/GraphQL/ParseGraphQLServer.js

+27-37
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import corsMiddleware from 'cors';
2-
import { createServer, renderGraphiQL } from '@graphql-yoga/node';
1+
import { createYoga, renderGraphiQL } from 'graphql-yoga';
2+
import { createFetch } from '@whatwg-node/fetch';
33
import { execute, subscribe } from 'graphql';
44
import { SubscriptionServer } from 'subscriptions-transport-ws';
55
import { handleParseErrors, handleParseHeaders } from '../middlewares';
@@ -27,39 +27,26 @@ class ParseGraphQLServer {
2727
graphQLCustomTypeDefs: this.config.graphQLCustomTypeDefs,
2828
appId: this.parseServer.config.appId,
2929
});
30-
}
31-
32-
async _getGraphQLOptions() {
33-
try {
34-
return {
35-
schema: await this.parseGraphQLSchema.load(),
36-
context: ({ req: { info, config, auth } }) => ({
37-
info,
38-
config,
39-
auth,
40-
}),
41-
maskedErrors: false,
42-
multipart: {
30+
this.yoga = createYoga({
31+
graphqlEndpoint: this.config.graphQLPath,
32+
schema: () => this.parseGraphQLSchema.load(),
33+
context: ({ req: { info, config, auth } }) => ({
34+
info,
35+
config,
36+
auth,
37+
}),
38+
maskedErrors: false,
39+
fetchAPI: createFetch({
40+
useNodeFetch: true,
41+
formDataLimits: {
4342
fileSize: this._transformMaxUploadSizeToBytes(
4443
this.parseServer.config.maxUploadSize || '20mb'
4544
),
4645
},
47-
};
48-
} catch (e) {
49-
this.log.error(e.stack || (typeof e.toString === 'function' && e.toString()) || e);
50-
throw e;
51-
}
52-
}
53-
54-
async _getServer() {
55-
const schemaRef = this.parseGraphQLSchema.graphQLSchema;
56-
const newSchemaRef = await this.parseGraphQLSchema.load();
57-
if (schemaRef === newSchemaRef && this._server) {
58-
return this._server;
59-
}
60-
const options = await this._getGraphQLOptions();
61-
this._server = createServer(options);
62-
return this._server;
46+
}),
47+
// Validation cache doesn't work with lazy schemas
48+
validationCache: false,
49+
});
6350
}
6451

6552
_transformMaxUploadSizeToBytes(maxUploadSize) {
@@ -80,13 +67,9 @@ class ParseGraphQLServer {
8067
requiredParameter('You must provide an Express.js app instance!');
8168
}
8269

83-
app.use(this.config.graphQLPath, corsMiddleware());
8470
app.use(this.config.graphQLPath, handleParseHeaders);
8571
app.use(this.config.graphQLPath, handleParseErrors);
86-
app.use(this.config.graphQLPath, async (req, res) => {
87-
const server = await this._getServer();
88-
return server(req, res);
89-
});
72+
app.use(this.config.graphQLPath, this.yoga);
9073
}
9174

9275
applyPlayground(app) {
@@ -119,7 +102,14 @@ class ParseGraphQLServer {
119102
execute,
120103
subscribe,
121104
onOperation: async (_message, params, webSocket) =>
122-
Object.assign({}, params, await this._getGraphQLOptions(webSocket.upgradeReq)),
105+
Object.assign({}, params, {
106+
schema: await this.parseGraphQLSchema.load(),
107+
context: {
108+
info: webSocket.upgradeReq.info,
109+
config: webSocket.upgradeReq.config,
110+
auth: webSocket.upgradeReq.auth,
111+
},
112+
}),
123113
},
124114
{
125115
server,

src/GraphQL/parseGraphQLUtils.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import Parse from 'parse/node';
2-
import { GraphQLYogaError } from '@graphql-yoga/node';
2+
import { GraphQLError } from 'graphql';
33

44
export function enforceMasterKeyAccess(auth) {
55
if (!auth.isMaster) {
@@ -16,7 +16,7 @@ export function toGraphQLError(error) {
1616
code = Parse.Error.INTERNAL_SERVER_ERROR;
1717
message = 'Internal server error';
1818
}
19-
return new GraphQLYogaError(message, { code });
19+
return new GraphQLError(message, { extensions: { code } });
2020
}
2121

2222
export const extractKeysAndInclude = selectedFields => {

src/ParseServer.js

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// ParseServer - open-source compatible API Server for Parse apps
22

33
var batch = require('./batch'),
4-
bodyParser = require('body-parser'),
54
express = require('express'),
65
middlewares = require('./middlewares'),
76
Parse = require('parse/node').Parse,
@@ -205,13 +204,13 @@ class ParseServer {
205204

206205
api.use(
207206
'/',
208-
bodyParser.urlencoded({ extended: false }),
207+
express.urlencoded({ extended: false }),
209208
pages.enableRouter
210209
? new PagesRouter(pages).expressRouter()
211210
: new PublicAPIRouter().expressRouter()
212211
);
213212

214-
api.use(bodyParser.json({ type: '*/*', limit: maxUploadSize }));
213+
api.use(express.json({ type: '*/*', limit: maxUploadSize }));
215214
api.use(middlewares.allowMethodOverride);
216215
api.use(middlewares.handleParseHeaders);
217216

src/Routers/FilesRouter.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import express from 'express';
2-
import BodyParser from 'body-parser';
32
import * as Middlewares from '../middlewares';
43
import Parse from 'parse/node';
54
import Config from '../Config';
@@ -46,7 +45,7 @@ export class FilesRouter {
4645

4746
router.post(
4847
'/files/:filename',
49-
BodyParser.raw({
48+
express.raw({
5049
type: () => {
5150
return true;
5251
},

0 commit comments

Comments
 (0)