"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const pg_1 = require("pg");
const pg_connection_string_1 = require("pg-connection-string");
const events_1 = require("events");
const postgraphile_core_1 = require("postgraphile-core");
const createPostGraphileHttpRequestHandler_1 = require("./http/createPostGraphileHttpRequestHandler");
const exportPostGraphileSchema_1 = require("./schema/exportPostGraphileSchema");
const pluginHook_1 = require("./pluginHook");
/**
 * Creates a PostGraphile Http request handler by first introspecting the
 * database to get a GraphQL schema, and then using that to create the Http
 * request handler.
 */
function getPostgraphileSchemaBuilder(pgPool, schema, incomingOptions) {
    const pluginHook = pluginHook_1.pluginHookFromOptions(incomingOptions);
    const options = pluginHook('postgraphile:options', incomingOptions, {
        pgPool,
        schema,
    });
    // Check for a jwtSecret without a jwtPgTypeIdentifier
    // a secret without a token identifier prevents JWT creation
    if (options.jwtSecret && !options.jwtPgTypeIdentifier) {
        // tslint:disable-next-line no-console
        console.warn('WARNING: jwtSecret provided, however jwtPgTypeIdentifier (token identifier) not provided.');
    }
    if (options.handleErrors && (options.extendedErrors || options.showErrorStack)) {
        throw new Error(`You cannot combine 'handleErrors' with the other error options`);
    }
    // Creates the Postgres schemas array.
    const pgSchemas = Array.isArray(schema) ? schema : [schema];
    const _emitter = new events_1.EventEmitter();
    // Creates a promise which will resolve to a GraphQL schema. Connects a
    // client from our pool to introspect the database.
    //
    // This is not a constant because when we are in watch mode, we want to swap
    // out the `gqlSchema`.
    let gqlSchema;
    const gqlSchemaPromise = createGqlSchema();
    return {
        _emitter,
        getGraphQLSchema: () => Promise.resolve(gqlSchema || gqlSchemaPromise),
    };
    async function createGqlSchema() {
        try {
            if (options.watchPg) {
                await postgraphile_core_1.watchPostGraphileSchema(pgPool, pgSchemas, options, newSchema => {
                    gqlSchema = newSchema;
                    _emitter.emit('schemas:changed');
                    exportGqlSchema(gqlSchema);
                });
                if (!gqlSchema) {
                    throw new Error("Consistency error: watchPostGraphileSchema promises to call the callback before the promise resolves; but this hasn't happened");
                }
            }
            else {
                gqlSchema = await postgraphile_core_1.createPostGraphileSchema(pgPool, pgSchemas, options);
                exportGqlSchema(gqlSchema);
            }
            return gqlSchema;
        }
        catch (error) {
            // If we fail to build our schema, log the error and exit the process.
            return handleFatalError(error);
        }
    }
    async function exportGqlSchema(newGqlSchema) {
        try {
            await exportPostGraphileSchema_1.default(newGqlSchema, options);
        }
        catch (error) {
            // If we fail to export our schema, log the error and exit the process.
            handleFatalError(error);
        }
    }
}
exports.getPostgraphileSchemaBuilder = getPostgraphileSchemaBuilder;
function postgraphile(poolOrConfig, schemaOrOptions, maybeOptions) {
    let schema;
    let options;
    // If the second argument is undefined, use defaults for both `schema` and
    // `options`.
    if (typeof schemaOrOptions === 'undefined') {
        schema = 'public';
        options = {};
    }
    // If the second argument is a string or array, it is the schemas so set the
    // `schema` value and try to use the third argument (or a default) for
    // `options`.
    else if (typeof schemaOrOptions === 'string' || Array.isArray(schemaOrOptions)) {
        schema = schemaOrOptions;
        options = maybeOptions || {};
    }
    // Otherwise the second argument is the options so set `schema` to the
    // default and `options` to the second argument.
    else {
        schema = 'public';
        options = schemaOrOptions;
    }
    // Do some things with `poolOrConfig` so that in the end, we actually get a
    // Postgres pool.
    const pgPool = 
    // If it is already a `Pool`, just use it.
    poolOrConfig instanceof pg_1.Pool || quacksLikePgPool(poolOrConfig)
        ? poolOrConfig
        : new pg_1.Pool(typeof poolOrConfig === 'string'
            ? // Otherwise if it is a string, let us parse it to get a config to
                // create a `Pool`.
                pg_connection_string_1.parse(poolOrConfig)
            : // Finally, it must just be a config itself. If it is undefined, we
                // will just use an empty config and let the defaults take over.
                poolOrConfig || {});
    const { getGraphQLSchema, _emitter } = getPostgraphileSchemaBuilder(pgPool, schema, options);
    return createPostGraphileHttpRequestHandler_1.default(Object.assign({}, options, { getGqlSchema: getGraphQLSchema, pgPool,
        _emitter }));
}
exports.default = postgraphile;
function handleFatalError(error) {
    process.stderr.write(`${error.stack}\n`); // console.error fails under the tests
    process.exit(1);
    // `process.exit` will mean all code below it will never get called.
    // However, we need to return a value with type `never` here for
    // TypeScript.
    return null;
}
function constructorName(obj) {
    return ((obj &&
        typeof obj.constructor === 'function' &&
        obj.constructor.name &&
        String(obj.constructor.name)) ||
        null);
}
// tslint:disable-next-line no-any
function quacksLikePgPool(pgConfig) {
    // A diagnosis of exclusion
    if (!pgConfig || typeof pgConfig !== 'object')
        return false;
    if (constructorName(pgConfig) !== 'Pool' && constructorName(pgConfig) !== 'BoundPool')
        return false;
    if (!pgConfig['Client'])
        return false;
    if (!pgConfig['options'])
        return false;
    if (typeof pgConfig['connect'] !== 'function')
        return false;
    if (typeof pgConfig['end'] !== 'function')
        return false;
    if (typeof pgConfig['query'] !== 'function')
        return false;
    return true;
}
//# sourceMappingURL=data:application/json;base64, 