Skip to content

Commit

Permalink
feat: update graphql yoga to v3
Browse files Browse the repository at this point in the history
  • Loading branch information
ardatan committed Dec 23, 2022
1 parent d19acf1 commit d1c4bbc
Show file tree
Hide file tree
Showing 12 changed files with 9,194 additions and 7,974 deletions.
2 changes: 1 addition & 1 deletion ci/definitionsCheck.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const fs = require('fs').promises;
const { exec } = require('child_process');
const core = require('@actions/core');
const { nextTick } = require('process');
const { AbortController } = require("node-abort-controller");
const { AbortController } = require("@whatwg-node/fetch");
(async () => {
const [currentDefinitions, currentDocs] = await Promise.all([
fs.readFile('./src/Options/Definitions.js', 'utf8'),
Expand Down
16,936 changes: 9,127 additions & 7,809 deletions package-lock.json

Large diffs are not rendered by default.

14 changes: 4 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,20 @@
"license": "BSD-3-Clause",
"dependencies": {
"@babel/eslint-parser": "7.19.1",
"@graphql-tools/merge": "8.3.6",
"@graphql-tools/schema": "9.0.4",
"@graphql-tools/utils": "8.12.0",
"@graphql-yoga/node": "2.6.0",
"@graphql-tools/schema": "9.0.12",
"@graphql-tools/utils": "9.1.3",
"@parse/fs-files-adapter": "1.2.2",
"@parse/push-adapter": "4.1.2",
"@whatwg-node/fetch": "0.5.3",
"bcryptjs": "2.4.3",
"body-parser": "1.20.1",
"commander": "5.1.0",
"cors": "2.8.5",
"deepcopy": "2.1.0",
"express": "4.18.2",
"follow-redirects": "1.15.2",
"graphql": "16.6.0",
"graphql-list-fields": "2.0.2",
"graphql-relay": "0.10.0",
"graphql-tag": "2.12.6",
"graphql-yoga": "3.1.1",
"intersect": "1.0.1",
"ip-range-check": "0.2.0",
"jsonwebtoken": "8.5.1",
Expand Down Expand Up @@ -84,7 +81,6 @@
"eslint": "8.26.0",
"eslint-plugin-flowtype": "8.0.3",
"flow-bin": "0.119.1",
"form-data": "3.0.0",
"graphql-tag": "2.12.6",
"husky": "4.3.8",
"jasmine": "3.5.0",
Expand All @@ -97,8 +93,6 @@
"mock-mail-adapter": "file:spec/dependencies/mock-mail-adapter",
"mongodb-runner": "4.8.1",
"mongodb-version-list": "1.0.0",
"node-fetch": "3.2.10",
"node-abort-controller": "3.0.1",
"nyc": "15.1.0",
"prettier": "2.0.5",
"semantic-release": "17.4.6",
Expand Down
3 changes: 1 addition & 2 deletions spec/HTTPRequest.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@

const httpRequest = require('../lib/request'),
HTTPResponse = require('../lib/request').HTTPResponse,
bodyParser = require('body-parser'),
express = require('express');

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

function startServer(done) {
const app = express();
app.use(bodyParser.json({ type: '*/*' }));
app.use(express.json({ type: '*/*' }));
app.get('/hello', function (req, res) {
res.json({ response: 'OK' });
});
Expand Down
122 changes: 23 additions & 99 deletions spec/ParseGraphQLServer.spec.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
const http = require('http');
const express = require('express');
const req = require('../lib/request');
const fetch = (...args) => import('node-fetch').then(({ default: fetch }) => fetch(...args));
const FormData = require('form-data');
const { fetch, FormData, File } = require('@whatwg-node/fetch');
const ws = require('ws');
require('./helper');
const { updateCLP } = require('./support/dev');
Expand Down Expand Up @@ -30,6 +29,7 @@ const {
GraphQLInputObjectType,
GraphQLSchema,
GraphQLList,
GraphQLError,
} = require('graphql');
const { ParseServer } = require('../');
const { ParseGraphQLServer } = require('../lib/GraphQL/ParseGraphQLServer');
Expand Down Expand Up @@ -99,54 +99,6 @@ describe('ParseGraphQLServer', () => {
});
});

describe('_getServer', () => {
it('should only return new server on schema changes', async () => {
parseGraphQLServer.server = undefined;
const server1 = await parseGraphQLServer._getServer();
const server2 = await parseGraphQLServer._getServer();
expect(server1).toBe(server2);

// Trigger a schema change
const obj = new Parse.Object('SomeClass');
await obj.save();

const server3 = await parseGraphQLServer._getServer();
const server4 = await parseGraphQLServer._getServer();
expect(server3).not.toBe(server2);
expect(server3).toBe(server4);
});
});

describe('_getGraphQLOptions', () => {
const req = {
info: new Object(),
config: new Object(),
auth: new Object(),
};

it("should return schema and context with req's info, config and auth", async () => {
const options = await parseGraphQLServer._getGraphQLOptions();
expect(options.multipart).toEqual({
fileSize: 20971520,
});
expect(options.schema).toEqual(parseGraphQLServer.parseGraphQLSchema.graphQLSchema);
const contextResponse = options.context({ req });
expect(contextResponse.info).toEqual(req.info);
expect(contextResponse.config).toEqual(req.config);
expect(contextResponse.auth).toEqual(req.auth);
});

it('should load GraphQL schema in every call', async () => {
const originalLoad = parseGraphQLServer.parseGraphQLSchema.load;
let counter = 0;
parseGraphQLServer.parseGraphQLSchema.load = () => ++counter;
expect((await parseGraphQLServer._getGraphQLOptions(req)).schema).toEqual(1);
expect((await parseGraphQLServer._getGraphQLOptions(req)).schema).toEqual(2);
expect((await parseGraphQLServer._getGraphQLOptions(req)).schema).toEqual(3);
parseGraphQLServer.parseGraphQLSchema.load = originalLoad;
});
});

describe('_transformMaxUploadSizeToBytes', () => {
it('should transform to bytes', () => {
expect(parseGraphQLServer._transformMaxUploadSizeToBytes('20mb')).toBe(20971520);
Expand Down Expand Up @@ -532,41 +484,6 @@ describe('ParseGraphQLServer', () => {
expect(healthResponse.data.health).toBeTruthy();
expect(checked).toBeTruthy();
});

it('should handle Parse headers', async () => {
const test = {
context: ({ req: { info, config, auth } }) => {
expect(req.info).toBeDefined();
expect(req.config).toBeDefined();
expect(req.auth).toBeDefined();
return {
info,
config,
auth,
};
},
};
const contextSpy = spyOn(test, 'context');
const originalGetGraphQLOptions = parseGraphQLServer._getGraphQLOptions;
parseGraphQLServer._getGraphQLOptions = async () => {
return {
schema: await parseGraphQLServer.parseGraphQLSchema.load(),
context: test.context,
};
};
const health = (
await apolloClient.query({
query: gql`
query Health {
health
}
`,
})
).data.health;
expect(health).toBeTruthy();
expect(contextSpy).toHaveBeenCalledTimes(1);
parseGraphQLServer._getGraphQLOptions = originalGetGraphQLOptions;
});
});

describe('Playground', () => {
Expand Down Expand Up @@ -9327,10 +9244,13 @@ describe('ParseGraphQLServer', () => {
})
);
body.append('map', JSON.stringify({ 1: ['variables.input.upload'] }));
body.append('1', 'My File Content', {
filename: 'myFileName.txt',
contentType: 'text/plain',
});
body.append(
'1',
new File(['My File Content'], 'myFileName.txt', {
type: 'text/plain',
}),
'myFileName.txt'
);

let res = await fetch('http://localhost:13377/graphql', {
method: 'POST',
Expand Down Expand Up @@ -9439,10 +9359,11 @@ describe('ParseGraphQLServer', () => {
})
);
body2.append('map', JSON.stringify({ 1: ['variables.fields3.someField.upload'] }));
body2.append('1', 'My File Content', {
filename: 'myFileName.txt',
contentType: 'text/plain',
});
body2.append(
'1',
new File(['My File Content'], 'myFileName.txt', { type: 'text/plain' }),
'myFileName.txt'
);

res = await fetch('http://localhost:13377/graphql', {
method: 'POST',
Expand Down Expand Up @@ -9574,11 +9495,14 @@ describe('ParseGraphQLServer', () => {
body.append('map', JSON.stringify({ 1: ['variables.input.upload'] }));
body.append(
'1',
Buffer.alloc(parseGraphQLServer._transformMaxUploadSizeToBytes('2kb'), 1),
{
filename: 'myFileName.txt',
contentType: 'text/plain',
}
new File(
[Buffer.alloc(parseGraphQLServer._transformMaxUploadSizeToBytes('2kb'), 1)],
'myFileName.txt',
{
type: 'text/plain',
}
),
'myFileName.txt'
);

const res = await fetch('http://localhost:13377/graphql', {
Expand Down Expand Up @@ -10945,7 +10869,7 @@ describe('ParseGraphQLServer', () => {
errorQuery: {
type: new GraphQLNonNull(GraphQLString),
resolve: () => {
throw new Error('A test error');
throw new GraphQLError('A test error');
},
},
customQueryWithAutoTypeReturn: {
Expand Down
3 changes: 1 addition & 2 deletions spec/ParseHooks.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ const request = require('../lib/request');
const triggers = require('../lib/triggers');
const HooksController = require('../lib/Controllers/HooksController').default;
const express = require('express');
const bodyParser = require('body-parser');
const auth = require('../lib/Auth');
const Config = require('../lib/Config');

Expand All @@ -17,7 +16,7 @@ describe('Hooks', () => {
beforeEach(done => {
if (!app) {
app = express();
app.use(bodyParser.json({ type: '*/*' }));
app.use(express.json({ type: '*/*' }));
server = app.listen(port, undefined, done);
} else {
done();
Expand Down
3 changes: 1 addition & 2 deletions spec/vulnerabilities.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,11 +140,10 @@ describe('Vulnerabilities', () => {

it('denies creating a hook with polluted data', async () => {
const express = require('express');
const bodyParser = require('body-parser');
const port = 34567;
const hookServerURL = 'http://localhost:' + port;
const app = express();
app.use(bodyParser.json({ type: '*/*' }));
app.use(express.json({ type: '*/*' }));
const server = await new Promise(resolve => {
const res = app.listen(port, undefined, () => resolve(res));
});
Expand Down
6 changes: 1 addition & 5 deletions src/GraphQL/ParseGraphQLSchema.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import Parse from 'parse/node';
import { GraphQLSchema, GraphQLObjectType, DocumentNode, GraphQLNamedType } from 'graphql';
import { mergeSchemas } from '@graphql-tools/schema';
import { mergeTypeDefs } from '@graphql-tools/merge';
import { isDeepStrictEqual } from 'util';
import requiredParameter from '../requiredParameter';
import * as defaultGraphQLTypes from './loaders/defaultGraphQLTypes';
Expand Down Expand Up @@ -284,10 +283,7 @@ class ParseGraphQLSchema {
} else {
this.graphQLSchema = mergeSchemas({
schemas: [this.graphQLAutoSchema],
typeDefs: mergeTypeDefs([
this.graphQLCustomTypeDefs,
this.graphQLSchemaDirectivesDefinitions,
]),
typeDefs: [this.graphQLCustomTypeDefs, this.graphQLSchemaDirectivesDefinitions],
});
this.graphQLSchema = this.graphQLSchemaDirectives(this.graphQLSchema);
}
Expand Down
67 changes: 30 additions & 37 deletions src/GraphQL/ParseGraphQLServer.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import corsMiddleware from 'cors';
import { createServer, renderGraphiQL } from '@graphql-yoga/node';
import { createYoga, renderGraphiQL } from 'graphql-yoga';
import { createFetch } from '@whatwg-node/fetch';
import { execute, subscribe } from 'graphql';
import { SubscriptionServer } from 'subscriptions-transport-ws';
import { handleParseErrors, handleParseHeaders } from '../middlewares';
Expand Down Expand Up @@ -27,39 +27,29 @@ class ParseGraphQLServer {
graphQLCustomTypeDefs: this.config.graphQLCustomTypeDefs,
appId: this.parseServer.config.appId,
});
}

async _getGraphQLOptions() {
try {
return {
schema: await this.parseGraphQLSchema.load(),
context: ({ req: { info, config, auth } }) => ({
info,
config,
auth,
}),
maskedErrors: false,
multipart: {
this.yoga = createYoga({
graphqlEndpoint: this.config.graphQLPath,
schema: () => this.parseGraphQLSchema.load(),
context: ({ req: { info, config, auth } }) => ({
info,
config,
auth,
}),
maskedErrors: false,
fetchAPI: createFetch({
useNodeFetch: true,
formDataLimits: {
fileSize: this._transformMaxUploadSizeToBytes(
this.parseServer.config.maxUploadSize || '20mb'
),
},
};
} catch (e) {
this.log.error(e.stack || (typeof e.toString === 'function' && e.toString()) || e);
throw e;
}
}

async _getServer() {
const schemaRef = this.parseGraphQLSchema.graphQLSchema;
const newSchemaRef = await this.parseGraphQLSchema.load();
if (schemaRef === newSchemaRef && this._server) {
return this._server;
}
const options = await this._getGraphQLOptions();
this._server = createServer(options);
return this._server;
}),
graphiql: {
title: 'ParseGraphiQL',
},
// Validation cache doesn't work with lazy schemas
validationCache: false,
});
}

_transformMaxUploadSizeToBytes(maxUploadSize) {
Expand All @@ -80,13 +70,9 @@ class ParseGraphQLServer {
requiredParameter('You must provide an Express.js app instance!');
}

app.use(this.config.graphQLPath, corsMiddleware());
app.use(this.config.graphQLPath, handleParseHeaders);
app.use(this.config.graphQLPath, handleParseErrors);
app.use(this.config.graphQLPath, async (req, res) => {
const server = await this._getServer();
return server(req, res);
});
app.use(this.config.graphQLPath, this.yoga);
}

applyPlayground(app) {
Expand Down Expand Up @@ -119,7 +105,14 @@ class ParseGraphQLServer {
execute,
subscribe,
onOperation: async (_message, params, webSocket) =>
Object.assign({}, params, await this._getGraphQLOptions(webSocket.upgradeReq)),
Object.assign({}, params, {
schema: await this.parseGraphQLSchema.load(),
context: {
info: webSocket.upgradeReq.info,
config: webSocket.upgradeReq.config,
auth: webSocket.upgradeReq.auth,
},
}),
},
{
server,
Expand Down
Loading

0 comments on commit d1c4bbc

Please sign in to comment.