"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __rest = (this && this.__rest) || function (s, e) {
    var t = {};
    for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
        t[p] = s[p];
    if (s != null && typeof Object.getOwnPropertySymbols === "function")
        for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
            if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
                t[p[i]] = s[p[i]];
        }
    return t;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ApolloServerBase = void 0;
const graphql_tools_1 = require("graphql-tools");
const net_1 = require("net");
const tls_1 = require("tls");
const loglevel_1 = __importDefault(require("loglevel"));
const graphql_1 = require("graphql");
const resolvable_1 = __importDefault(require("@josephg/resolvable"));
const apollo_server_caching_1 = require("apollo-server-caching");
const runtimeSupportsUploads_1 = __importDefault(require("./utils/runtimeSupportsUploads"));
const apollo_server_errors_1 = require("apollo-server-errors");
const index_1 = require("./index");
const playground_1 = require("./playground");
const schemaHash_1 = require("./utils/schemaHash");
const isDirectiveDefined_1 = require("./utils/isDirectiveDefined");
const requestPipeline_1 = require("./requestPipeline");
const apollo_server_env_1 = require("apollo-server-env");
const apollo_tools_1 = require("@apollographql/apollo-tools");
const apollo_tracing_1 = require("apollo-tracing");
const apollo_cache_control_1 = require("apollo-cache-control");
const runHttpQuery_1 = require("./runHttpQuery");
const isNodeLike_1 = __importDefault(require("./utils/isNodeLike"));
const determineApolloConfig_1 = require("./determineApolloConfig");
const plugin_1 = require("./plugin");
const internalPlugin_1 = require("./plugin/internalPlugin");
const NoIntrospection = (context) => ({
    Field(node) {
        if (node.name.value === '__schema' || node.name.value === '__type') {
            context.reportError(new graphql_1.GraphQLError('GraphQL introspection is not allowed by Apollo Server, but the query contained __schema or __type. To enable introspection, pass introspection: true to ApolloServer in production', [node]));
        }
    },
});
const forbidUploadsForTesting = process && process.env.NODE_ENV === 'test' && !runtimeSupportsUploads_1.default;
function approximateObjectSize(obj) {
    return Buffer.byteLength(JSON.stringify(obj), 'utf8');
}
class UnreachableCaseError extends Error {
    constructor(val) {
        super(`Unreachable case: ${val}`);
    }
}
class ApolloServerBase {
    constructor(config) {
        this.graphqlPath = '/graphql';
        this.requestOptions = Object.create(null);
        this.plugins = [];
        this.toDispose = new Set();
        this.toDisposeLast = new Set();
        if (!config)
            throw new Error('ApolloServer requires options.');
        this.config = config;
        const { context, resolvers, schema, schemaDirectives, modules, typeDefs, parseOptions = {}, introspection, mocks, mockEntireSchema, extensions, subscriptions, uploads, playground, plugins, gateway, cacheControl, experimental_approximateDocumentStoreMiB, stopOnTerminationSignals, apollo, engine } = config, requestOptions = __rest(config, ["context", "resolvers", "schema", "schemaDirectives", "modules", "typeDefs", "parseOptions", "introspection", "mocks", "mockEntireSchema", "extensions", "subscriptions", "uploads", "playground", "plugins", "gateway", "cacheControl", "experimental_approximateDocumentStoreMiB", "stopOnTerminationSignals", "apollo", "engine"]);
        if (engine !== undefined && apollo) {
            throw new Error('You cannot provide both `engine` and `apollo` to `new ApolloServer()`. ' +
                'For details on how to migrate all of your options out of `engine`, see ' +
                'https://go.apollo.dev/s/migration-engine-plugins');
        }
        if (config.logger) {
            this.logger = config.logger;
        }
        else {
            const loglevelLogger = loglevel_1.default.getLogger('apollo-server');
            if (this.config.debug === true) {
                loglevelLogger.setLevel(loglevel_1.default.levels.DEBUG);
            }
            else {
                loglevelLogger.setLevel(loglevel_1.default.levels.INFO);
            }
            this.logger = loglevelLogger;
        }
        this.apolloConfig = determineApolloConfig_1.determineApolloConfig(apollo, engine, this.logger);
        if (gateway && (modules || schema || typeDefs || resolvers)) {
            throw new Error('Cannot define both `gateway` and any of: `modules`, `schema`, `typeDefs`, or `resolvers`');
        }
        this.parseOptions = parseOptions;
        this.context = context;
        const isDev = process.env.NODE_ENV !== 'production';
        if ((typeof introspection === 'boolean' && !introspection) ||
            (introspection === undefined && !isDev)) {
            const noIntro = [NoIntrospection];
            requestOptions.validationRules = requestOptions.validationRules
                ? requestOptions.validationRules.concat(noIntro)
                : noIntro;
        }
        if (!requestOptions.cache) {
            requestOptions.cache = new apollo_server_caching_1.InMemoryLRUCache();
        }
        if (requestOptions.persistedQueries !== false) {
            const _a = requestOptions.persistedQueries || Object.create(null), { cache: apqCache = requestOptions.cache } = _a, apqOtherOptions = __rest(_a, ["cache"]);
            requestOptions.persistedQueries = Object.assign({ cache: new apollo_server_caching_1.PrefixingKeyValueCache(apqCache, requestPipeline_1.APQ_CACHE_PREFIX) }, apqOtherOptions);
        }
        else {
            delete requestOptions.persistedQueries;
        }
        this.requestOptions = requestOptions;
        this.disableUploadsIfSchemaDoesNotUseUploadScalar = false;
        if (uploads !== false && !forbidUploadsForTesting) {
            if (this.supportsUploads()) {
                if (!runtimeSupportsUploads_1.default) {
                    printNodeFileUploadsMessage(this.logger);
                    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) {
                    this.uploadsConfig = {};
                    warnAboutUploads(this.logger, false);
                }
                else if (typeof uploads === 'undefined') {
                    this.uploadsConfig = {};
                    this.disableUploadsIfSchemaDoesNotUseUploadScalar = true;
                }
                else {
                    this.uploadsConfig = uploads;
                    warnAboutUploads(this.logger, false);
                }
            }
            else if (uploads) {
                throw new Error('This implementation of ApolloServer does not support file uploads because the environment cannot accept multi-part forms');
            }
        }
        if (gateway && subscriptions !== false) {
            throw new Error([
                'Subscriptions are not yet compatible with the gateway.',
                "Set `subscriptions: false` in Apollo Server's constructor to",
                'explicitly disable subscriptions (which are on by default)',
                'and allow for gateway functionality.',
            ].join(' '));
        }
        else if (subscriptions !== false) {
            if (this.supportsSubscriptions()) {
                if (subscriptions === true || typeof subscriptions === 'undefined') {
                    this.subscriptionServerOptions = {
                        path: this.graphqlPath,
                    };
                }
                else if (typeof subscriptions === 'string') {
                    this.subscriptionServerOptions = { path: subscriptions };
                }
                else {
                    this.subscriptionServerOptions = Object.assign({ path: this.graphqlPath }, subscriptions);
                }
                this.subscriptionsPath = this.subscriptionServerOptions.path;
            }
            else if (subscriptions) {
                throw new Error('This implementation of ApolloServer does not support GraphQL subscriptions.');
            }
        }
        this.playgroundOptions = playground_1.createPlaygroundOptions(playground);
        this.ensurePluginInstantiation(plugins);
        if (typeof stopOnTerminationSignals === 'boolean'
            ? stopOnTerminationSignals
            : typeof engine === 'object' &&
                typeof engine.handleSignals === 'boolean'
                ? engine.handleSignals
                : isNodeLike_1.default && process.env.NODE_ENV !== 'test') {
            const signals = ['SIGINT', 'SIGTERM'];
            let receivedSignal = false;
            signals.forEach((signal) => {
                const handler = () => __awaiter(this, void 0, void 0, function* () {
                    if (receivedSignal) {
                        return;
                    }
                    receivedSignal = true;
                    try {
                        yield this.stop();
                    }
                    catch (e) {
                        this.logger.error(`stop() threw during ${signal} shutdown`);
                        this.logger.error(e);
                        process.exit(1);
                    }
                    process.kill(process.pid, signal);
                });
                process.on(signal, handler);
                this.toDisposeLast.add(() => __awaiter(this, void 0, void 0, function* () {
                    process.removeListener(signal, handler);
                }));
            });
        }
        if (gateway) {
            this.state = { phase: 'initialized with gateway', gateway };
            this.requestOptions.executor = gateway.executor;
        }
        else {
            this.state = {
                phase: 'initialized with schema',
                schemaDerivedData: this.generateSchemaDerivedData(this.constructSchema()),
            };
            this.schema = this.state.schemaDerivedData.schema;
        }
        if (this.serverlessFramework()) {
            this.ensureStarting();
        }
    }
    setGraphQLPath(path) {
        this.graphqlPath = path;
    }
    start() {
        return __awaiter(this, void 0, void 0, function* () {
            if (this.serverlessFramework()) {
                throw new Error('When using an ApolloServer subclass from a serverless framework ' +
                    "package, you don't need to call start(); just call createHandler().");
            }
            return yield this._start();
        });
    }
    _start() {
        var _a;
        return __awaiter(this, void 0, void 0, function* () {
            const initialState = this.state;
            if (initialState.phase !== 'initialized with gateway' &&
                initialState.phase !== 'initialized with schema') {
                throw new Error(`called start() with surprising state ${initialState.phase}`);
            }
            const barrier = resolvable_1.default();
            this.state = { phase: 'starting', barrier };
            let loadedSchema = false;
            try {
                const schemaDerivedData = initialState.phase === 'initialized with schema'
                    ? initialState.schemaDerivedData
                    : this.generateSchemaDerivedData(yield this.startGatewayAndLoadSchema(initialState.gateway));
                loadedSchema = true;
                this.state = {
                    phase: 'invoking serverWillStart',
                    barrier,
                    schemaDerivedData,
                };
                const service = {
                    logger: this.logger,
                    schema: schemaDerivedData.schema,
                    schemaHash: schemaDerivedData.schemaHash,
                    apollo: this.apolloConfig,
                    serverlessFramework: this.serverlessFramework(),
                    engine: {
                        serviceID: this.apolloConfig.graphId,
                        apiKeyHash: this.apolloConfig.keyHash,
                    },
                };
                if ((_a = this.requestOptions.persistedQueries) === null || _a === void 0 ? void 0 : _a.cache) {
                    service.persistedQueries = {
                        cache: this.requestOptions.persistedQueries.cache,
                    };
                }
                const serverListeners = (yield Promise.all(this.plugins.map((plugin) => plugin.serverWillStart && plugin.serverWillStart(service)))).filter((maybeServerListener) => typeof maybeServerListener === 'object' &&
                    !!maybeServerListener.serverWillStop);
                this.toDispose.add(() => __awaiter(this, void 0, void 0, function* () {
                    yield Promise.all(serverListeners.map(({ serverWillStop }) => serverWillStop === null || serverWillStop === void 0 ? void 0 : serverWillStop()));
                }));
                this.state = { phase: 'started', schemaDerivedData };
            }
            catch (error) {
                this.state = { phase: 'failed to start', error, loadedSchema };
                throw error;
            }
            finally {
                barrier.resolve();
            }
        });
    }
    willStart() {
        return __awaiter(this, void 0, void 0, function* () {
            this.ensureStarting();
        });
    }
    ensureStarted() {
        return __awaiter(this, void 0, void 0, function* () {
            while (true) {
                switch (this.state.phase) {
                    case 'initialized with gateway':
                    case 'initialized with schema':
                        try {
                            yield this._start();
                        }
                        catch (_a) {
                        }
                        break;
                    case 'starting':
                    case 'invoking serverWillStart':
                        yield this.state.barrier;
                        break;
                    case 'failed to start':
                        this.logStartupError(this.state.error);
                        throw new Error('This data graph is missing a valid configuration. More details may be available in the server logs.');
                    case 'started':
                        return this.state.schemaDerivedData;
                    case 'stopping':
                        throw new Error('Cannot execute GraphQL operations while the server is stopping.');
                    case 'stopped':
                        throw new Error('Cannot execute GraphQL operations after the server has stopped.');
                    default:
                        throw new UnreachableCaseError(this.state);
                }
            }
        });
    }
    ensureStarting() {
        if (this.state.phase === 'initialized with gateway' ||
            this.state.phase === 'initialized with schema') {
            this._start().catch((e) => this.logStartupError(e));
        }
    }
    logStartupError(err) {
        const prelude = this.serverlessFramework()
            ? 'An error occurred during Apollo Server startup.'
            : 'Apollo Server was started implicitly and an error occurred during startup. ' +
                '(Consider calling `await server.start()` immediately after ' +
                '`server = new ApolloServer()` so you can handle these errors directly before ' +
                'starting your web server.)';
        this.logger.error(prelude +
            ' All GraphQL requests will now fail. The startup error ' +
            'was: ' +
            ((err && err.message) || err));
    }
    startGatewayAndLoadSchema(gateway) {
        return __awaiter(this, void 0, void 0, function* () {
            const unsubscriber = gateway.onSchemaChange((schema) => {
                if (this.state.phase === 'started') {
                    this.state.schemaDerivedData = this.generateSchemaDerivedData(schema);
                }
            });
            this.toDispose.add(() => __awaiter(this, void 0, void 0, function* () { return unsubscriber(); }));
            const engineConfig = this.apolloConfig.keyHash && this.apolloConfig.graphId
                ? {
                    apiKeyHash: this.apolloConfig.keyHash,
                    graphId: this.apolloConfig.graphId,
                    graphVariant: this.apolloConfig.graphVariant,
                }
                : undefined;
            const config = yield gateway.load({
                apollo: this.apolloConfig,
                engine: engineConfig,
            });
            this.toDispose.add(() => __awaiter(this, void 0, void 0, function* () { var _a; return yield ((_a = gateway.stop) === null || _a === void 0 ? void 0 : _a.call(gateway)); }));
            return config.schema;
        });
    }
    constructSchema() {
        const { schema, modules, typeDefs, resolvers, schemaDirectives, parseOptions, } = this.config;
        if (schema) {
            return schema;
        }
        if (modules) {
            const { schema, errors } = apollo_tools_1.buildServiceDefinition(modules);
            if (errors && errors.length > 0) {
                throw new Error(errors.map((error) => error.message).join('\n\n'));
            }
            return schema;
        }
        if (!typeDefs) {
            throw Error('Apollo Server requires either an existing schema, modules or typeDefs');
        }
        const augmentedTypeDefs = Array.isArray(typeDefs) ? typeDefs : [typeDefs];
        if (!isDirectiveDefined_1.isDirectiveDefined(augmentedTypeDefs, 'cacheControl')) {
            augmentedTypeDefs.push(index_1.gql `
          enum CacheControlScope {
            PUBLIC
            PRIVATE
          }

          directive @cacheControl(
            maxAge: Int
            scope: CacheControlScope
          ) on FIELD_DEFINITION | OBJECT | INTERFACE
        `);
        }
        if (this.uploadsConfig) {
            const { GraphQLUpload } = require('@apollographql/graphql-upload-8-fork');
            if (Array.isArray(resolvers)) {
                if (resolvers.every((resolver) => !resolver.Upload)) {
                    resolvers.push({ Upload: GraphQLUpload });
                }
            }
            else {
                if (resolvers && !resolvers.Upload) {
                    resolvers.Upload = GraphQLUpload;
                }
            }
            augmentedTypeDefs.push(index_1.gql `
          scalar Upload
        `);
        }
        return graphql_tools_1.makeExecutableSchema({
            typeDefs: augmentedTypeDefs,
            schemaDirectives,
            resolvers,
            parseOptions,
        });
    }
    generateSchemaDerivedData(schema) {
        const schemaHash = schemaHash_1.generateSchemaHash(schema);
        const { mocks, mockEntireSchema, extensions: _extensions } = this.config;
        if (mocks || (typeof mockEntireSchema !== 'undefined' && mocks !== false)) {
            graphql_tools_1.addMockFunctionsToSchema({
                schema,
                mocks: typeof mocks === 'boolean' || typeof mocks === 'undefined'
                    ? {}
                    : mocks,
                preserveResolvers: typeof mockEntireSchema === 'undefined' ? false : !mockEntireSchema,
            });
        }
        const extensions = [];
        extensions.push(...(_extensions || []));
        const documentStore = this.initializeDocumentStore();
        let disableUploads = false;
        if (this.disableUploadsIfSchemaDoesNotUseUploadScalar) {
            const ast = graphql_1.parse(graphql_1.printSchema(schema));
            disableUploads = true;
            graphql_1.visit(ast, {
                NamedType(node) {
                    if (node.name.value === 'Upload') {
                        disableUploads = false;
                    }
                }
            });
            if (!disableUploads) {
                warnAboutUploads(this.logger, true);
            }
        }
        return {
            schema,
            schemaHash,
            extensions,
            documentStore,
            disableUploads,
        };
    }
    disableUploads() {
        return this.state.phase !== 'started' || this.state.schemaDerivedData.disableUploads;
    }
    stop() {
        return __awaiter(this, void 0, void 0, function* () {
            if (this.state.phase === 'stopped') {
                if (this.state.stopError) {
                    throw this.state.stopError;
                }
                return;
            }
            if (this.state.phase === 'stopping') {
                yield this.state.barrier;
                const state = this.state;
                if (state.phase !== 'stopped') {
                    throw Error(`Surprising post-stopping state ${state.phase}`);
                }
                if (state.stopError) {
                    throw state.stopError;
                }
                return;
            }
            this.state = { phase: 'stopping', barrier: resolvable_1.default() };
            try {
                yield Promise.all([...this.toDispose].map((dispose) => dispose()));
                if (this.subscriptionServer)
                    this.subscriptionServer.close();
                yield Promise.all([...this.toDisposeLast].map((dispose) => dispose()));
            }
            catch (stopError) {
                this.state = { phase: 'stopped', stopError };
                return;
            }
            this.state = { phase: 'stopped', stopError: null };
        });
    }
    installSubscriptionHandlers(server) {
        if (!this.subscriptionServerOptions) {
            if (this.config.gateway) {
                throw Error('Subscriptions are not supported when operating as a gateway');
            }
            if (this.supportsSubscriptions()) {
                throw Error('Subscriptions are disabled, due to subscriptions set to false in the ApolloServer constructor');
            }
            else {
                throw Error('Subscriptions are not supported, choose an integration, such as apollo-server-express that allows persistent connections');
            }
        }
        const { SubscriptionServer } = require('subscriptions-transport-ws');
        const { onDisconnect, onConnect, keepAlive, path, } = this.subscriptionServerOptions;
        let schema;
        switch (this.state.phase) {
            case 'initialized with schema':
            case 'invoking serverWillStart':
            case 'started':
                schema = this.state.schemaDerivedData.schema;
                break;
            case 'initialized with gateway':
            case 'starting':
            case 'failed to start':
            case 'stopping':
            case 'stopped':
                throw new Error(`Can't install subscription handlers when state is ${this.state.phase}`);
            default:
                throw new UnreachableCaseError(this.state);
        }
        this.subscriptionServer = SubscriptionServer.create({
            schema,
            execute: graphql_1.execute,
            subscribe: graphql_1.subscribe,
            onConnect: onConnect
                ? onConnect
                : (connectionParams) => (Object.assign({}, connectionParams)),
            onDisconnect: onDisconnect,
            onOperation: (message, connection) => __awaiter(this, void 0, void 0, function* () {
                connection.formatResponse = (value) => (Object.assign(Object.assign({}, value), { errors: value.errors &&
                        apollo_server_errors_1.formatApolloErrors([...value.errors], {
                            formatter: this.requestOptions.formatError,
                            debug: this.requestOptions.debug,
                        }) }));
                connection.formatError = this.requestOptions.formatError;
                let context = this.context ? this.context : { connection };
                try {
                    context =
                        typeof this.context === 'function'
                            ? yield this.context({ connection, payload: message.payload })
                            : context;
                }
                catch (e) {
                    throw apollo_server_errors_1.formatApolloErrors([e], {
                        formatter: this.requestOptions.formatError,
                        debug: this.requestOptions.debug,
                    })[0];
                }
                return Object.assign(Object.assign({}, connection), { context });
            }),
            keepAlive,
            validationRules: this.requestOptions.validationRules,
        }, server instanceof net_1.Server || server instanceof tls_1.Server
            ? {
                server,
                path,
            }
            : server);
    }
    supportsSubscriptions() {
        return false;
    }
    supportsUploads() {
        return false;
    }
    serverlessFramework() {
        return false;
    }
    ensurePluginInstantiation(plugins = []) {
        var _a, _b;
        const pluginsToInit = [];
        if (this.config.tracing) {
            pluginsToInit.push(apollo_tracing_1.plugin());
        }
        if (this.config.cacheControl !== false) {
            let cacheControlOptions = {};
            if (typeof this.config.cacheControl === 'boolean' &&
                this.config.cacheControl === true) {
                cacheControlOptions = {
                    stripFormattedExtensions: false,
                    calculateHttpHeaders: false,
                    defaultMaxAge: 0,
                };
            }
            else {
                cacheControlOptions = Object.assign({ stripFormattedExtensions: true, calculateHttpHeaders: true, defaultMaxAge: 0 }, this.config.cacheControl);
            }
            pluginsToInit.push(apollo_cache_control_1.plugin(cacheControlOptions));
        }
        pluginsToInit.push(...plugins);
        this.plugins = pluginsToInit.map((plugin) => {
            if (typeof plugin === 'function') {
                return plugin();
            }
            return plugin;
        });
        const alreadyHavePluginWithInternalId = (id) => this.plugins.some((p) => internalPlugin_1.pluginIsInternal(p) && p.__internal_plugin_id__() === id);
        {
            const alreadyHavePlugin = alreadyHavePluginWithInternalId('UsageReporting');
            const { engine } = this.config;
            const disabledViaLegacyOption = engine === false ||
                (typeof engine === 'object' && engine.reportTiming === false);
            if (alreadyHavePlugin) {
                if (engine !== undefined) {
                    throw Error("You can't combine the legacy `new ApolloServer({engine})` option with directly " +
                        'creating an ApolloServerPluginUsageReporting plugin. See ' +
                        'https://go.apollo.dev/s/migration-engine-plugins');
                }
            }
            else if (this.apolloConfig.key && !disabledViaLegacyOption) {
                this.plugins.unshift(typeof engine === 'object'
                    ? plugin_1.ApolloServerPluginUsageReportingFromLegacyOptions(engine)
                    : plugin_1.ApolloServerPluginUsageReporting());
            }
        }
        {
            const alreadyHavePlugin = alreadyHavePluginWithInternalId('SchemaReporting');
            const enabledViaEnvVar = process.env.APOLLO_SCHEMA_REPORTING === 'true';
            const { engine } = this.config;
            const enabledViaLegacyOption = typeof engine === 'object' &&
                (engine.reportSchema || engine.experimental_schemaReporting);
            if (alreadyHavePlugin || enabledViaEnvVar || enabledViaLegacyOption) {
                if (this.config.gateway) {
                    throw new Error([
                        "Schema reporting is not yet compatible with the gateway. If you're",
                        'interested in using schema reporting with the gateway, please',
                        'contact Apollo support. To set up managed federation, see',
                        'https://go.apollo.dev/s/managed-federation',
                    ].join(' '));
                }
            }
            if (alreadyHavePlugin) {
                if (engine !== undefined) {
                    throw Error("You can't combine the legacy `new ApolloServer({engine})` option with directly " +
                        'creating an ApolloServerPluginSchemaReporting plugin. See ' +
                        'https://go.apollo.dev/s/migration-engine-plugins');
                }
            }
            else if (!this.apolloConfig.key) {
                if (enabledViaEnvVar) {
                    throw new Error("You've enabled schema reporting by setting the APOLLO_SCHEMA_REPORTING " +
                        'environment variable to true, but you also need to provide your ' +
                        'Apollo API key, via the APOLLO_KEY environment ' +
                        'variable or via `new ApolloServer({apollo: {key})');
                }
                if (enabledViaLegacyOption) {
                    throw new Error("You've enabled schema reporting in the `engine` argument to `new ApolloServer()`, " +
                        'but you also need to provide your Apollo API key, via the APOLLO_KEY environment ' +
                        'variable or via `new ApolloServer({apollo: {key})');
                }
            }
            else if (enabledViaEnvVar || enabledViaLegacyOption) {
                const options = {};
                if (typeof engine === 'object') {
                    options.initialDelayMaxMs = (_a = engine.schemaReportingInitialDelayMaxMs) !== null && _a !== void 0 ? _a : engine.experimental_schemaReportingInitialDelayMaxMs;
                    options.overrideReportedSchema = (_b = engine.overrideReportedSchema) !== null && _b !== void 0 ? _b : engine.experimental_overrideReportedSchema;
                    options.endpointUrl = engine.schemaReportingUrl;
                }
                this.plugins.push(plugin_1.ApolloServerPluginSchemaReporting(options));
            }
        }
        {
            const alreadyHavePlugin = alreadyHavePluginWithInternalId('InlineTrace');
            const { engine } = this.config;
            if (alreadyHavePlugin) {
                if (engine !== undefined) {
                    throw Error("You can't combine the legacy `new ApolloServer({engine})` option with directly " +
                        'creating an ApolloServerPluginInlineTrace plugin. See ' +
                        'https://go.apollo.dev/s/migration-engine-plugins');
                }
            }
            else if (this.config.engine !== false) {
                const options = {
                    __onlyIfSchemaIsFederated: true,
                };
                if (typeof engine === 'object') {
                    options.rewriteError = engine.rewriteError;
                }
                this.plugins.push(plugin_1.ApolloServerPluginInlineTrace(options));
            }
        }
    }
    initializeDocumentStore() {
        return new apollo_server_caching_1.InMemoryLRUCache({
            maxSize: Math.pow(2, 20) * (this.experimental_approximateDocumentStoreMiB || 30),
            sizeCalculator: approximateObjectSize,
        });
    }
    graphQLServerOptions(integrationContextArgument) {
        return __awaiter(this, void 0, void 0, function* () {
            const { schema, schemaHash, documentStore, extensions, } = yield this.ensureStarted();
            let context = this.context ? this.context : {};
            try {
                context =
                    typeof this.context === 'function'
                        ? yield this.context(integrationContextArgument || {})
                        : context;
            }
            catch (error) {
                context = () => {
                    throw error;
                };
            }
            return Object.assign({ schema,
                schemaHash, logger: this.logger, plugins: this.plugins, documentStore,
                extensions,
                context, persistedQueries: this.requestOptions
                    .persistedQueries, fieldResolver: this.requestOptions.fieldResolver, parseOptions: this.parseOptions }, this.requestOptions);
        });
    }
    executeOperation(request, integrationContextArgument) {
        return __awaiter(this, void 0, void 0, function* () {
            const options = yield this.graphQLServerOptions(integrationContextArgument);
            if (typeof options.context === 'function') {
                options.context = options.context();
            }
            else if (typeof options.context === 'object') {
                options.context = runHttpQuery_1.cloneObject(options.context);
            }
            const requestCtx = {
                logger: this.logger,
                schema: options.schema,
                schemaHash: options.schemaHash,
                request: Object.assign(Object.assign({}, request), { query: request.query && typeof request.query !== 'string'
                        ? graphql_1.print(request.query)
                        : request.query }),
                context: options.context || Object.create(null),
                cache: options.cache,
                metrics: {},
                response: {
                    http: {
                        headers: new apollo_server_env_1.Headers(),
                    },
                },
                debug: options.debug,
            };
            return requestPipeline_1.processGraphQLRequest(options, requestCtx);
        });
    }
}
exports.ApolloServerBase = ApolloServerBase;
function printNodeFileUploadsMessage(logger) {
    logger.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.',
        '',
        'Deployments which NEED file upload capabilities should update to',
        'Node.js >= v8.5.0 to continue using uploads.',
        '',
        'If this server DOES NOT NEED file uploads and wishes to continue',
        'using this version of Node.js, uploads can be disabled by adding:',
        '',
        '  uploads: false,',
        '',
        '...to the options for Apollo Server and re-deploying the server.',
        '',
        'For more information, see https://bit.ly/gql-upload-node-6.',
        '',
    ].join('\n'));
}
function warnAboutUploads(logger, implicit) {
    logger.error([
        'The third-party `graphql-upload` package is enabled in your server',
        (implicit
            ? 'because you use the `Upload` scalar in your schema.'
            : 'because you explicitly enabled it with the `uploads` option.'),
        'This package is vulnerable to Cross-Site Request Forgery (CSRF) attacks.',
        'We recommend you either disable uploads if it is not a necessary part of',
        'your server, or upgrade to Apollo Server 3.7 and enable CSRF prevention.',
        'See https://go.apollo.dev/s/graphql-upload-csrf for more details.',
    ].join('\n'));
}
//# sourceMappingURL=ApolloServer.js.map