Initial Save
This commit is contained in:
289
node_modules/apollo-server/src/__tests__/index.test.ts
generated
vendored
Normal file
289
node_modules/apollo-server/src/__tests__/index.test.ts
generated
vendored
Normal file
@@ -0,0 +1,289 @@
|
||||
import { createConnection } from 'net';
|
||||
import request from 'request';
|
||||
import { createApolloFetch } from 'apollo-fetch';
|
||||
import resolvable from '@josephg/resolvable';
|
||||
|
||||
import { gql, ApolloServer } from '../index';
|
||||
|
||||
const typeDefs = gql`
|
||||
type Query {
|
||||
hello: String
|
||||
hang: String
|
||||
}
|
||||
`;
|
||||
|
||||
const resolvers = {
|
||||
Query: {
|
||||
hello: () => 'hi',
|
||||
},
|
||||
};
|
||||
|
||||
describe('apollo-server', () => {
|
||||
describe('constructor', () => {
|
||||
it('accepts typeDefs and resolvers', () => {
|
||||
expect(() => new ApolloServer({ typeDefs, resolvers })).not.toThrow;
|
||||
});
|
||||
|
||||
it('accepts typeDefs and mocks', () => {
|
||||
expect(() => new ApolloServer({ typeDefs, mocks: true })).not.toThrow;
|
||||
});
|
||||
|
||||
it('runs serverWillStart and serverWillStop', async () => {
|
||||
const fn = jest.fn();
|
||||
const beAsync = () => new Promise((res) => res());
|
||||
const server = new ApolloServer({
|
||||
typeDefs,
|
||||
resolvers,
|
||||
plugins: [
|
||||
{
|
||||
async serverWillStart() {
|
||||
fn('a');
|
||||
await beAsync();
|
||||
fn('b');
|
||||
return {
|
||||
async serverWillStop() {
|
||||
fn('c');
|
||||
await beAsync();
|
||||
fn('d');
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
await server.listen();
|
||||
expect(fn.mock.calls).toEqual([['a'], ['b']]);
|
||||
await server.stop();
|
||||
expect(fn.mock.calls).toEqual([['a'], ['b'], ['c'], ['d']]);
|
||||
});
|
||||
|
||||
describe('stops even with open HTTP connections', () => {
|
||||
it('all connections are idle', async () => {
|
||||
const server = new ApolloServer({
|
||||
typeDefs,
|
||||
resolvers,
|
||||
// Disable killing non-idle connections. This means the test will only
|
||||
// pass if the fast graceful close of the idle connection works.
|
||||
stopGracePeriodMillis: Infinity,
|
||||
});
|
||||
const { port } = await server.listen({ port: 0 });
|
||||
|
||||
// Open a TCP connection to the server, and let it dangle idle
|
||||
// without starting a request.
|
||||
const connectionBarrier = resolvable();
|
||||
createConnection({ host: 'localhost', port: port as number }, () =>
|
||||
connectionBarrier.resolve(),
|
||||
);
|
||||
await connectionBarrier;
|
||||
|
||||
// Stop the server. Before, when this was just net.Server.close, this
|
||||
// would hang. Now that we use stoppable, the idle connection is immediately
|
||||
// killed.
|
||||
await server.stop();
|
||||
});
|
||||
|
||||
it('a connection with an active HTTP request', async () => {
|
||||
const gotToHangBarrier = resolvable();
|
||||
const hangBarrier = resolvable();
|
||||
const server = new ApolloServer({
|
||||
typeDefs,
|
||||
resolvers: {
|
||||
...resolvers,
|
||||
Query: {
|
||||
...resolvers.Query,
|
||||
async hang() {
|
||||
gotToHangBarrier.resolve();
|
||||
await hangBarrier; // never unblocks
|
||||
},
|
||||
},
|
||||
},
|
||||
// A short grace period, because we're going to actually let this
|
||||
// strike.
|
||||
stopGracePeriodMillis: 10,
|
||||
});
|
||||
const { url } = await server.listen({ port: 0 });
|
||||
|
||||
// Start an HTTP request that won't ever finish. (Ignore the very
|
||||
// expected error that happens after the server is stopped.)
|
||||
const apolloFetch = createApolloFetch({ uri: url });
|
||||
apolloFetch({ query: '{hang}' }).catch(() => {});
|
||||
await gotToHangBarrier;
|
||||
|
||||
// Stop the server. Before, when this was just net.Server.close, this
|
||||
// would hang. Now that we use stoppable, the idle connection is immediately
|
||||
// killed.
|
||||
await server.stop();
|
||||
});
|
||||
});
|
||||
|
||||
// These tests are duplicates of ones in apollo-server-integration-testsuite
|
||||
// We don't actually expect Jest to do much here, the purpose of these
|
||||
// tests is to make sure our typings are correct, and to trigger a
|
||||
// compile error if they aren't
|
||||
describe('context field', () => {
|
||||
describe('as a function', () => {
|
||||
it('can accept and return `req`', () => {
|
||||
expect(
|
||||
new ApolloServer({
|
||||
typeDefs,
|
||||
resolvers,
|
||||
context: ({ req }) => ({ req }),
|
||||
}),
|
||||
).not.toThrow;
|
||||
});
|
||||
|
||||
it('can accept nothing and return an empty object', () => {
|
||||
expect(
|
||||
new ApolloServer({
|
||||
typeDefs,
|
||||
resolvers,
|
||||
context: () => ({}),
|
||||
}),
|
||||
).not.toThrow;
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('as an object', () => {
|
||||
it('can be an empty object', () => {
|
||||
expect(
|
||||
new ApolloServer({
|
||||
typeDefs,
|
||||
resolvers,
|
||||
context: {},
|
||||
}),
|
||||
).not.toThrow;
|
||||
});
|
||||
|
||||
it('can contain arbitrary values', () => {
|
||||
expect(
|
||||
new ApolloServer({
|
||||
typeDefs,
|
||||
resolvers,
|
||||
context: { value: 'arbitrary' },
|
||||
}),
|
||||
).not.toThrow;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('without registerServer', () => {
|
||||
let server: ApolloServer;
|
||||
afterEach(async () => {
|
||||
await server.stop();
|
||||
});
|
||||
|
||||
it('can be queried', async () => {
|
||||
server = new ApolloServer({
|
||||
typeDefs,
|
||||
resolvers,
|
||||
});
|
||||
|
||||
const { url: uri } = await server.listen({ port: 0 });
|
||||
const apolloFetch = createApolloFetch({ uri });
|
||||
const result = await apolloFetch({ query: '{hello}' });
|
||||
|
||||
expect(result.data).toEqual({ hello: 'hi' });
|
||||
expect(result.errors).toBeUndefined();
|
||||
});
|
||||
|
||||
it('renders GraphQL playground when browser requests', async () => {
|
||||
const nodeEnv = process.env.NODE_ENV;
|
||||
delete process.env.NODE_ENV;
|
||||
|
||||
server = new ApolloServer({
|
||||
typeDefs,
|
||||
resolvers,
|
||||
stopOnTerminationSignals: false,
|
||||
});
|
||||
|
||||
const { url } = await server.listen({ port: 0 });
|
||||
return new Promise((resolve, reject) => {
|
||||
request(
|
||||
{
|
||||
url,
|
||||
method: 'GET',
|
||||
headers: {
|
||||
accept:
|
||||
'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
|
||||
},
|
||||
},
|
||||
(error, response, body) => {
|
||||
process.env.NODE_ENV = nodeEnv;
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
expect(body).toMatch('GraphQLPlayground');
|
||||
expect(response.statusCode).toEqual(200);
|
||||
resolve();
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('configures cors', async () => {
|
||||
server = new ApolloServer({
|
||||
typeDefs,
|
||||
resolvers,
|
||||
});
|
||||
|
||||
const { url: uri } = await server.listen({ port: 0 });
|
||||
|
||||
const apolloFetch = createApolloFetch({ uri }).useAfter(
|
||||
(response, next) => {
|
||||
expect(
|
||||
response.response.headers.get('access-control-allow-origin'),
|
||||
).toEqual('*');
|
||||
next();
|
||||
},
|
||||
);
|
||||
await apolloFetch({ query: '{hello}' });
|
||||
});
|
||||
|
||||
it('configures cors', async () => {
|
||||
server = new ApolloServer({
|
||||
typeDefs,
|
||||
resolvers,
|
||||
cors: { origin: 'localhost' },
|
||||
});
|
||||
|
||||
const { url: uri } = await server.listen({ port: 0 });
|
||||
|
||||
const apolloFetch = createApolloFetch({ uri }).useAfter(
|
||||
(response, next) => {
|
||||
expect(
|
||||
response.response.headers.get('access-control-allow-origin'),
|
||||
).toEqual('localhost');
|
||||
next();
|
||||
},
|
||||
);
|
||||
await apolloFetch({ query: '{hello}' });
|
||||
});
|
||||
|
||||
it('creates a healthcheck endpoint', async () => {
|
||||
server = new ApolloServer({
|
||||
typeDefs,
|
||||
resolvers,
|
||||
});
|
||||
|
||||
const { port } = await server.listen({ port: 0 });
|
||||
return new Promise((resolve, reject) => {
|
||||
request(
|
||||
{
|
||||
url: `http://localhost:${port}/.well-known/apollo/server-health`,
|
||||
method: 'GET',
|
||||
},
|
||||
(error, response, body) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
expect(body).toEqual(JSON.stringify({ status: 'pass' }));
|
||||
expect(response.statusCode).toEqual(200);
|
||||
resolve();
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
7
node_modules/apollo-server/src/__tests__/tsconfig.json
generated
vendored
Normal file
7
node_modules/apollo-server/src/__tests__/tsconfig.json
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": "../../../../tsconfig.test.base",
|
||||
"include": ["**/*"],
|
||||
"references": [
|
||||
{ "path": "../../" },
|
||||
]
|
||||
}
|
||||
25
node_modules/apollo-server/src/exports.ts
generated
vendored
Normal file
25
node_modules/apollo-server/src/exports.ts
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
export * from 'graphql-tools';
|
||||
export * from 'graphql-subscriptions';
|
||||
|
||||
export {
|
||||
gql,
|
||||
GraphQLUpload,
|
||||
GraphQLOptions,
|
||||
GraphQLExtension,
|
||||
Config,
|
||||
GraphQLSchemaModule,
|
||||
// Errors
|
||||
ApolloError,
|
||||
toApolloError,
|
||||
SyntaxError,
|
||||
ValidationError,
|
||||
AuthenticationError,
|
||||
ForbiddenError,
|
||||
UserInputError,
|
||||
// playground
|
||||
defaultPlaygroundOptions,
|
||||
PlaygroundConfig,
|
||||
PlaygroundRenderPageOptions,
|
||||
} from 'apollo-server-core';
|
||||
|
||||
export { CorsOptions } from 'apollo-server-express';
|
||||
166
node_modules/apollo-server/src/index.ts
generated
vendored
Normal file
166
node_modules/apollo-server/src/index.ts
generated
vendored
Normal file
@@ -0,0 +1,166 @@
|
||||
// This is the "batteries-included" version of `apollo-server-express`. It
|
||||
// handles creating the Express app and HTTP server for you (using whatever
|
||||
// version of `express` its dependency pulls in). If you need to customize the
|
||||
// Express app or HTTP server at all, you just use `apollo-server-express`
|
||||
// instead.
|
||||
import express from 'express';
|
||||
import http from 'http';
|
||||
import stoppable from 'stoppable';
|
||||
import {
|
||||
ApolloServer as ApolloServerBase,
|
||||
CorsOptions,
|
||||
ApolloServerExpressConfig,
|
||||
} from 'apollo-server-express';
|
||||
|
||||
export * from './exports';
|
||||
|
||||
export interface ServerInfo {
|
||||
address: string;
|
||||
family: string;
|
||||
url: string;
|
||||
subscriptionsUrl: string;
|
||||
port: number | string;
|
||||
subscriptionsPath: string;
|
||||
server: http.Server;
|
||||
}
|
||||
|
||||
export class ApolloServer extends ApolloServerBase {
|
||||
private httpServer?: stoppable.StoppableServer;
|
||||
private cors?: CorsOptions | boolean;
|
||||
private onHealthCheck?: (req: express.Request) => Promise<any>;
|
||||
private stopGracePeriodMillis: number;
|
||||
|
||||
constructor(
|
||||
config: ApolloServerExpressConfig & {
|
||||
cors?: CorsOptions | boolean;
|
||||
onHealthCheck?: (req: express.Request) => Promise<any>;
|
||||
stopGracePeriodMillis?: number;
|
||||
},
|
||||
) {
|
||||
super(config);
|
||||
this.cors = config && config.cors;
|
||||
this.onHealthCheck = config && config.onHealthCheck;
|
||||
this.stopGracePeriodMillis = config?.stopGracePeriodMillis ?? 10_000;
|
||||
}
|
||||
|
||||
private createServerInfo(
|
||||
server: http.Server,
|
||||
subscriptionsPath?: string,
|
||||
): ServerInfo {
|
||||
const serverInfo: any = {
|
||||
// TODO: Once we bump to `@types/node@10` or higher, we can replace cast
|
||||
// with the `net.AddressInfo` type, rather than this custom interface.
|
||||
// Unfortunately, prior to the 10.x types, this type existed on `dgram`,
|
||||
// but not on `net`, and in later types, the `server.address()` signature
|
||||
// can also be a string.
|
||||
...(server.address() as {
|
||||
address: string;
|
||||
family: string;
|
||||
port: number;
|
||||
}),
|
||||
server,
|
||||
subscriptionsPath,
|
||||
};
|
||||
|
||||
// Convert IPs which mean "any address" (IPv4 or IPv6) into localhost
|
||||
// corresponding loopback ip. Note that the url field we're setting is
|
||||
// primarily for consumption by our test suite. If this heuristic is wrong
|
||||
// for your use case, explicitly specify a frontend host (in the `host`
|
||||
// option to ApolloServer.listen).
|
||||
let hostForUrl = serverInfo.address;
|
||||
if (serverInfo.address === '' || serverInfo.address === '::')
|
||||
hostForUrl = 'localhost';
|
||||
|
||||
serverInfo.url = require('url').format({
|
||||
protocol: 'http',
|
||||
hostname: hostForUrl,
|
||||
port: serverInfo.port,
|
||||
pathname: this.graphqlPath,
|
||||
});
|
||||
|
||||
serverInfo.subscriptionsUrl = require('url').format({
|
||||
protocol: 'ws',
|
||||
hostname: hostForUrl,
|
||||
port: serverInfo.port,
|
||||
slashes: true,
|
||||
pathname: subscriptionsPath,
|
||||
});
|
||||
|
||||
return serverInfo;
|
||||
}
|
||||
|
||||
public applyMiddleware() {
|
||||
throw new Error(
|
||||
'To use Apollo Server with an existing express application, please use apollo-server-express',
|
||||
);
|
||||
}
|
||||
|
||||
public async start(): Promise<void> {
|
||||
throw new Error(
|
||||
"When using the `apollo-server` package, you don't need to call start(); just call listen().",
|
||||
);
|
||||
}
|
||||
|
||||
// Listen takes the same arguments as http.Server.listen.
|
||||
public async listen(...opts: Array<any>): Promise<ServerInfo> {
|
||||
// First start the server and throw if startup fails (eg, schema can't be loaded
|
||||
// or a serverWillStart plugin throws).
|
||||
await this._start();
|
||||
|
||||
// This class is the easy mode for people who don't create their own express
|
||||
// object, so we have to create it.
|
||||
const app = express();
|
||||
|
||||
app.disable('x-powered-by');
|
||||
|
||||
// provide generous values for the getting started experience
|
||||
super.applyMiddleware({
|
||||
app,
|
||||
path: '/',
|
||||
bodyParserConfig: { limit: '50mb' },
|
||||
onHealthCheck: this.onHealthCheck,
|
||||
cors:
|
||||
typeof this.cors !== 'undefined'
|
||||
? this.cors
|
||||
: {
|
||||
origin: '*',
|
||||
},
|
||||
});
|
||||
|
||||
const httpServer = http.createServer(app);
|
||||
// `stoppable` adds a `.stop()` method which:
|
||||
// - closes the server (ie, stops listening)
|
||||
// - closes all connections with no active requests
|
||||
// - continues to close connections when their active request count drops to
|
||||
// zero
|
||||
// - in 10 seconds (configurable), closes all remaining active connections
|
||||
// - calls its callback once there are no remaining active connections
|
||||
//
|
||||
// If you don't like this behavior, use apollo-server-express instead of
|
||||
// apollo-server.
|
||||
this.httpServer = stoppable(httpServer, this.stopGracePeriodMillis);
|
||||
|
||||
if (this.subscriptionServerOptions) {
|
||||
this.installSubscriptionHandlers(httpServer);
|
||||
}
|
||||
|
||||
await new Promise((resolve) => {
|
||||
httpServer.once('listening', resolve);
|
||||
// If the user passed a callback to listen, it'll get called in addition
|
||||
// to our resolver. They won't have the ability to get the ServerInfo
|
||||
// object unless they use our Promise, though.
|
||||
httpServer.listen(...(opts.length ? opts : [{ port: 4000 }]));
|
||||
});
|
||||
|
||||
return this.createServerInfo(httpServer, this.subscriptionsPath);
|
||||
}
|
||||
|
||||
public async stop() {
|
||||
if (this.httpServer) {
|
||||
const httpServer = this.httpServer;
|
||||
await new Promise<void>((resolve) => httpServer.stop(() => resolve()));
|
||||
this.httpServer = undefined;
|
||||
}
|
||||
await super.stop();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user