From 0c2fbc3dece3a185e5d5740daa998561c04f8b60 Mon Sep 17 00:00:00 2001 From: Leonardo Gatica Date: Tue, 2 Oct 2018 16:35:26 -0300 Subject: [PATCH 01/10] feat(uploads): replace apollo-upload-server v5 with graphql-upload v8 --- docs/source/whats-new.md | 2 +- package-lock.json | 113 ++++++++++-------- packages/apollo-server-core/package.json | 2 +- .../apollo-server-core/src/ApolloServer.ts | 4 +- packages/apollo-server-core/src/index.ts | 2 +- packages/apollo-server-core/src/types.ts | 2 +- packages/apollo-server-express/package.json | 2 +- .../apollo-server-express/src/ApolloServer.ts | 4 +- packages/apollo-server-hapi/package.json | 4 +- .../apollo-server-hapi/src/ApolloServer.ts | 10 +- packages/apollo-server-koa/package.json | 2 +- .../apollo-server-koa/src/ApolloServer.ts | 8 +- packages/apollo-server-micro/package.json | 2 +- .../apollo-server-micro/src/ApolloServer.ts | 10 +- .../apollo-upload-server/index.d.ts | 25 ---- types/graphql-upload/index.d.ts | 30 +++++ 16 files changed, 120 insertions(+), 102 deletions(-) delete mode 100644 types/@apollographql/apollo-upload-server/index.d.ts create mode 100644 types/graphql-upload/index.d.ts diff --git a/docs/source/whats-new.md b/docs/source/whats-new.md index bfabe6a69d4..f60e1dc3cad 100644 --- a/docs/source/whats-new.md +++ b/docs/source/whats-new.md @@ -246,7 +246,7 @@ const resolvers = { Mutation: { singleUpload: (parent, args) => { return args.file.then(file => { - //Contents of Upload scalar: https://github.com/jaydenseric/apollo-upload-server#upload-scalar + //Contents of Upload scalar: https://github.com/jaydenseric/graphql-upload#class-graphqlupload //file.stream is a node stream that contains the contents of the uploaded file //node stream api: https://nodejs.org/api/stream.html return file; diff --git a/package-lock.json b/package-lock.json index df0beeee016..ca18a74c91e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,21 +4,27 @@ "lockfileVersion": 1, "dependencies": { "@apollographql/apollo-tools": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/@apollographql/apollo-tools/-/apollo-tools-0.2.6.tgz", - "integrity": "sha512-IKn35EzAHFQw4x8THux+fkVLkFNs8HDzIgKqcU5ZMfkDt3MOa1nfMbGVoVh2YdvpIjPtyUR3kvAAVQwmRPRjAQ==", - "requires": { - "apollo-env": "0.2.3" - } - }, - "@apollographql/apollo-upload-server": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/@apollographql/apollo-upload-server/-/apollo-upload-server-5.0.3.tgz", - "integrity": "sha512-tGAp3ULNyoA8b5o9LsU2Lq6SwgVPUOKAqKywu2liEtTvrFSGPrObwanhYwArq3GPeOqp2bi+JknSJCIU3oQN1Q==", + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@apollographql/apollo-tools/-/apollo-tools-0.2.8.tgz", + "integrity": "sha512-A7FTUigtpGCFBaLT1ILicdjM6pZ7LQNw7Vgos0t4aLYtvlKO/L1nMi/NO7bPypzZaJSToTgcxHJPRydP1Md+Kw==", "requires": { - "@babel/runtime-corejs2": "^7.0.0-rc.1", - "busboy": "^0.2.14", - "object-path": "^0.11.4" + "apollo-env": "0.2.5" + }, + "dependencies": { + "apollo-env": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/apollo-env/-/apollo-env-0.2.5.tgz", + "integrity": "sha512-Gc7TEbwCl7jJVutnn8TWfzNSkrrqyoo0DP92BQJFU9pZbJhpidoXf2Sw1YwOJl82rRKH3ujM3C8vdZLOgpFcFA==", + "requires": { + "core-js": "^3.0.0-beta.3", + "node-fetch": "^2.2.0" + } + }, + "core-js": { + "version": "3.0.0-beta.3", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.0.0-beta.3.tgz", + "integrity": "sha512-kM/OfrnMThP5PwGAj5HhQLdjUqzjrllqN2EVnk/X9qrLsfYjR2hzZ+E/8CzH0xuosexZtqMTLQrk//BULrBj9w==" + } } }, "@apollographql/graphql-playground-html": { @@ -63,15 +69,6 @@ "regenerator-runtime": "^0.12.0" } }, - "@babel/runtime-corejs2": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.1.2.tgz", - "integrity": "sha512-drxaPByExlcRDKW4ZLubUO4ZkI8/8ax9k9wve1aEthdLKFzjB7XRkOQ0xoTIWGxqdDnWDElkjYq77bt7yrcYJQ==", - "requires": { - "core-js": "^2.5.7", - "regenerator-runtime": "^0.12.0" - } - }, "@iamstarkov/listr-update-renderer": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/@iamstarkov/listr-update-renderer/-/listr-update-renderer-0.4.1.tgz", @@ -2039,22 +2036,6 @@ "protobufjs": "^6.8.6" } }, - "apollo-env": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/apollo-env/-/apollo-env-0.2.3.tgz", - "integrity": "sha512-7hRWPG8tWQNXCNrZsOMqxtkHGqhTzFgsw7RpFDyC1xgcZvVFCJvthWgWO07EhcaHhRqvrPxmicLnoTw2e/iCsA==", - "requires": { - "core-js": "^3.0.0-beta.3", - "node-fetch": "^2.2.0" - }, - "dependencies": { - "core-js": { - "version": "3.0.0-beta.3", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.0.0-beta.3.tgz", - "integrity": "sha512-kM/OfrnMThP5PwGAj5HhQLdjUqzjrllqN2EVnk/X9qrLsfYjR2hzZ+E/8CzH0xuosexZtqMTLQrk//BULrBj9w==" - } - } - }, "apollo-fetch": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/apollo-fetch/-/apollo-fetch-0.7.0.tgz", @@ -2177,7 +2158,6 @@ "version": "file:packages/apollo-server-core", "requires": { "@apollographql/apollo-tools": "^0.2.6", - "@apollographql/apollo-upload-server": "^5.0.3", "@apollographql/graphql-playground-html": "^1.6.6", "@types/ws": "^6.0.0", "apollo-cache-control": "file:packages/apollo-cache-control", @@ -2192,6 +2172,7 @@ "graphql-subscriptions": "^1.0.0", "graphql-tag": "^2.9.2", "graphql-tools": "^4.0.0", + "graphql-upload": "^8.0.0", "json-stable-stringify": "^1.0.1", "lodash": "^4.17.10", "subscriptions-transport-ws": "^0.9.11", @@ -2211,7 +2192,6 @@ "apollo-server-express": { "version": "file:packages/apollo-server-express", "requires": { - "@apollographql/apollo-upload-server": "^5.0.3", "@apollographql/graphql-playground-html": "^1.6.6", "@types/accepts": "^1.3.5", "@types/body-parser": "1.17.0", @@ -2223,6 +2203,7 @@ "cors": "^2.8.4", "graphql-subscriptions": "^1.0.0", "graphql-tools": "^4.0.0", + "graphql-upload": "^8.0.0", "type-is": "^1.6.16" }, "dependencies": { @@ -2235,19 +2216,13 @@ "apollo-server-hapi": { "version": "file:packages/apollo-server-hapi", "requires": { - "@apollographql/apollo-upload-server": "^5.0.3", "@apollographql/graphql-playground-html": "^1.6.6", "accept": "^3.0.2", "apollo-server-core": "file:packages/apollo-server-core", "boom": "^7.1.0", "graphql-subscriptions": "^1.0.0", - "graphql-tools": "^4.0.0" - }, - "dependencies": { - "@apollographql/graphql-playground-html": { - "version": "1.6.6", - "bundled": true - } + "graphql-tools": "^4.0.0", + "graphql-upload": "^8.0.0" } }, "apollo-server-integration-testsuite": { @@ -2259,7 +2234,6 @@ "apollo-server-koa": { "version": "file:packages/apollo-server-koa", "requires": { - "@apollographql/apollo-upload-server": "^5.0.3", "@apollographql/graphql-playground-html": "^1.6.6", "@koa/cors": "^2.2.1", "@types/accepts": "^1.3.5", @@ -2272,6 +2246,7 @@ "apollo-server-core": "file:packages/apollo-server-core", "graphql-subscriptions": "^1.0.0", "graphql-tools": "^4.0.0", + "graphql-upload": "^8.0.0", "koa": "2.6.2", "koa-bodyparser": "^3.0.0", "koa-router": "^7.4.0", @@ -2302,10 +2277,10 @@ "apollo-server-micro": { "version": "file:packages/apollo-server-micro", "requires": { - "@apollographql/apollo-upload-server": "^5.0.3", "@apollographql/graphql-playground-html": "^1.6.6", "accept": "^3.0.2", "apollo-server-core": "file:packages/apollo-server-core", + "graphql-upload": "^8.0.0", "micro": "^9.3.2" }, "dependencies": { @@ -3849,7 +3824,8 @@ "core-js": { "version": "2.5.7", "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz", - "integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==" + "integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==", + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -5295,6 +5271,11 @@ } } }, + "fs-capacitor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fs-capacitor/-/fs-capacitor-1.0.1.tgz", + "integrity": "sha512-XdZK0Q78WP29Vm3FGgJRhRhrBm51PagovzWtW2kJ3Q6cYJbGtZqWSGTSPwvtEkyjIirFd7b8Yes/dpOYjt4RRQ==" + }, "fs-extra": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", @@ -6319,6 +6300,31 @@ "uuid": "^3.1.0" } }, + "graphql-upload": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/graphql-upload/-/graphql-upload-8.0.0.tgz", + "integrity": "sha512-DlEi6+Kblj6gAAA5XJahJl1+UfcXICiFypJYIxd4zK26W2/LKm6nwLmSWjECwbAkz/OtB+oSWGb5gqXCDQOLqg==", + "requires": { + "busboy": "^0.2.14", + "fs-capacitor": "^1.0.0", + "http-errors": "^1.7.1", + "object-path": "^0.11.4" + }, + "dependencies": { + "http-errors": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.1.tgz", + "integrity": "sha512-jWEUgtZWGSMba9I1N3gc1HmvpBUaNC9vDdA46yScAdp+C5rdEuKWUBLWTQpW9FwSWSbYYs++b6SDCxf9UEJzfw==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + } + } + }, "growly": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", @@ -11023,7 +11029,8 @@ "regenerator-runtime": { "version": "0.12.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz", - "integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==" + "integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==", + "dev": true }, "regex-cache": { "version": "0.4.4", diff --git a/packages/apollo-server-core/package.json b/packages/apollo-server-core/package.json index 38a530f85ff..fdb032e930a 100644 --- a/packages/apollo-server-core/package.json +++ b/packages/apollo-server-core/package.json @@ -25,7 +25,6 @@ }, "dependencies": { "@apollographql/apollo-tools": "^0.2.6", - "@apollographql/apollo-upload-server": "^5.0.3", "@apollographql/graphql-playground-html": "^1.6.6", "@types/ws": "^6.0.0", "apollo-cache-control": "file:../apollo-cache-control", @@ -40,6 +39,7 @@ "graphql-subscriptions": "^1.0.0", "graphql-tag": "^2.9.2", "graphql-tools": "^4.0.0", + "graphql-upload": "^8.0.0", "json-stable-stringify": "^1.0.1", "lodash": "^4.17.10", "subscriptions-transport-ws": "^0.9.11", diff --git a/packages/apollo-server-core/src/ApolloServer.ts b/packages/apollo-server-core/src/ApolloServer.ts index 7b715ba005b..41c97fe3a52 100644 --- a/packages/apollo-server-core/src/ApolloServer.ts +++ b/packages/apollo-server-core/src/ApolloServer.ts @@ -249,9 +249,7 @@ export class ApolloServerBase { ); if (this.uploadsConfig) { - const { - GraphQLUpload, - } = require('@apollographql/apollo-upload-server'); + const { GraphQLUpload } = require('graphql-upload'); if (resolvers && !resolvers.Upload) { resolvers.Upload = GraphQLUpload; } diff --git a/packages/apollo-server-core/src/index.ts b/packages/apollo-server-core/src/index.ts index c000b836c45..f48a9675795 100644 --- a/packages/apollo-server-core/src/index.ts +++ b/packages/apollo-server-core/src/index.ts @@ -42,5 +42,5 @@ export const gql: ( ) => DocumentNode = gqlTag; import { GraphQLScalarType } from 'graphql'; -import { GraphQLUpload as UploadScalar } from '@apollographql/apollo-upload-server'; +import { GraphQLUpload as UploadScalar } from 'graphql-upload'; export const GraphQLUpload = UploadScalar as GraphQLScalarType; diff --git a/packages/apollo-server-core/src/types.ts b/packages/apollo-server-core/src/types.ts index 8a6ddc635ab..36d050ad487 100644 --- a/packages/apollo-server-core/src/types.ts +++ b/packages/apollo-server-core/src/types.ts @@ -72,7 +72,7 @@ export interface Config plugins?: PluginDefinition[]; persistedQueries?: PersistedQueryOptions | false; subscriptions?: Partial | string | false; - //https://github.com/jaydenseric/apollo-upload-server#options + //https://github.com/jaydenseric/graphql-upload#type-uploadoptions uploads?: boolean | FileUploadOptions; playground?: PlaygroundConfig; } diff --git a/packages/apollo-server-express/package.json b/packages/apollo-server-express/package.json index 8ec92f56977..af6907b039f 100644 --- a/packages/apollo-server-express/package.json +++ b/packages/apollo-server-express/package.json @@ -26,7 +26,6 @@ "node": ">=6" }, "dependencies": { - "@apollographql/apollo-upload-server": "^5.0.3", "@apollographql/graphql-playground-html": "^1.6.6", "@types/accepts": "^1.3.5", "@types/body-parser": "1.17.0", @@ -38,6 +37,7 @@ "cors": "^2.8.4", "graphql-subscriptions": "^1.0.0", "graphql-tools": "^4.0.0", + "graphql-upload": "^8.0.0", "type-is": "^1.6.16" }, "devDependencies": { diff --git a/packages/apollo-server-express/src/ApolloServer.ts b/packages/apollo-server-express/src/ApolloServer.ts index e08d2454433..be7f9255cf5 100644 --- a/packages/apollo-server-express/src/ApolloServer.ts +++ b/packages/apollo-server-express/src/ApolloServer.ts @@ -16,7 +16,7 @@ import typeis from 'type-is'; import { graphqlExpress } from './expressApollo'; -import { processRequest as processFileUploads } from '@apollographql/apollo-upload-server'; +import { processRequest as processFileUploads } from 'graphql-upload'; export { GraphQLOptions, GraphQLExtension } from 'apollo-server-core'; @@ -45,7 +45,7 @@ const fileUploadMiddleware = ( ) => { // Note: we use typeis directly instead of via req.is for connect support. if (typeis(req, ['multipart/form-data'])) { - processFileUploads(req, uploadsConfig) + processFileUploads(req, res, uploadsConfig) .then(body => { req.body = body; next(); diff --git a/packages/apollo-server-hapi/package.json b/packages/apollo-server-hapi/package.json index a28e1844c14..f4fc3c1db53 100644 --- a/packages/apollo-server-hapi/package.json +++ b/packages/apollo-server-hapi/package.json @@ -25,13 +25,13 @@ "node": ">=8" }, "dependencies": { - "@apollographql/apollo-upload-server": "^5.0.3", "@apollographql/graphql-playground-html": "^1.6.6", "accept": "^3.0.2", "apollo-server-core": "file:../apollo-server-core", "boom": "^7.1.0", "graphql-subscriptions": "^1.0.0", - "graphql-tools": "^4.0.0" + "graphql-tools": "^4.0.0", + "graphql-upload": "^8.0.0" }, "devDependencies": { "apollo-server-integration-testsuite": "file:../apollo-server-integration-testsuite" diff --git a/packages/apollo-server-hapi/src/ApolloServer.ts b/packages/apollo-server-hapi/src/ApolloServer.ts index 5bc2ca8adb7..40d160f8697 100644 --- a/packages/apollo-server-hapi/src/ApolloServer.ts +++ b/packages/apollo-server-hapi/src/ApolloServer.ts @@ -4,7 +4,7 @@ import { renderPlaygroundPage, RenderPageOptions as PlaygroundRenderPageOptions, } from '@apollographql/graphql-playground-html'; -import { processRequest as processFileUploads } from '@apollographql/apollo-upload-server'; +import { processRequest as processFileUploads } from 'graphql-upload'; import { graphqlHapi } from './hapiApollo'; @@ -16,10 +16,14 @@ import { } from 'apollo-server-core'; function handleFileUploads(uploadsConfig: FileUploadOptions) { - return async (request: hapi.Request) => { + return async (request: hapi.Request, _h?: hapi.ResponseToolkit) => { if (request.mime === 'multipart/form-data') { Object.defineProperty(request, 'payload', { - value: await processFileUploads(request, uploadsConfig), + value: await processFileUploads( + request, + request.response, + uploadsConfig, + ), writable: false, }); } diff --git a/packages/apollo-server-koa/package.json b/packages/apollo-server-koa/package.json index 3fe3933ae59..70e9a6c6ec8 100644 --- a/packages/apollo-server-koa/package.json +++ b/packages/apollo-server-koa/package.json @@ -25,7 +25,6 @@ "node": ">=6" }, "dependencies": { - "@apollographql/apollo-upload-server": "^5.0.3", "@apollographql/graphql-playground-html": "^1.6.6", "@koa/cors": "^2.2.1", "@types/accepts": "^1.3.5", @@ -38,6 +37,7 @@ "apollo-server-core": "file:../apollo-server-core", "graphql-subscriptions": "^1.0.0", "graphql-tools": "^4.0.0", + "graphql-upload": "^8.0.0", "koa": "2.6.2", "koa-bodyparser": "^3.0.0", "koa-router": "^7.4.0", diff --git a/packages/apollo-server-koa/src/ApolloServer.ts b/packages/apollo-server-koa/src/ApolloServer.ts index ef5f6aabdc4..ec7871a81a6 100644 --- a/packages/apollo-server-koa/src/ApolloServer.ts +++ b/packages/apollo-server-koa/src/ApolloServer.ts @@ -12,7 +12,7 @@ import typeis from 'type-is'; import { graphqlKoa } from './koaApollo'; -import { processRequest as processFileUploads } from '@apollographql/apollo-upload-server'; +import { processRequest as processFileUploads } from 'graphql-upload'; export { GraphQLOptions, GraphQLExtension } from 'apollo-server-core'; import { GraphQLOptions, FileUploadOptions } from 'apollo-server-core'; @@ -32,7 +32,11 @@ const fileUploadMiddleware = ( ) => async (ctx: Koa.Context, next: Function) => { if (typeis(ctx.req, ['multipart/form-data'])) { try { - ctx.request.body = await processFileUploads(ctx.req, uploadsConfig); + ctx.request.body = await processFileUploads( + ctx.req, + ctx.res, + uploadsConfig, + ); return next(); } catch (error) { if (error.status && error.expose) ctx.status = error.status; diff --git a/packages/apollo-server-micro/package.json b/packages/apollo-server-micro/package.json index c989cffc6b1..800ec0fa43b 100644 --- a/packages/apollo-server-micro/package.json +++ b/packages/apollo-server-micro/package.json @@ -23,10 +23,10 @@ }, "homepage": "https://github.com/apollographql/apollo-server#readme", "dependencies": { - "@apollographql/apollo-upload-server": "^5.0.3", "@apollographql/graphql-playground-html": "^1.6.6", "accept": "^3.0.2", "apollo-server-core": "file:../apollo-server-core", + "graphql-upload": "^8.0.0", "micro": "^9.3.2" }, "devDependencies": { diff --git a/packages/apollo-server-micro/src/ApolloServer.ts b/packages/apollo-server-micro/src/ApolloServer.ts index 503a5860853..6bdbee16e96 100644 --- a/packages/apollo-server-micro/src/ApolloServer.ts +++ b/packages/apollo-server-micro/src/ApolloServer.ts @@ -1,5 +1,5 @@ import { ApolloServerBase, GraphQLOptions } from 'apollo-server-core'; -import { processRequest as processFileUploads } from '@apollographql/apollo-upload-server'; +import { processRequest as processFileUploads } from 'graphql-upload'; import { ServerResponse } from 'http'; import { send } from 'micro'; import { renderPlaygroundPage } from '@apollographql/graphql-playground-html'; @@ -39,7 +39,7 @@ export class ApolloServer extends ApolloServerBase { await promiseWillStart; - await this.handleFileUploads(req); + await this.handleFileUploads(req, res); (await this.handleHealthCheck({ req, @@ -160,15 +160,15 @@ export class ApolloServer extends ApolloServerBase { } // If file uploads are detected, prepare them for easier handling with - // the help of `apollo-upload-server`. - private async handleFileUploads(req: MicroRequest) { + // the help of `graphql-upload`. + private async handleFileUploads(req: MicroRequest, res: ServerResponse) { const contentType = req.headers['content-type']; if ( this.uploadsConfig && contentType && contentType.startsWith('multipart/form-data') ) { - req.filePayload = await processFileUploads(req, this.uploadsConfig); + req.filePayload = await processFileUploads(req, res, this.uploadsConfig); } } } diff --git a/types/@apollographql/apollo-upload-server/index.d.ts b/types/@apollographql/apollo-upload-server/index.d.ts deleted file mode 100644 index 71bd0ea4514..00000000000 --- a/types/@apollographql/apollo-upload-server/index.d.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { GraphQLScalarType } from 'graphql'; - -export const GraphQLUpload: GraphQLScalarType; - -export interface ApolloUploadOptions { - /** - * Max allowed non-file multipart form field size in bytes; enough for your queries (default: 1 MB) - */ - maxFieldSize?: number; - /** - * Max allowed file size in bytes (default: Infinity) - */ - maxFileSize?: number; - /** - * Max allowed number of files (default: Infinity) - */ - maxFiles?: number; -} - -export type Request = any; - -export function processRequest( - request: Request, - options?: ApolloUploadOptions, -): Promise; diff --git a/types/graphql-upload/index.d.ts b/types/graphql-upload/index.d.ts new file mode 100644 index 00000000000..3516f84fa35 --- /dev/null +++ b/types/graphql-upload/index.d.ts @@ -0,0 +1,30 @@ +declare module 'graphql-upload' { + import { GraphQLScalarType } from 'graphql'; + + export const GraphQLUpload: GraphQLScalarType; + + export interface ApolloUploadOptions { + /** + * Max allowed non-file multipart form field size in bytes; enough for your queries (default: 1 MB) + */ + maxFieldSize?: number; + /** + * Max allowed file size in bytes (default: Infinity) + */ + maxFileSize?: number; + /** + * Max allowed number of files (default: Infinity) + */ + maxFiles?: number; + } + + export type Request = any; + + export type Response = any; + + export function processRequest( + request: Request, + response: Response, + options?: ApolloUploadOptions, + ): Promise; +} From 9c563fae5048f5e77ca2716e3aa3d47044861abf Mon Sep 17 00:00:00 2001 From: Jesse Rosenberger Date: Thu, 29 Nov 2018 22:23:51 +0200 Subject: [PATCH 02/10] Throw error at startup when file uploads are enabled on Node.js < 8.5.0. Due to changes in the third-party `graphql-upload` package which Apollo Server utilizes to implement out-of-the-box file upload functionality, we must drop support for file uploads in versions of the Node.js engine prior to v8.5.0. Since file uploads are supported by default in Apollo Server 2.x, and there is an explicit dependency on `graphql-upload`, we must prevent users who are affected by this mid-major-release deprecation by being surprised by the sudden lack of upload support. By `throw`-ing an error at server startup for affected users, we certainly are breaking a semantic versioning agreement for these users, however with a relatively simple ergonomic (setting `uploads: false`) we allow those users who are NOT utilizing file uploads (as we believe is the case with a majority) to continue using their version of Node.js until it reaches the end of its supported lifetime (as dictated by its Long Term Support agreement with the Node.js Foundation). If we did not `throw` the error at server start-up, those affected may not notice since they may update and start their updated server without noticing the impending chance of failure when someone tries updating! Apollo Server 2.x has attempted to maintain full compatibility with versions of Node.js which are still under Long Term Support agreements with the Node.js Foundation. While this continues to mostly be true, file uploads are an exception which we've now had to make. Third-party open-source projects must absolutely do what's best for their project. From an architecture standpoint, I suspect that we (the designers behind Apollo Server) are mostly to blame for this. Namely, it's unfortunate that we had made such an incredibly coupled integration with a third-party package that we restricted our users from incrementally adopting the changes (and new/improved functionality) of, in this particular case, the `graphql-upload` package. I hope we can take better care with decisions like this in the future! Lastly, this commit also adds documentation to help those affected. --- CHANGELOG.md | 8 ++++ docs/_config.yml | 1 + docs/source/migration-file-uploads.md | 29 ++++++++++++++ package-lock.json | 6 +-- .../apollo-server-core/src/ApolloServer.ts | 38 +++++++++++++++++++ packages/apollo-server-core/src/index.ts | 16 +++++++- .../src/processFileUploads.ts | 16 ++++++++ .../src/utils/supportsUploadsInNode.ts | 21 ++++++++++ packages/apollo-server-express/package.json | 1 - .../apollo-server-express/src/ApolloServer.ts | 10 +++-- packages/apollo-server-hapi/package.json | 3 +- .../apollo-server-hapi/src/ApolloServer.ts | 9 +++-- packages/apollo-server-koa/package.json | 1 - .../apollo-server-koa/src/ApolloServer.ts | 10 +++-- packages/apollo-server-micro/package.json | 1 - .../apollo-server-micro/src/ApolloServer.ts | 15 ++++++-- 16 files changed, 159 insertions(+), 26 deletions(-) create mode 100644 docs/source/migration-file-uploads.md create mode 100644 packages/apollo-server-core/src/processFileUploads.ts create mode 100644 packages/apollo-server-core/src/utils/supportsUploadsInNode.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 9541f732de5..adcaff8d074 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ ### vNEXT +- **BREAKING FOR NODE.JS <= 8.5.0 ONLY**: To continue using Apollo Server 2.x in versions of Node.js prior to v8.5.0, file uploads must be disabled by setting `uploads: false` on the `ApolloServer` constructor options. Without explicit disabling file-uploads, the server will `throw` at launch (with instructions and a link to our documentation). + + This early deprecation is due to changes in the third-party `graphql-upload` package which Apollo Server utilizes to implement out-of-the-box file upload functionality. While, in general, Apollo Server 2.x aims to support all Node.js versions which were under an LTS policy at the time of its release, we felt this required an exception. By `throw`-ing when `uploads` is not explicitly set to `false`, we aim to make it clear immediately (rather than surprisingly) that this deprecation has taken effect. + + While Node.js 6.x is covered by a [Long Term Support agreement by the Node.js Foundation](https://github.com/nodejs/Release#release-schedule) until April 2019, there are substantial performance (e.g. [V8](https://v8.dev/) improvements) and language changes (e.g. "modern" ECMAScript support) offered by newer Node.js engines (e.g. 8.x, 10.x). We encourage _all users_ of Apollo Server to update to newer LTS versions of Node.js prior to the "end-of-life" dates for their current server version. + + **We intend to drop support for Node.js 6.x in the next major version of Apollo Server.** + ### v2.2.5 - Follow-up on the update to `graphql-playground-html` in previous release by also bumping the minor version of the `graphql-playground-react` dependency to `1.7.10` — which is the version requested from the from the CDN bundle by `graphql-playground-html`. [PR #2037](https://github.com/apollographql/apollo-server/pull/2037) diff --git a/docs/_config.yml b/docs/_config.yml index 6d007ec2205..0df030fa1b9 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -44,6 +44,7 @@ sidebar_categories: Migration: - migration-two-dot - migration-engine + - migration-file-uploads github_repo: apollographql/apollo-server content_root: docs/source diff --git a/docs/source/migration-file-uploads.md b/docs/source/migration-file-uploads.md new file mode 100644 index 00000000000..27db226c293 --- /dev/null +++ b/docs/source/migration-file-uploads.md @@ -0,0 +1,29 @@ +--- +title: File uploads in Node.js < v8.5.0 +--- + +File uploads are supported in Apollo Server 2.x through the third-party [`graphql-upload`](https://npm.im/graphql-upload/) package. While Apollo Server 2.x aims to support Node.js LTS versions prior to v8.5.0, the `graphql-upload` project no longer supports file uploads on versions of Node.js prior to v8.5.0 due to changes in the underlying architecture. + +While Node.js versions prior to v8.5.0 are still under [_Long Term Support_ (LTS) agreements](https://github.com/nodejs/Release#release-schedule) from the Node.js Foundation, **we encourage _all users_ of Apollo Server to update to newer LTS versions of Node.js** prior to the "end-of-life" dates for their current server version. + +For example, while Node.js 6.x is covered by Long Term Support until April 2019, there are substantial performance (e.g. [V8](https://v8.dev/) improvements) and language changes (e.g. "modern" ECMAScript support) offered by newer Node.js engines (e.g. 8.x, 10.x). Switching to newer Long Term Support versions of Node.js comes with long-term benefits; Node.js 10.x LTS releases are covered by the Node.js Foundation through April 2021. + +Since file upload support for Node.js versions prior to v8.5.0 is no longer offered by `graphql-upload`, users of those versions must must disable file uploads to continue using newer Apollo Server 2.x versions. + +**To disable file uploads and continue using Apollo Server 2.x on Node.js 6.x**, add the `uploads: false` setting to the options when constructing the server. For example: + +```js +const { typeDefs, resolvers } = require('./anOutsideDependency'); +const server = new ApolloServer({ + /* Existing Apollo Server settings — e.g. type definitions */ + typeDefs, + resolvers, + + /* Add this line to disable upload support! */ + uploads: false, + + /* ... other Apollo Server settings ... */ +}) +``` + +For additional assistance, please [search for existing issues](https://github.com/apollographql/apollo-server/issues?q=uploads) or file a [new issue](https://github.com/apollographql/apollo-server/issues/new) on the Apollo Server GitHub repository. diff --git a/package-lock.json b/package-lock.json index ca18a74c91e..141799cb1c5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2203,7 +2203,6 @@ "cors": "^2.8.4", "graphql-subscriptions": "^1.0.0", "graphql-tools": "^4.0.0", - "graphql-upload": "^8.0.0", "type-is": "^1.6.16" }, "dependencies": { @@ -2221,8 +2220,7 @@ "apollo-server-core": "file:packages/apollo-server-core", "boom": "^7.1.0", "graphql-subscriptions": "^1.0.0", - "graphql-tools": "^4.0.0", - "graphql-upload": "^8.0.0" + "graphql-tools": "^4.0.0" } }, "apollo-server-integration-testsuite": { @@ -2246,7 +2244,6 @@ "apollo-server-core": "file:packages/apollo-server-core", "graphql-subscriptions": "^1.0.0", "graphql-tools": "^4.0.0", - "graphql-upload": "^8.0.0", "koa": "2.6.2", "koa-bodyparser": "^3.0.0", "koa-router": "^7.4.0", @@ -2280,7 +2277,6 @@ "@apollographql/graphql-playground-html": "^1.6.6", "accept": "^3.0.2", "apollo-server-core": "file:packages/apollo-server-core", - "graphql-upload": "^8.0.0", "micro": "^9.3.2" }, "dependencies": { diff --git a/packages/apollo-server-core/src/ApolloServer.ts b/packages/apollo-server-core/src/ApolloServer.ts index 41c97fe3a52..134982d899b 100644 --- a/packages/apollo-server-core/src/ApolloServer.ts +++ b/packages/apollo-server-core/src/ApolloServer.ts @@ -14,6 +14,7 @@ import { GraphQLExtension } from 'graphql-extensions'; import { EngineReportingAgent } from 'apollo-engine-reporting'; import { InMemoryLRUCache } from 'apollo-server-caching'; import { ApolloServerPlugin } from 'apollo-server-plugin-base'; +import supportsUploadsInNode from './utils/supportsUploadsInNode'; import { SubscriptionServer, @@ -201,6 +202,14 @@ export class ApolloServerBase { if (uploads !== false) { if (this.supportsUploads()) { + if (!supportsUploadsInNode) { + printNodeFileUploadsMessage(); + throw new Error( + '`graphql-upload` is no longer supported on Node.js < v8.5.0. ' + + 'See https://bit.ly/gql-upload-node-6.', + ); + } + if (uploads === true || typeof uploads === 'undefined') { this.uploadsConfig = {}; } else { @@ -540,3 +549,32 @@ export class ApolloServerBase { return processGraphQLRequest(options, requestCtx); } } + +function printNodeFileUploadsMessage() { + console.error( + [ + '*****************************************************************', + '* *', + '* ERROR! Manual intervention is necessary for Node.js < v8.5.0! *', + '* *', + '*****************************************************************', + '', + 'The third-party `graphql-upload` package, which is used to implement', + 'file uploads in Apollo Server 2.x, no longer supports Node.js LTS', + 'versions prior to Node.js v8.5.0.', + '', + 'If this server DOES NOT USE file uploads, it is necessary to add:', + '', + ' uploads: false,', + '', + 'to the options for Apollo Server and re-deploy to disable file uploads', + 'and continue using this version of Node.js.', + '', + 'Deployments which need file upload capabilities should update to', + 'Node.js >= v8.5.0 to continue using newer Apollo Server versions.', + '', + 'For more information, see https://bit.ly/gql-upload-node-6.', + '', + ].join('\n'), + ); +} diff --git a/packages/apollo-server-core/src/index.ts b/packages/apollo-server-core/src/index.ts index f48a9675795..a53b0e669e7 100644 --- a/packages/apollo-server-core/src/index.ts +++ b/packages/apollo-server-core/src/index.ts @@ -41,6 +41,18 @@ export const gql: ( ...substitutions: any[] ) => DocumentNode = gqlTag; +import supportsUploadsInNode from './utils/supportsUploadsInNode'; import { GraphQLScalarType } from 'graphql'; -import { GraphQLUpload as UploadScalar } from 'graphql-upload'; -export const GraphQLUpload = UploadScalar as GraphQLScalarType; +export { default as processFileUploads } from './processFileUploads'; + +// This is a conditional export intended to avoid traversing the +// entire module tree of `graphql-upload`. This only defined if the +// version of Node.js is >= 8.5.0 since those are the only Node.js versions +// which are supported by `graphql-upload@8`. Since the source of +// `graphql-upload` is not transpiled for older targets (in fact, it includes +// experimental ECMAScript modules), this conditional export is necessary +// to avoid modern ECMAScript from failing to parse by versions of Node.js +// which don't support it (yet — eg. Node.js 6 and async/await). +export const GraphQLUpload = supportsUploadsInNode + ? (require('graphql-upload').GraphQLUpload as GraphQLScalarType) + : undefined; diff --git a/packages/apollo-server-core/src/processFileUploads.ts b/packages/apollo-server-core/src/processFileUploads.ts new file mode 100644 index 00000000000..bf5a323ff6d --- /dev/null +++ b/packages/apollo-server-core/src/processFileUploads.ts @@ -0,0 +1,16 @@ +import supportsUploadsInNode from './utils/supportsUploadsInNode'; + +// We'll memoize this function once at module load time since it should never +// change during runtime. In the event that we're using a version of Node.js +// less than 8.5.0, we'll +const processFileUploads: + | typeof import('graphql-upload').processRequest + | undefined = (() => { + if (supportsUploadsInNode) { + return require('graphql-upload') + .processRequest as typeof import('graphql-upload').processRequest; + } + return undefined; +})(); + +export default processFileUploads; diff --git a/packages/apollo-server-core/src/utils/supportsUploadsInNode.ts b/packages/apollo-server-core/src/utils/supportsUploadsInNode.ts new file mode 100644 index 00000000000..da5910a9476 --- /dev/null +++ b/packages/apollo-server-core/src/utils/supportsUploadsInNode.ts @@ -0,0 +1,21 @@ +const supportsUploadsInNode = (() => { + if ( + process && + process.release && + process.release.name === 'node' && + process.versions && + typeof process.versions.node === 'string' + ) { + const [nodeMajor, nodeMinor] = process.versions.node + .split('.', 2) + .map(segment => parseInt(segment, 10)); + + if (nodeMajor < 8 || (nodeMajor === 8 && nodeMinor < 5)) { + return false; + } + } + + return true; +})(); + +export default supportsUploadsInNode; diff --git a/packages/apollo-server-express/package.json b/packages/apollo-server-express/package.json index af6907b039f..283fd0335e8 100644 --- a/packages/apollo-server-express/package.json +++ b/packages/apollo-server-express/package.json @@ -37,7 +37,6 @@ "cors": "^2.8.4", "graphql-subscriptions": "^1.0.0", "graphql-tools": "^4.0.0", - "graphql-upload": "^8.0.0", "type-is": "^1.6.16" }, "devDependencies": { diff --git a/packages/apollo-server-express/src/ApolloServer.ts b/packages/apollo-server-express/src/ApolloServer.ts index be7f9255cf5..dae1b59dbad 100644 --- a/packages/apollo-server-express/src/ApolloServer.ts +++ b/packages/apollo-server-express/src/ApolloServer.ts @@ -10,14 +10,13 @@ import { FileUploadOptions, ApolloServerBase, formatApolloErrors, + processFileUploads, } from 'apollo-server-core'; import accepts from 'accepts'; import typeis from 'type-is'; import { graphqlExpress } from './expressApollo'; -import { processRequest as processFileUploads } from 'graphql-upload'; - export { GraphQLOptions, GraphQLExtension } from 'apollo-server-core'; export interface ServerRegistration { @@ -44,7 +43,10 @@ const fileUploadMiddleware = ( next: express.NextFunction, ) => { // Note: we use typeis directly instead of via req.is for connect support. - if (typeis(req, ['multipart/form-data'])) { + if ( + typeof processFileUploads === 'function' && + typeis(req, ['multipart/form-data']) + ) { processFileUploads(req, res, uploadsConfig) .then(body => { req.body = body; @@ -134,7 +136,7 @@ export class ApolloServer extends ApolloServerBase { } let uploadsMiddleware; - if (this.uploadsConfig) { + if (this.uploadsConfig && typeof processFileUploads === 'function') { uploadsMiddleware = fileUploadMiddleware(this.uploadsConfig, this); } diff --git a/packages/apollo-server-hapi/package.json b/packages/apollo-server-hapi/package.json index f4fc3c1db53..5f419df33b9 100644 --- a/packages/apollo-server-hapi/package.json +++ b/packages/apollo-server-hapi/package.json @@ -30,8 +30,7 @@ "apollo-server-core": "file:../apollo-server-core", "boom": "^7.1.0", "graphql-subscriptions": "^1.0.0", - "graphql-tools": "^4.0.0", - "graphql-upload": "^8.0.0" + "graphql-tools": "^4.0.0" }, "devDependencies": { "apollo-server-integration-testsuite": "file:../apollo-server-integration-testsuite" diff --git a/packages/apollo-server-hapi/src/ApolloServer.ts b/packages/apollo-server-hapi/src/ApolloServer.ts index 40d160f8697..79c7d2dc2d8 100644 --- a/packages/apollo-server-hapi/src/ApolloServer.ts +++ b/packages/apollo-server-hapi/src/ApolloServer.ts @@ -4,7 +4,6 @@ import { renderPlaygroundPage, RenderPageOptions as PlaygroundRenderPageOptions, } from '@apollographql/graphql-playground-html'; -import { processRequest as processFileUploads } from 'graphql-upload'; import { graphqlHapi } from './hapiApollo'; @@ -13,11 +12,15 @@ import { ApolloServerBase, GraphQLOptions, FileUploadOptions, + processFileUploads, } from 'apollo-server-core'; function handleFileUploads(uploadsConfig: FileUploadOptions) { return async (request: hapi.Request, _h?: hapi.ResponseToolkit) => { - if (request.mime === 'multipart/form-data') { + if ( + typeof processFileUploads === 'function' && + request.mime === 'multipart/form-data' + ) { Object.defineProperty(request, 'payload', { value: await processFileUploads( request, @@ -68,7 +71,7 @@ export class ApolloServer extends ApolloServerBase { return h.continue; } - if (this.uploadsConfig) { + if (this.uploadsConfig && typeof processFileUploads === 'function') { await handleFileUploads(this.uploadsConfig)(request); } diff --git a/packages/apollo-server-koa/package.json b/packages/apollo-server-koa/package.json index 70e9a6c6ec8..01321af06d1 100644 --- a/packages/apollo-server-koa/package.json +++ b/packages/apollo-server-koa/package.json @@ -37,7 +37,6 @@ "apollo-server-core": "file:../apollo-server-core", "graphql-subscriptions": "^1.0.0", "graphql-tools": "^4.0.0", - "graphql-upload": "^8.0.0", "koa": "2.6.2", "koa-bodyparser": "^3.0.0", "koa-router": "^7.4.0", diff --git a/packages/apollo-server-koa/src/ApolloServer.ts b/packages/apollo-server-koa/src/ApolloServer.ts index ec7871a81a6..2ad247ea052 100644 --- a/packages/apollo-server-koa/src/ApolloServer.ts +++ b/packages/apollo-server-koa/src/ApolloServer.ts @@ -6,14 +6,16 @@ import { renderPlaygroundPage, RenderPageOptions as PlaygroundRenderPageOptions, } from '@apollographql/graphql-playground-html'; -import { ApolloServerBase, formatApolloErrors } from 'apollo-server-core'; +import { + ApolloServerBase, + formatApolloErrors, + processFileUploads, +} from 'apollo-server-core'; import accepts from 'accepts'; import typeis from 'type-is'; import { graphqlKoa } from './koaApollo'; -import { processRequest as processFileUploads } from 'graphql-upload'; - export { GraphQLOptions, GraphQLExtension } from 'apollo-server-core'; import { GraphQLOptions, FileUploadOptions } from 'apollo-server-core'; @@ -138,7 +140,7 @@ export class ApolloServer extends ApolloServerBase { } let uploadsMiddleware; - if (this.uploadsConfig) { + if (this.uploadsConfig && typeof processFileUploads === 'function') { uploadsMiddleware = fileUploadMiddleware(this.uploadsConfig, this); } diff --git a/packages/apollo-server-micro/package.json b/packages/apollo-server-micro/package.json index 800ec0fa43b..fd462d5acf8 100644 --- a/packages/apollo-server-micro/package.json +++ b/packages/apollo-server-micro/package.json @@ -26,7 +26,6 @@ "@apollographql/graphql-playground-html": "^1.6.6", "accept": "^3.0.2", "apollo-server-core": "file:../apollo-server-core", - "graphql-upload": "^8.0.0", "micro": "^9.3.2" }, "devDependencies": { diff --git a/packages/apollo-server-micro/src/ApolloServer.ts b/packages/apollo-server-micro/src/ApolloServer.ts index 6bdbee16e96..25e15f9deb0 100644 --- a/packages/apollo-server-micro/src/ApolloServer.ts +++ b/packages/apollo-server-micro/src/ApolloServer.ts @@ -1,5 +1,8 @@ -import { ApolloServerBase, GraphQLOptions } from 'apollo-server-core'; -import { processRequest as processFileUploads } from 'graphql-upload'; +import { + ApolloServerBase, + GraphQLOptions, + processFileUploads, +} from 'apollo-server-core'; import { ServerResponse } from 'http'; import { send } from 'micro'; import { renderPlaygroundPage } from '@apollographql/graphql-playground-html'; @@ -39,7 +42,9 @@ export class ApolloServer extends ApolloServerBase { await promiseWillStart; - await this.handleFileUploads(req, res); + if (typeof processFileUploads === 'function') { + await this.handleFileUploads(req, res); + } (await this.handleHealthCheck({ req, @@ -162,6 +167,10 @@ export class ApolloServer extends ApolloServerBase { // If file uploads are detected, prepare them for easier handling with // the help of `graphql-upload`. private async handleFileUploads(req: MicroRequest, res: ServerResponse) { + if (typeof processFileUploads !== 'function') { + return; + } + const contentType = req.headers['content-type']; if ( this.uploadsConfig && From 7e6d6cf13b4e5dc1df3bd2ab236b8bc979c1bdf7 Mon Sep 17 00:00:00 2001 From: Jesse Rosenberger Date: Fri, 30 Nov 2018 17:22:29 +0200 Subject: [PATCH 03/10] Update to `graphql-upload@8.0.2`. --- package-lock.json | 50 ++++++++++++------------ packages/apollo-server-core/package.json | 2 +- 2 files changed, 25 insertions(+), 27 deletions(-) diff --git a/package-lock.json b/package-lock.json index 141799cb1c5..28c6c7cb21b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2172,11 +2172,34 @@ "graphql-subscriptions": "^1.0.0", "graphql-tag": "^2.9.2", "graphql-tools": "^4.0.0", - "graphql-upload": "^8.0.0", + "graphql-upload": "^8.0.2", "json-stable-stringify": "^1.0.1", "lodash": "^4.17.10", "subscriptions-transport-ws": "^0.9.11", "ws": "^6.0.0" + }, + "dependencies": { + "graphql-upload": { + "version": "8.0.2", + "bundled": true, + "requires": { + "busboy": "^0.2.14", + "fs-capacitor": "^1.0.0", + "http-errors": "^1.7.1", + "object-path": "^0.11.4" + } + }, + "http-errors": { + "version": "1.7.1", + "bundled": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + } } }, "apollo-server-env": { @@ -6296,31 +6319,6 @@ "uuid": "^3.1.0" } }, - "graphql-upload": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/graphql-upload/-/graphql-upload-8.0.0.tgz", - "integrity": "sha512-DlEi6+Kblj6gAAA5XJahJl1+UfcXICiFypJYIxd4zK26W2/LKm6nwLmSWjECwbAkz/OtB+oSWGb5gqXCDQOLqg==", - "requires": { - "busboy": "^0.2.14", - "fs-capacitor": "^1.0.0", - "http-errors": "^1.7.1", - "object-path": "^0.11.4" - }, - "dependencies": { - "http-errors": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.1.tgz", - "integrity": "sha512-jWEUgtZWGSMba9I1N3gc1HmvpBUaNC9vDdA46yScAdp+C5rdEuKWUBLWTQpW9FwSWSbYYs++b6SDCxf9UEJzfw==", - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - } - } - } - }, "growly": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", diff --git a/packages/apollo-server-core/package.json b/packages/apollo-server-core/package.json index fdb032e930a..3aeacd326e1 100644 --- a/packages/apollo-server-core/package.json +++ b/packages/apollo-server-core/package.json @@ -39,7 +39,7 @@ "graphql-subscriptions": "^1.0.0", "graphql-tag": "^2.9.2", "graphql-tools": "^4.0.0", - "graphql-upload": "^8.0.0", + "graphql-upload": "^8.0.2", "json-stable-stringify": "^1.0.1", "lodash": "^4.17.10", "subscriptions-transport-ws": "^0.9.11", From 2fd56f0c3e9ce677b06a09fe98b63dca09f65508 Mon Sep 17 00:00:00 2001 From: Jesse Rosenberger Date: Fri, 30 Nov 2018 18:50:23 +0200 Subject: [PATCH 04/10] Don't enable uploads when using an unsupported Node.js in tests. Now that there are specific versions of Node.js which don't support file uploads (namely, <= 8.5.0) we need to explicitly disable uploads on those versions, similar to how those users must opt-in to that behavior by setting `uploads: false` in their Apollo Server constructor options. This effectively accomplishes exactly that, but only when necessary. --- packages/apollo-server-core/src/ApolloServer.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/apollo-server-core/src/ApolloServer.ts b/packages/apollo-server-core/src/ApolloServer.ts index 134982d899b..fede7b2b666 100644 --- a/packages/apollo-server-core/src/ApolloServer.ts +++ b/packages/apollo-server-core/src/ApolloServer.ts @@ -89,6 +89,9 @@ function getEngineServiceId(engine: Config['engine']): string | undefined { return; } +const forbidUploadsForTesting = + process && process.env.NODE_ENV === 'test' && !supportsUploadsInNode; + export class ApolloServerBase { public subscriptionsPath?: string; public graphqlPath: string = '/graphql'; @@ -200,7 +203,7 @@ export class ApolloServerBase { this.requestOptions = requestOptions as GraphQLOptions; this.context = context; - if (uploads !== false) { + if (uploads !== false && !forbidUploadsForTesting) { if (this.supportsUploads()) { if (!supportsUploadsInNode) { printNodeFileUploadsMessage(); From 6dc132e0c6b9f01338d2f4167f158d1f7a33a91e Mon Sep 17 00:00:00 2001 From: Jesse Rosenberger Date: Fri, 30 Nov 2018 19:00:55 +0200 Subject: [PATCH 05/10] (tests) Switch to a very explicit Node.js 10 restriction. I hope to actually remove this limitation, but I'm going to iterate on it first. This also just switches to a `NODE_MAJOR_VERSION` constant from the `apollo-server-integration-testsuite` rather than using the GTE function on the same input since...math. --- .../src/__tests__/ApolloServer.test.ts | 6 +- .../src/__tests__/ApolloServer.test.ts | 4 +- .../src/__tests__/hapiApollo.test.ts | 61 +++++++++---------- .../src/index.ts | 5 +- .../src/__tests__/ApolloServer.test.ts | 6 +- .../src/__tests__/ApolloServer.test.ts | 5 +- 6 files changed, 41 insertions(+), 46 deletions(-) diff --git a/packages/apollo-server-express/src/__tests__/ApolloServer.test.ts b/packages/apollo-server-express/src/__tests__/ApolloServer.test.ts index ce385c50bd4..b83dbd99b68 100644 --- a/packages/apollo-server-express/src/__tests__/ApolloServer.test.ts +++ b/packages/apollo-server-express/src/__tests__/ApolloServer.test.ts @@ -11,7 +11,7 @@ import { gql, AuthenticationError, Config } from 'apollo-server-core'; import { ApolloServer, ServerRegistration } from '../ApolloServer'; import { - atLeastMajorNodeVersion, + NODE_MAJOR_VERSION, testApolloServer, createServerInfo, } from 'apollo-server-integration-testsuite'; @@ -427,8 +427,8 @@ describe('apollo-server-express', () => { }); }); }); - // NODE: Intentionally skip file upload tests on Node.js 10 or higher. - (atLeastMajorNodeVersion(10) ? describe.skip : describe)( + // NODE: Intentionally skip file upload tests on Node.js 10. + (NODE_MAJOR_VERSION === 10 ? describe.skip : describe)( 'file uploads', () => { it('enabled uploads', async () => { diff --git a/packages/apollo-server-hapi/src/__tests__/ApolloServer.test.ts b/packages/apollo-server-hapi/src/__tests__/ApolloServer.test.ts index 60cf32e1206..43d00dd7761 100644 --- a/packages/apollo-server-hapi/src/__tests__/ApolloServer.test.ts +++ b/packages/apollo-server-hapi/src/__tests__/ApolloServer.test.ts @@ -1,5 +1,5 @@ import { - atLeastMajorNodeVersion, + NODE_MAJOR_VERSION, testApolloServer, createServerInfo, } from 'apollo-server-integration-testsuite'; @@ -16,7 +16,7 @@ import { ApolloServer } from '../ApolloServer'; const port = 5555; // NODE: Intentionally skip for Node.js < 8 since Hapi 17 doesn't support those. -(atLeastMajorNodeVersion(8) ? describe : describe.skip)( +(NODE_MAJOR_VERSION < 8 ? describe.skip : describe)( 'apollo-server-hapi', () => { let server: ApolloServer; diff --git a/packages/apollo-server-hapi/src/__tests__/hapiApollo.test.ts b/packages/apollo-server-hapi/src/__tests__/hapiApollo.test.ts index d76eec1f832..2145b44c751 100644 --- a/packages/apollo-server-hapi/src/__tests__/hapiApollo.test.ts +++ b/packages/apollo-server-hapi/src/__tests__/hapiApollo.test.ts @@ -4,40 +4,37 @@ import { Config } from 'apollo-server-core'; import testSuite, { schema as Schema, CreateAppOptions, - atLeastMajorNodeVersion, + NODE_MAJOR_VERSION, } from 'apollo-server-integration-testsuite'; // NODE: Intentionally skip on Node.js < 8 since Hapi 17 doesn't support less -(atLeastMajorNodeVersion(8) ? describe : describe.skip)( - 'integration:Hapi', - () => { - async function createApp(options: CreateAppOptions = {}) { - const { Server } = require('hapi'); - - const app: import('hapi').Server = new Server({ - host: 'localhost', - port: 8000, - }); - - const server = new ApolloServer( - (options.graphqlOptions as Config) || { schema: Schema }, - ); - await server.applyMiddleware({ - app, - }); - - await app.start(); - - return app.listener; +(NODE_MAJOR_VERSION < 8 ? describe.skip : describe)('integration:Hapi', () => { + async function createApp(options: CreateAppOptions = {}) { + const { Server } = require('hapi'); + + const app: import('hapi').Server = new Server({ + host: 'localhost', + port: 8000, + }); + + const server = new ApolloServer( + (options.graphqlOptions as Config) || { schema: Schema }, + ); + await server.applyMiddleware({ + app, + }); + + await app.start(); + + return app.listener; + } + + async function destroyApp(app) { + if (!app || !app.close) { + return; } + await new Promise(resolve => app.close(resolve)); + } - async function destroyApp(app) { - if (!app || !app.close) { - return; - } - await new Promise(resolve => app.close(resolve)); - } - - testSuite(createApp, destroyApp); - }, -); + testSuite(createApp, destroyApp); +}); diff --git a/packages/apollo-server-integration-testsuite/src/index.ts b/packages/apollo-server-integration-testsuite/src/index.ts index c5766e18452..0cd9fac44a1 100644 --- a/packages/apollo-server-integration-testsuite/src/index.ts +++ b/packages/apollo-server-integration-testsuite/src/index.ts @@ -23,13 +23,10 @@ import gql from 'graphql-tag'; export * from './ApolloServer'; -const NODE_MAJOR_VERSION: number = parseInt( +export const NODE_MAJOR_VERSION: number = parseInt( process.versions.node.split('.', 1)[0], 10, ); -export function atLeastMajorNodeVersion(desiredVersion: number): boolean { - return NODE_MAJOR_VERSION >= desiredVersion; -} const QueryRootType = new GraphQLObjectType({ name: 'QueryRoot', diff --git a/packages/apollo-server-koa/src/__tests__/ApolloServer.test.ts b/packages/apollo-server-koa/src/__tests__/ApolloServer.test.ts index 25ed0197002..13201098e39 100644 --- a/packages/apollo-server-koa/src/__tests__/ApolloServer.test.ts +++ b/packages/apollo-server-koa/src/__tests__/ApolloServer.test.ts @@ -11,7 +11,7 @@ import { gql, AuthenticationError, Config } from 'apollo-server-core'; import { ApolloServer, ServerRegistration } from '../ApolloServer'; import { - atLeastMajorNodeVersion, + NODE_MAJOR_VERSION, testApolloServer, createServerInfo, } from 'apollo-server-integration-testsuite'; @@ -323,8 +323,8 @@ describe('apollo-server-koa', () => { }); }); }); - // NODE: Intentionally skip file upload tests on Node.js 10 or higher. - (atLeastMajorNodeVersion(10) ? describe.skip : describe)( + // NODE: Intentionally skip file upload tests on Node.js 10. + (NODE_MAJOR_VERSION === 10 ? describe.skip : describe)( 'file uploads', () => { it('enabled uploads', async () => { diff --git a/packages/apollo-server-micro/src/__tests__/ApolloServer.test.ts b/packages/apollo-server-micro/src/__tests__/ApolloServer.test.ts index fcbad2cb06a..69f2d7ff19e 100644 --- a/packages/apollo-server-micro/src/__tests__/ApolloServer.test.ts +++ b/packages/apollo-server-micro/src/__tests__/ApolloServer.test.ts @@ -1,7 +1,7 @@ import micro from 'micro'; import listen from 'test-listen'; import { createApolloFetch } from 'apollo-fetch'; -import { atLeastMajorNodeVersion } from 'apollo-server-integration-testsuite'; +import { NODE_MAJOR_VERSION } from 'apollo-server-integration-testsuite'; import { gql } from 'apollo-server-core'; import FormData from 'form-data'; import fs from 'fs'; @@ -146,7 +146,8 @@ describe('apollo-server-micro', function() { }); }); - (atLeastMajorNodeVersion(10) ? describe.skip : describe)( + // NODE: Intentionally skip file upload tests on Node.js 10. + (NODE_MAJOR_VERSION === 10 ? describe.skip : describe)( 'file uploads', function() { it('should handle file uploads', async function() { From cd6e575bb788036a9dfbdbc0aae14cc00286afa1 Mon Sep 17 00:00:00 2001 From: Jesse Rosenberger Date: Fri, 30 Nov 2018 19:03:44 +0200 Subject: [PATCH 06/10] (tests) Skip file upload tests for `NODE_MAJOR_VERSION === 6`. This commit looks way more complicated than it really is thanks to Prettier trying to be helpful. All I've done is add `NODE_MAJOR_VERSION === 6` as a version NOT to test uploads for, in an effort to fix the failing tests (failing appropriately since Node.js 6 with file uploads throws an error right now and cannot run uploads anymore.). --- .../src/__tests__/ApolloServer.test.ts | 125 ++++++++-------- .../src/__tests__/ApolloServer.test.ts | 125 ++++++++-------- .../src/__tests__/ApolloServer.test.ts | 137 +++++++++--------- 3 files changed, 195 insertions(+), 192 deletions(-) diff --git a/packages/apollo-server-express/src/__tests__/ApolloServer.test.ts b/packages/apollo-server-express/src/__tests__/ApolloServer.test.ts index b83dbd99b68..aa3a39baa11 100644 --- a/packages/apollo-server-express/src/__tests__/ApolloServer.test.ts +++ b/packages/apollo-server-express/src/__tests__/ApolloServer.test.ts @@ -428,45 +428,47 @@ describe('apollo-server-express', () => { }); }); // NODE: Intentionally skip file upload tests on Node.js 10. - (NODE_MAJOR_VERSION === 10 ? describe.skip : describe)( - 'file uploads', - () => { - it('enabled uploads', async () => { - const { port } = await createServer({ - typeDefs: gql` - type File { - filename: String! - mimetype: String! - encoding: String! - } + // Also skip Node.js 6, but only because `graphql-upload` + // doesn't support it. + (NODE_MAJOR_VERSION === 10 || NODE_MAJOR_VERSION === 6 + ? describe.skip + : describe)('file uploads', () => { + it('enabled uploads', async () => { + const { port } = await createServer({ + typeDefs: gql` + type File { + filename: String! + mimetype: String! + encoding: String! + } - type Query { - uploads: [File] - } + type Query { + uploads: [File] + } - type Mutation { - singleUpload(file: Upload!): File! - } - `, - resolvers: { - Query: { - uploads: () => {}, - }, - Mutation: { - singleUpload: async (_, args) => { - expect((await args.file).stream).toBeDefined(); - return args.file; - }, + type Mutation { + singleUpload(file: Upload!): File! + } + `, + resolvers: { + Query: { + uploads: () => {}, + }, + Mutation: { + singleUpload: async (_, args) => { + expect((await args.file).stream).toBeDefined(); + return args.file; }, }, - }); + }, + }); - const body = new FormData(); + const body = new FormData(); - body.append( - 'operations', - JSON.stringify({ - query: ` + body.append( + 'operations', + JSON.stringify({ + query: ` mutation($file: Upload!) { singleUpload(file: $file) { filename @@ -475,36 +477,35 @@ describe('apollo-server-express', () => { } } `, - variables: { - file: null, - }, - }), - ); + variables: { + file: null, + }, + }), + ); - body.append('map', JSON.stringify({ 1: ['variables.file'] })); - body.append('1', fs.createReadStream('package.json')); - - try { - const resolved = await fetch(`http://localhost:${port}/graphql`, { - method: 'POST', - body: body as any, - }); - const text = await resolved.text(); - const response = JSON.parse(text); - - expect(response.data.singleUpload).toEqual({ - filename: 'package.json', - encoding: '7bit', - mimetype: 'application/json', - }); - } catch (error) { - // This error began appearing randomly and seems to be a dev dependency bug. - // https://github.com/jaydenseric/apollo-upload-server/blob/18ecdbc7a1f8b69ad51b4affbd986400033303d4/test.js#L39-L42 - if (error.code !== 'EPIPE') throw error; - } - }); - }, - ); + body.append('map', JSON.stringify({ 1: ['variables.file'] })); + body.append('1', fs.createReadStream('package.json')); + + try { + const resolved = await fetch(`http://localhost:${port}/graphql`, { + method: 'POST', + body: body as any, + }); + const text = await resolved.text(); + const response = JSON.parse(text); + + expect(response.data.singleUpload).toEqual({ + filename: 'package.json', + encoding: '7bit', + mimetype: 'application/json', + }); + } catch (error) { + // This error began appearing randomly and seems to be a dev dependency bug. + // https://github.com/jaydenseric/apollo-upload-server/blob/18ecdbc7a1f8b69ad51b4affbd986400033303d4/test.js#L39-L42 + if (error.code !== 'EPIPE') throw error; + } + }); + }); describe('errors', () => { it('returns thrown context error as a valid graphql result', async () => { diff --git a/packages/apollo-server-koa/src/__tests__/ApolloServer.test.ts b/packages/apollo-server-koa/src/__tests__/ApolloServer.test.ts index 13201098e39..127a6a9f93e 100644 --- a/packages/apollo-server-koa/src/__tests__/ApolloServer.test.ts +++ b/packages/apollo-server-koa/src/__tests__/ApolloServer.test.ts @@ -324,45 +324,47 @@ describe('apollo-server-koa', () => { }); }); // NODE: Intentionally skip file upload tests on Node.js 10. - (NODE_MAJOR_VERSION === 10 ? describe.skip : describe)( - 'file uploads', - () => { - it('enabled uploads', async () => { - const { port } = await createServer({ - typeDefs: gql` - type File { - filename: String! - mimetype: String! - encoding: String! - } + // Also skip Node.js 6, but only because `graphql-upload` + // doesn't support it anymore. + (NODE_MAJOR_VERSION === 10 || NODE_MAJOR_VERSION === 6 + ? describe.skip + : describe)('file uploads', () => { + it('enabled uploads', async () => { + const { port } = await createServer({ + typeDefs: gql` + type File { + filename: String! + mimetype: String! + encoding: String! + } - type Query { - uploads: [File] - } + type Query { + uploads: [File] + } - type Mutation { - singleUpload(file: Upload!): File! - } - `, - resolvers: { - Query: { - uploads: () => {}, - }, - Mutation: { - singleUpload: async (_, args) => { - expect((await args.file).stream).toBeDefined(); - return args.file; - }, + type Mutation { + singleUpload(file: Upload!): File! + } + `, + resolvers: { + Query: { + uploads: () => {}, + }, + Mutation: { + singleUpload: async (_, args) => { + expect((await args.file).stream).toBeDefined(); + return args.file; }, }, - }); + }, + }); - const body = new FormData(); + const body = new FormData(); - body.append( - 'operations', - JSON.stringify({ - query: ` + body.append( + 'operations', + JSON.stringify({ + query: ` mutation($file: Upload!) { singleUpload(file: $file) { filename @@ -371,36 +373,35 @@ describe('apollo-server-koa', () => { } } `, - variables: { - file: null, - }, - }), - ); + variables: { + file: null, + }, + }), + ); - body.append('map', JSON.stringify({ 1: ['variables.file'] })); - body.append('1', fs.createReadStream('package.json')); - - try { - const resolved = await fetch(`http://localhost:${port}/graphql`, { - method: 'POST', - body: body as any, - }); - const text = await resolved.text(); - const response = JSON.parse(text); - - expect(response.data.singleUpload).toEqual({ - filename: 'package.json', - encoding: '7bit', - mimetype: 'application/json', - }); - } catch (error) { - // This error began appearing randomly and seems to be a dev dependency bug. - // https://github.com/jaydenseric/apollo-upload-server/blob/18ecdbc7a1f8b69ad51b4affbd986400033303d4/test.js#L39-L42 - if (error.code !== 'EPIPE') throw error; - } - }); - }, - ); + body.append('map', JSON.stringify({ 1: ['variables.file'] })); + body.append('1', fs.createReadStream('package.json')); + + try { + const resolved = await fetch(`http://localhost:${port}/graphql`, { + method: 'POST', + body: body as any, + }); + const text = await resolved.text(); + const response = JSON.parse(text); + + expect(response.data.singleUpload).toEqual({ + filename: 'package.json', + encoding: '7bit', + mimetype: 'application/json', + }); + } catch (error) { + // This error began appearing randomly and seems to be a dev dependency bug. + // https://github.com/jaydenseric/apollo-upload-server/blob/18ecdbc7a1f8b69ad51b4affbd986400033303d4/test.js#L39-L42 + if (error.code !== 'EPIPE') throw error; + } + }); + }); describe('errors', () => { it('returns thrown context error as a valid graphql result', async () => { diff --git a/packages/apollo-server-micro/src/__tests__/ApolloServer.test.ts b/packages/apollo-server-micro/src/__tests__/ApolloServer.test.ts index 69f2d7ff19e..941b7b25a36 100644 --- a/packages/apollo-server-micro/src/__tests__/ApolloServer.test.ts +++ b/packages/apollo-server-micro/src/__tests__/ApolloServer.test.ts @@ -147,46 +147,48 @@ describe('apollo-server-micro', function() { }); // NODE: Intentionally skip file upload tests on Node.js 10. - (NODE_MAJOR_VERSION === 10 ? describe.skip : describe)( - 'file uploads', - function() { - it('should handle file uploads', async function() { - const apolloServer = new ApolloServer({ - typeDefs: gql` - type File { - filename: String! - mimetype: String! - encoding: String! - } - - type Query { - uploads: [File] - } - - type Mutation { - singleUpload(file: Upload!): File! - } - `, - resolvers: { - Query: { - uploads: () => {}, - }, - Mutation: { - singleUpload: async (_, args) => { - expect((await args.file).stream).toBeDefined(); - return args.file; - }, + // Also skip Node.js 6, but only because `graphql-upload` + // doesn't support it. + (NODE_MAJOR_VERSION === 10 || NODE_MAJOR_VERSION === 6 + ? describe.skip + : describe)('file uploads', function() { + it('should handle file uploads', async function() { + const apolloServer = new ApolloServer({ + typeDefs: gql` + type File { + filename: String! + mimetype: String! + encoding: String! + } + + type Query { + uploads: [File] + } + + type Mutation { + singleUpload(file: Upload!): File! + } + `, + resolvers: { + Query: { + uploads: () => {}, + }, + Mutation: { + singleUpload: async (_, args) => { + expect((await args.file).stream).toBeDefined(); + return args.file; }, }, - }); - const service = micro(apolloServer.createHandler()); - const url = await listen(service); - - const body = new FormData(); - body.append( - 'operations', - JSON.stringify({ - query: ` + }, + }); + const service = micro(apolloServer.createHandler()); + const url = await listen(service); + + const body = new FormData(); + body.append( + 'operations', + JSON.stringify({ + query: ` mutation($file: Upload!) { singleUpload(file: $file) { filename @@ -195,37 +197,36 @@ describe('apollo-server-micro', function() { } } `, - variables: { - file: null, - }, - }), - ); - body.append('map', JSON.stringify({ 1: ['variables.file'] })); - body.append('1', fs.createReadStream('package.json')); + variables: { + file: null, + }, + }), + ); + body.append('map', JSON.stringify({ 1: ['variables.file'] })); + body.append('1', fs.createReadStream('package.json')); - try { - const resolved = await fetch(`${url}/graphql`, { - method: 'POST', - body: body as any, - }); - const text = await resolved.text(); - const response = JSON.parse(text); - - expect(response.data.singleUpload).toEqual({ - filename: 'package.json', - encoding: '7bit', - mimetype: 'application/json', - }); - } catch (error) { - // This error began appearing randomly and seems to be a dev - // dependency bug. - // https://github.com/jaydenseric/apollo-upload-server/blob/18ecdbc7a1f8b69ad51b4affbd986400033303d4/test.js#L39-L42 - if (error.code !== 'EPIPE') throw error; - } + try { + const resolved = await fetch(`${url}/graphql`, { + method: 'POST', + body: body as any, + }); + const text = await resolved.text(); + const response = JSON.parse(text); - service.close(); - }); - }, - ); + expect(response.data.singleUpload).toEqual({ + filename: 'package.json', + encoding: '7bit', + mimetype: 'application/json', + }); + } catch (error) { + // This error began appearing randomly and seems to be a dev + // dependency bug. + // https://github.com/jaydenseric/apollo-upload-server/blob/18ecdbc7a1f8b69ad51b4affbd986400033303d4/test.js#L39-L42 + if (error.code !== 'EPIPE') throw error; + } + + service.close(); + }); + }); }); }); From 705256e8790a4c7df84704bcc9e28f33b9b639d9 Mon Sep 17 00:00:00 2001 From: Jesse Rosenberger Date: Fri, 30 Nov 2018 19:10:07 +0200 Subject: [PATCH 07/10] =?UTF-8?q?(tests):=20Re-enable=20file=20upload=20te?= =?UTF-8?q?sts=20for=20Node.js=2010.=20=F0=9F=8E=89=F0=9F=8E=89?= =?UTF-8?q?=F0=9F=8E=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit again looks quite complicated, but's merely an over-complication inflicted by Prettification. Disabling whitespace differences when viewing this commit, the functional change here is that we no longer skip many file upload tests when the (semver) major segment of `process.version` is `10`. I couldn't be happier to get rid of this exception for file upload tests on Node.js 10, which was an unfortunate reality of the non-updated `graphql-upload` module world we previously lived in. Thanks, @jaydenseric, for the newfound (to us!) Node.js upload support! --- .../src/__tests__/ApolloServer.test.ts | 126 ++++++++-------- .../src/__tests__/ApolloServer.test.ts | 126 ++++++++-------- .../src/__tests__/ApolloServer.test.ts | 135 +++++++++--------- 3 files changed, 194 insertions(+), 193 deletions(-) diff --git a/packages/apollo-server-express/src/__tests__/ApolloServer.test.ts b/packages/apollo-server-express/src/__tests__/ApolloServer.test.ts index aa3a39baa11..e9db26e4461 100644 --- a/packages/apollo-server-express/src/__tests__/ApolloServer.test.ts +++ b/packages/apollo-server-express/src/__tests__/ApolloServer.test.ts @@ -427,48 +427,47 @@ describe('apollo-server-express', () => { }); }); }); - // NODE: Intentionally skip file upload tests on Node.js 10. - // Also skip Node.js 6, but only because `graphql-upload` + // NODE: Skip Node.js 6, but only because `graphql-upload` // doesn't support it. - (NODE_MAJOR_VERSION === 10 || NODE_MAJOR_VERSION === 6 - ? describe.skip - : describe)('file uploads', () => { - it('enabled uploads', async () => { - const { port } = await createServer({ - typeDefs: gql` - type File { - filename: String! - mimetype: String! - encoding: String! - } + (NODE_MAJOR_VERSION === 6 ? describe.skip : describe)( + 'file uploads', + () => { + it('enabled uploads', async () => { + const { port } = await createServer({ + typeDefs: gql` + type File { + filename: String! + mimetype: String! + encoding: String! + } - type Query { - uploads: [File] - } + type Query { + uploads: [File] + } - type Mutation { - singleUpload(file: Upload!): File! - } - `, - resolvers: { - Query: { - uploads: () => {}, - }, - Mutation: { - singleUpload: async (_, args) => { - expect((await args.file).stream).toBeDefined(); - return args.file; + type Mutation { + singleUpload(file: Upload!): File! + } + `, + resolvers: { + Query: { + uploads: () => {}, + }, + Mutation: { + singleUpload: async (_, args) => { + expect((await args.file).stream).toBeDefined(); + return args.file; + }, }, }, - }, - }); + }); - const body = new FormData(); + const body = new FormData(); - body.append( - 'operations', - JSON.stringify({ - query: ` + body.append( + 'operations', + JSON.stringify({ + query: ` mutation($file: Upload!) { singleUpload(file: $file) { filename @@ -477,35 +476,36 @@ describe('apollo-server-express', () => { } } `, - variables: { - file: null, - }, - }), - ); - - body.append('map', JSON.stringify({ 1: ['variables.file'] })); - body.append('1', fs.createReadStream('package.json')); - - try { - const resolved = await fetch(`http://localhost:${port}/graphql`, { - method: 'POST', - body: body as any, - }); - const text = await resolved.text(); - const response = JSON.parse(text); + variables: { + file: null, + }, + }), + ); - expect(response.data.singleUpload).toEqual({ - filename: 'package.json', - encoding: '7bit', - mimetype: 'application/json', - }); - } catch (error) { - // This error began appearing randomly and seems to be a dev dependency bug. - // https://github.com/jaydenseric/apollo-upload-server/blob/18ecdbc7a1f8b69ad51b4affbd986400033303d4/test.js#L39-L42 - if (error.code !== 'EPIPE') throw error; - } - }); - }); + body.append('map', JSON.stringify({ 1: ['variables.file'] })); + body.append('1', fs.createReadStream('package.json')); + + try { + const resolved = await fetch(`http://localhost:${port}/graphql`, { + method: 'POST', + body: body as any, + }); + const text = await resolved.text(); + const response = JSON.parse(text); + + expect(response.data.singleUpload).toEqual({ + filename: 'package.json', + encoding: '7bit', + mimetype: 'application/json', + }); + } catch (error) { + // This error began appearing randomly and seems to be a dev dependency bug. + // https://github.com/jaydenseric/apollo-upload-server/blob/18ecdbc7a1f8b69ad51b4affbd986400033303d4/test.js#L39-L42 + if (error.code !== 'EPIPE') throw error; + } + }); + }, + ); describe('errors', () => { it('returns thrown context error as a valid graphql result', async () => { diff --git a/packages/apollo-server-koa/src/__tests__/ApolloServer.test.ts b/packages/apollo-server-koa/src/__tests__/ApolloServer.test.ts index 127a6a9f93e..9318be58c96 100644 --- a/packages/apollo-server-koa/src/__tests__/ApolloServer.test.ts +++ b/packages/apollo-server-koa/src/__tests__/ApolloServer.test.ts @@ -323,48 +323,47 @@ describe('apollo-server-koa', () => { }); }); }); - // NODE: Intentionally skip file upload tests on Node.js 10. - // Also skip Node.js 6, but only because `graphql-upload` + // NODE: Skip Node.js 6, but only because `graphql-upload` // doesn't support it anymore. - (NODE_MAJOR_VERSION === 10 || NODE_MAJOR_VERSION === 6 - ? describe.skip - : describe)('file uploads', () => { - it('enabled uploads', async () => { - const { port } = await createServer({ - typeDefs: gql` - type File { - filename: String! - mimetype: String! - encoding: String! - } + (NODE_MAJOR_VERSION === 6 ? describe.skip : describe)( + 'file uploads', + () => { + it('enabled uploads', async () => { + const { port } = await createServer({ + typeDefs: gql` + type File { + filename: String! + mimetype: String! + encoding: String! + } - type Query { - uploads: [File] - } + type Query { + uploads: [File] + } - type Mutation { - singleUpload(file: Upload!): File! - } - `, - resolvers: { - Query: { - uploads: () => {}, - }, - Mutation: { - singleUpload: async (_, args) => { - expect((await args.file).stream).toBeDefined(); - return args.file; + type Mutation { + singleUpload(file: Upload!): File! + } + `, + resolvers: { + Query: { + uploads: () => {}, + }, + Mutation: { + singleUpload: async (_, args) => { + expect((await args.file).stream).toBeDefined(); + return args.file; + }, }, }, - }, - }); + }); - const body = new FormData(); + const body = new FormData(); - body.append( - 'operations', - JSON.stringify({ - query: ` + body.append( + 'operations', + JSON.stringify({ + query: ` mutation($file: Upload!) { singleUpload(file: $file) { filename @@ -373,35 +372,36 @@ describe('apollo-server-koa', () => { } } `, - variables: { - file: null, - }, - }), - ); - - body.append('map', JSON.stringify({ 1: ['variables.file'] })); - body.append('1', fs.createReadStream('package.json')); - - try { - const resolved = await fetch(`http://localhost:${port}/graphql`, { - method: 'POST', - body: body as any, - }); - const text = await resolved.text(); - const response = JSON.parse(text); + variables: { + file: null, + }, + }), + ); - expect(response.data.singleUpload).toEqual({ - filename: 'package.json', - encoding: '7bit', - mimetype: 'application/json', - }); - } catch (error) { - // This error began appearing randomly and seems to be a dev dependency bug. - // https://github.com/jaydenseric/apollo-upload-server/blob/18ecdbc7a1f8b69ad51b4affbd986400033303d4/test.js#L39-L42 - if (error.code !== 'EPIPE') throw error; - } - }); - }); + body.append('map', JSON.stringify({ 1: ['variables.file'] })); + body.append('1', fs.createReadStream('package.json')); + + try { + const resolved = await fetch(`http://localhost:${port}/graphql`, { + method: 'POST', + body: body as any, + }); + const text = await resolved.text(); + const response = JSON.parse(text); + + expect(response.data.singleUpload).toEqual({ + filename: 'package.json', + encoding: '7bit', + mimetype: 'application/json', + }); + } catch (error) { + // This error began appearing randomly and seems to be a dev dependency bug. + // https://github.com/jaydenseric/apollo-upload-server/blob/18ecdbc7a1f8b69ad51b4affbd986400033303d4/test.js#L39-L42 + if (error.code !== 'EPIPE') throw error; + } + }); + }, + ); describe('errors', () => { it('returns thrown context error as a valid graphql result', async () => { diff --git a/packages/apollo-server-micro/src/__tests__/ApolloServer.test.ts b/packages/apollo-server-micro/src/__tests__/ApolloServer.test.ts index 941b7b25a36..89742ae4da1 100644 --- a/packages/apollo-server-micro/src/__tests__/ApolloServer.test.ts +++ b/packages/apollo-server-micro/src/__tests__/ApolloServer.test.ts @@ -149,46 +149,46 @@ describe('apollo-server-micro', function() { // NODE: Intentionally skip file upload tests on Node.js 10. // Also skip Node.js 6, but only because `graphql-upload` // doesn't support it. - (NODE_MAJOR_VERSION === 10 || NODE_MAJOR_VERSION === 6 - ? describe.skip - : describe)('file uploads', function() { - it('should handle file uploads', async function() { - const apolloServer = new ApolloServer({ - typeDefs: gql` - type File { - filename: String! - mimetype: String! - encoding: String! - } - - type Query { - uploads: [File] - } - - type Mutation { - singleUpload(file: Upload!): File! - } - `, - resolvers: { - Query: { - uploads: () => {}, - }, - Mutation: { - singleUpload: async (_, args) => { - expect((await args.file).stream).toBeDefined(); - return args.file; + (NODE_MAJOR_VERSION === 6 ? describe.skip : describe)( + 'file uploads', + function() { + it('should handle file uploads', async function() { + const apolloServer = new ApolloServer({ + typeDefs: gql` + type File { + filename: String! + mimetype: String! + encoding: String! + } + + type Query { + uploads: [File] + } + + type Mutation { + singleUpload(file: Upload!): File! + } + `, + resolvers: { + Query: { + uploads: () => {}, + }, + Mutation: { + singleUpload: async (_, args) => { + expect((await args.file).stream).toBeDefined(); + return args.file; + }, }, }, - }, - }); - const service = micro(apolloServer.createHandler()); - const url = await listen(service); - - const body = new FormData(); - body.append( - 'operations', - JSON.stringify({ - query: ` + }); + const service = micro(apolloServer.createHandler()); + const url = await listen(service); + + const body = new FormData(); + body.append( + 'operations', + JSON.stringify({ + query: ` mutation($file: Upload!) { singleUpload(file: $file) { filename @@ -197,36 +197,37 @@ describe('apollo-server-micro', function() { } } `, - variables: { - file: null, - }, - }), - ); - body.append('map', JSON.stringify({ 1: ['variables.file'] })); - body.append('1', fs.createReadStream('package.json')); - - try { - const resolved = await fetch(`${url}/graphql`, { - method: 'POST', - body: body as any, - }); - const text = await resolved.text(); - const response = JSON.parse(text); + variables: { + file: null, + }, + }), + ); + body.append('map', JSON.stringify({ 1: ['variables.file'] })); + body.append('1', fs.createReadStream('package.json')); - expect(response.data.singleUpload).toEqual({ - filename: 'package.json', - encoding: '7bit', - mimetype: 'application/json', - }); - } catch (error) { - // This error began appearing randomly and seems to be a dev - // dependency bug. - // https://github.com/jaydenseric/apollo-upload-server/blob/18ecdbc7a1f8b69ad51b4affbd986400033303d4/test.js#L39-L42 - if (error.code !== 'EPIPE') throw error; - } + try { + const resolved = await fetch(`${url}/graphql`, { + method: 'POST', + body: body as any, + }); + const text = await resolved.text(); + const response = JSON.parse(text); + + expect(response.data.singleUpload).toEqual({ + filename: 'package.json', + encoding: '7bit', + mimetype: 'application/json', + }); + } catch (error) { + // This error began appearing randomly and seems to be a dev + // dependency bug. + // https://github.com/jaydenseric/apollo-upload-server/blob/18ecdbc7a1f8b69ad51b4affbd986400033303d4/test.js#L39-L42 + if (error.code !== 'EPIPE') throw error; + } - service.close(); - }); - }); + service.close(); + }); + }, + ); }); }); From 8a58e9f4ceeb08fd6616a1e1d454149588b8c6bd Mon Sep 17 00:00:00 2001 From: Jesse Rosenberger Date: Mon, 3 Dec 2018 14:01:15 +0200 Subject: [PATCH 08/10] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index adcaff8d074..f4147787cc7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ### vNEXT -- **BREAKING FOR NODE.JS <= 8.5.0 ONLY**: To continue using Apollo Server 2.x in versions of Node.js prior to v8.5.0, file uploads must be disabled by setting `uploads: false` on the `ApolloServer` constructor options. Without explicit disabling file-uploads, the server will `throw` at launch (with instructions and a link to our documentation). +- **BREAKING FOR NODE.JS <= 8.5.0 ONLY**: To continue using Apollo Server 2.x in versions of Node.js prior to v8.5.0, file uploads must be disabled by setting `uploads: false` on the `ApolloServer` constructor options. Without explicitly disabling file-uploads, the server will `throw` at launch (with instructions and a link to our documentation). This early deprecation is due to changes in the third-party `graphql-upload` package which Apollo Server utilizes to implement out-of-the-box file upload functionality. While, in general, Apollo Server 2.x aims to support all Node.js versions which were under an LTS policy at the time of its release, we felt this required an exception. By `throw`-ing when `uploads` is not explicitly set to `false`, we aim to make it clear immediately (rather than surprisingly) that this deprecation has taken effect. From 363472c87f39b3cdead56b398dbcfbd0731b9374 Mon Sep 17 00:00:00 2001 From: Jesse Rosenberger Date: Mon, 3 Dec 2018 14:01:28 +0200 Subject: [PATCH 09/10] Update migration-file-uploads.md --- docs/source/migration-file-uploads.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/migration-file-uploads.md b/docs/source/migration-file-uploads.md index 27db226c293..592aedb7c03 100644 --- a/docs/source/migration-file-uploads.md +++ b/docs/source/migration-file-uploads.md @@ -8,7 +8,7 @@ While Node.js versions prior to v8.5.0 are still under [_Long Term Support_ (LTS For example, while Node.js 6.x is covered by Long Term Support until April 2019, there are substantial performance (e.g. [V8](https://v8.dev/) improvements) and language changes (e.g. "modern" ECMAScript support) offered by newer Node.js engines (e.g. 8.x, 10.x). Switching to newer Long Term Support versions of Node.js comes with long-term benefits; Node.js 10.x LTS releases are covered by the Node.js Foundation through April 2021. -Since file upload support for Node.js versions prior to v8.5.0 is no longer offered by `graphql-upload`, users of those versions must must disable file uploads to continue using newer Apollo Server 2.x versions. +Since file upload support for Node.js versions prior to v8.5.0 is no longer offered by `graphql-upload`, users of those versions must disable file uploads to continue using newer Apollo Server 2.x versions. **To disable file uploads and continue using Apollo Server 2.x on Node.js 6.x**, add the `uploads: false` setting to the options when constructing the server. For example: From fe2d5972c0f882763619c93c20de0d9efe0dda84 Mon Sep 17 00:00:00 2001 From: Jesse Rosenberger Date: Mon, 3 Dec 2018 14:08:40 +0200 Subject: [PATCH 10/10] Update ApolloServer.ts --- packages/apollo-server-core/src/ApolloServer.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/apollo-server-core/src/ApolloServer.ts b/packages/apollo-server-core/src/ApolloServer.ts index fede7b2b666..8d81e2f0794 100644 --- a/packages/apollo-server-core/src/ApolloServer.ts +++ b/packages/apollo-server-core/src/ApolloServer.ts @@ -566,15 +566,15 @@ function printNodeFileUploadsMessage() { 'file uploads in Apollo Server 2.x, no longer supports Node.js LTS', 'versions prior to Node.js v8.5.0.', '', - 'If this server DOES NOT USE file uploads, it is necessary to add:', + 'Deployments which NEED file upload capabilities should update to', + 'Node.js >= v8.5.0 to continue using uploads.', '', - ' uploads: false,', + 'If this server DOES NOT NEED file uploads and wishes to continue', + 'using this version of Node.js, uploads can be disabled by adding:', '', - 'to the options for Apollo Server and re-deploy to disable file uploads', - 'and continue using this version of Node.js.', + ' uploads: false,', '', - 'Deployments which need file upload capabilities should update to', - 'Node.js >= v8.5.0 to continue using newer Apollo Server versions.', + '...to the options for Apollo Server and re-deploying the server.', '', 'For more information, see https://bit.ly/gql-upload-node-6.', '',