Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move Uploads to constructor and remove enhanceSchema #1204

Merged
merged 4 commits into from
Jun 21, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
ApolloError,
AuthenticationError,
ForbiddenError,
} from 'apollo-server-core';
} from 'apollo-server-errors';
import { RESTDataSource } from '../RESTDataSource';

import fetch, { mockFetch, unmockFetch } from '../../../../__mocks__/fetch';
Expand Down
63 changes: 44 additions & 19 deletions packages/apollo-server-core/src/ApolloServer.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
import {
makeExecutableSchema,
addMockFunctionsToSchema,
mergeSchemas,
} from 'graphql-tools';
import { makeExecutableSchema, addMockFunctionsToSchema } from 'graphql-tools';
import { Server as HttpServer } from 'http';
import {
execute,
Expand All @@ -18,12 +14,14 @@ import { GraphQLExtension } from 'graphql-extensions';
import { EngineReportingAgent } from 'apollo-engine-reporting';
import { InMemoryLRUCache } from 'apollo-datasource-rest';

import { GraphQLUpload } from 'apollo-upload-server';

import {
SubscriptionServer,
ExecutionParams,
} from 'subscriptions-transport-ws';

//use as default persisted query store
// use as default persisted query store
import Keyv = require('keyv');
import QuickLru = require('quick-lru');

Expand All @@ -38,6 +36,7 @@ import {
Context,
ContextFunction,
SubscriptionServerOptions,
FileUploadOptions,
} from './types';

import { gql } from './index';
Expand Down Expand Up @@ -65,6 +64,7 @@ export class ApolloServerBase {
private engineReportingAgent?: EngineReportingAgent;
private extensions: Array<() => GraphQLExtension>;
protected subscriptionServerOptions?: SubscriptionServerOptions;
protected uploadsConfig?: FileUploadOptions;

// set by installSubscriptionHandlers.
private subscriptionServer?: SubscriptionServer;
Expand All @@ -83,6 +83,7 @@ export class ApolloServerBase {
extensions,
engine,
subscriptions,
uploads,
...requestOptions
} = config;

Expand Down Expand Up @@ -128,16 +129,41 @@ export class ApolloServerBase {
this.requestOptions = requestOptions as GraphQLOptions;
this.context = context;

if (uploads !== false) {
if (this.supportsUploads()) {
if (uploads === true || typeof uploads === 'undefined') {
this.uploadsConfig = {};
} else {
this.uploadsConfig = uploads;
}
//This is here to check if uploads is requested without support. By
//default we enable them if supported by the integration
} else if (uploads) {
throw new Error(
'This implementation of ApolloServer does not support file uploads because the environmnet cannot accept multi-part forms',
);
}
}

//Add upload resolver
if (this.uploadsConfig) {
if (resolvers && !resolvers.Upload) {
resolvers.Upload = GraphQLUpload;
}
}

this.schema = schema
? schema
: makeExecutableSchema({
//we add in the upload scalar, so that schemas that don't include it
//won't error when we makeExecutableSchema
typeDefs: [
gql`
scalar Upload
`,
].concat(typeDefs),
typeDefs: this.uploadsConfig
? [
gql`
scalar Upload
`,
].concat(typeDefs)
: typeDefs,
schemaDirectives,
resolvers,
});
Expand Down Expand Up @@ -182,6 +208,9 @@ export class ApolloServerBase {
}
// This is part of the public API.
this.subscriptionsPath = this.subscriptionServerOptions.path;

//This is here to check if subscriptions are requested without support. By
//default we enable them if supported by the integration
} else if (subscriptions) {
throw new Error(
'This implementation of ApolloServer does not support GraphQL subscriptions.',
Expand All @@ -196,14 +225,6 @@ export class ApolloServerBase {
this.graphqlPath = path;
}

// If this is more generally useful to things other than Upload, we can make
// it public.
protected enhanceSchema(schema: GraphQLSchema) {
this.schema = mergeSchemas({
schemas: [this.schema, schema],
});
}

public async stop() {
if (this.subscriptionServer) await this.subscriptionServer.close();
if (this.engineReportingAgent) {
Expand Down Expand Up @@ -280,6 +301,10 @@ export class ApolloServerBase {
return false;
}

protected supportsUploads(): boolean {
return false;
}

//This function is used by the integrations to generate the graphQLOptions
//from an object containing the request and other integration specific
//options
Expand Down
13 changes: 13 additions & 0 deletions packages/apollo-server-core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ export interface SubscriptionServerOptions {
onDisconnect?: (websocket: WebSocket, context: ConnectionContext) => any;
}

// This configuration is shared between all integrations and should include
// fields that are not specific to a single integration
export interface Config
extends Pick<
GraphQLOptions<Context<any>>,
Expand All @@ -55,6 +57,17 @@ export interface Config
extensions?: Array<() => GraphQLExtension>;
persistedQueries?: PersistedQueryOptions | false;
subscriptions?: Partial<SubscriptionServerOptions> | string | false;
//https://github.com/jaydenseric/apollo-upload-server#options
uploads?: boolean | FileUploadOptions;
}

export interface FileUploadOptions {
//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 interface MiddlewareOptions {
Expand Down
26 changes: 10 additions & 16 deletions packages/apollo-server-express/src/ApolloServer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -419,23 +419,17 @@ describe('apollo-server-express', () => {
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,
});
const response = await resolved.json();
const resolved = await fetch(`http://localhost:${port}/graphql`, {
method: 'POST',
body,
});
const response = await resolved.json();

expect(response.data.singleUpload).to.deep.equal({
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;
}
expect(response.data.singleUpload).to.deep.equal({
filename: 'package.json',
encoding: '7bit',
mimetype: 'application/json',
});
});
});

Expand Down
32 changes: 9 additions & 23 deletions packages/apollo-server-express/src/ApolloServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,10 @@ import * as typeis from 'type-is';

import { graphqlExpress } from './expressApollo';

import {
processRequest as processFileUploads,
GraphQLUpload,
} from 'apollo-upload-server';
import { processRequest as processFileUploads } from 'apollo-upload-server';

export { GraphQLOptions, GraphQLExtension } from 'apollo-server-core';
import { GraphQLOptions, gql, makeExecutableSchema } from 'apollo-server-core';
import { GraphQLOptions, FileUploadOptions } from 'apollo-server-core';

export interface ServerRegistration {
// Note: You can also pass a connect.Server here. If we changed this field to
Expand All @@ -31,12 +28,10 @@ export interface ServerRegistration {
onHealthCheck?: (req: express.Request) => Promise<any>;
disableHealthCheck?: boolean;
gui?: boolean | PlaygroundMiddlewareOptions;
//https://github.com/jaydenseric/apollo-upload-server#options
uploads?: boolean | Record<string, any>;
}

const fileUploadMiddleware = (
uploadsConfig: Record<string, any>,
uploadsConfig: FileUploadOptions,
server: ApolloServerBase,
) => (
req: express.Request,
Expand Down Expand Up @@ -80,6 +75,10 @@ export class ApolloServer extends ApolloServerBase {
return true;
}

protected supportsUploads(): boolean {
return true;
}

public applyMiddleware({
app,
path,
Expand All @@ -88,7 +87,6 @@ export class ApolloServer extends ApolloServerBase {
disableHealthCheck,
gui,
onHealthCheck,
uploads,
}: ServerRegistration) {
if (!path) path = '/graphql';

Expand All @@ -113,20 +111,8 @@ export class ApolloServer extends ApolloServerBase {
}

let uploadsMiddleware;
if (uploads !== false) {
this.enhanceSchema(
makeExecutableSchema({
typeDefs: gql`
scalar Upload
`,
resolvers: { Upload: GraphQLUpload },
}),
);

uploadsMiddleware = fileUploadMiddleware(
typeof uploads !== 'boolean' ? uploads : {},
this,
);
if (this.uploadsConfig) {
uploadsMiddleware = fileUploadMiddleware(this.uploadsConfig, this);
}

// XXX multiple paths?
Expand Down
31 changes: 9 additions & 22 deletions packages/apollo-server-hapi/src/ApolloServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,14 @@ import {
renderPlaygroundPage,
MiddlewareOptions as PlaygroundMiddlewareOptions,
} from 'graphql-playground-html';
import {
processRequest as processFileUploads,
GraphQLUpload,
} from 'apollo-upload-server';
import { processRequest as processFileUploads } from 'apollo-upload-server';

import { graphqlHapi } from './hapiApollo';

export { GraphQLOptions, GraphQLExtension } from 'apollo-server-core';
import { GraphQLOptions, gql, makeExecutableSchema } from 'apollo-server-core';
import { GraphQLOptions, FileUploadOptions } from 'apollo-server-core';

function handleFileUploads(uploadsConfig: Record<string, any>) {
function handleFileUploads(uploadsConfig: FileUploadOptions) {
return async (request: hapi.Request) => {
if (request.mime === 'multipart/form-data') {
Object.defineProperty(request, 'payload', {
Expand All @@ -41,39 +38,29 @@ export class ApolloServer extends ApolloServerBase {
return true;
}

protected supportsUploads(): boolean {
return true;
}

public async applyMiddleware({
app,
cors,
path,
disableHealthCheck,
gui,
onHealthCheck,
uploads,
}: ServerRegistration) {
if (!path) path = '/graphql';

if (uploads !== false) {
this.enhanceSchema(
makeExecutableSchema({
typeDefs: gql`
scalar Upload
`,
resolvers: { Upload: GraphQLUpload },
}),
);
}

await app.ext({
type: 'onRequest',
method: async function(request, h) {
if (request.path !== path) {
return h.continue;
}

if (uploads !== false) {
await handleFileUploads(typeof uploads !== 'boolean' ? uploads : {})(
request,
);
if (this.uploadsConfig) {
await handleFileUploads(this.uploadsConfig)(request);
}

// Note: if you enable a gui in production and expect to be able to see your
Expand Down