UNPKG

postgraphile

Version:

A GraphQL schema created by reflection over a PostgreSQL schema 🐘 (previously known as PostGraphQL)

337 lines 27.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getPostgraphileSchemaBuilder = void 0; const pg_1 = require("pg"); const graphql_1 = require("graphql"); 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"); const chalk_1 = require("chalk"); const withPostGraphileContext_1 = require("./withPostGraphileContext"); const shutdownActions_1 = require("./shutdownActions"); const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms)); // tslint:disable-next-line no-any function isPlainObject(obj) { if (!obj || typeof obj !== 'object' || String(obj) !== '[object Object]') return false; const proto = Object.getPrototypeOf(obj); if (proto === null || proto === Object.prototype) { return true; } return false; } /** * 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, shutdownActions = new shutdownActions_1.ShutdownActions()) { if (incomingOptions.live && incomingOptions.subscriptions == null) { // live implies subscriptions incomingOptions.subscriptions = true; } 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 = options['_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: () => (gqlSchema ? Promise.resolve(gqlSchema) : gqlSchemaPromise), options, }; async function createGqlSchema() { let attempts = 0; let isShuttingDown = false; shutdownActions.add(async () => { isShuttingDown = true; }); /* * This function should be called after every `await` in the try{} block * below so that if a shutdown occurs whilst we're awaiting something else * we immediately clean up. */ const assertAlive = () => { if (isShuttingDown) { throw Object.assign(new Error('PostGraphile is shutting down'), { isShutdownAction: true }); } }; // If we're in watch mode, cancel watch mode on shutdown let releaseWatchFnPromise = null; shutdownActions.add(async () => { if (releaseWatchFnPromise) { try { const releaseWatchFn = await releaseWatchFnPromise; await releaseWatchFn(); } catch (e) { // Ignore errors during shutdown. } } }); // If the server shuts down, make sure the schema has resolved or // rejected before signaling shutdown is complete. If it rejected, // don't propagate the error. let gqlSchemaPromise = null; shutdownActions.add(async () => { if (gqlSchemaPromise) { await gqlSchemaPromise.catch(() => null); } }); // eslint-disable-next-line no-constant-condition while (true) { assertAlive(); try { if (options.watchPg) { // We must register the value used by the shutdown action immediately to avoid a race condition. releaseWatchFnPromise = postgraphile_core_1.watchPostGraphileSchema(pgPool, pgSchemas, options, newSchema => { gqlSchema = newSchema; _emitter.emit('schemas:changed'); exportGqlSchema(gqlSchema); }); // Wait for the watch to be set up before progressing. await releaseWatchFnPromise; assertAlive(); if (!gqlSchema) { throw new Error("Consistency error: watchPostGraphileSchema promises to call the callback before the promise resolves; but this hasn't happened"); } } else { // We must register the value used by the shutdown action immediately to avoid a race condition. gqlSchemaPromise = postgraphile_core_1.createPostGraphileSchema(pgPool, pgSchemas, options); gqlSchema = await gqlSchemaPromise; assertAlive(); exportGqlSchema(gqlSchema); } if (attempts > 0) { // tslint:disable-next-line no-console console.error(`Schema ${attempts > 15 ? 'eventually' : attempts > 5 ? 'finally' : 'now'} generated successfully`); } return gqlSchema; } catch (error) { releaseWatchFnPromise = null; gqlSchemaPromise = null; attempts++; const delay = Math.min(100 * Math.pow(attempts, 2), 30000); if (error.isShutdownAction) { throw error; } else if (isShuttingDown) { console.error('An error occurred whilst building the schema. However, the server was shutting down, which might have caused it.'); console.error(error); throw error; } else if (typeof options.retryOnInitFail === 'function') { try { const start = process.hrtime(); const retry = await options.retryOnInitFail(error, attempts); const diff = process.hrtime(start); const dur = diff[0] * 1e3 + diff[1] * 1e-6; if (isShuttingDown) { throw error; } else if (!retry) { // Trigger a shutdown, and swallow any new errors so old error is still thrown await shutdownActions.invokeAll().catch(e => { console.error('An additional error occured whilst calling shutdownActions.invokeAll():'); console.error(e); }); throw error; } else { if (dur < 50) { // retryOnInitFail didn't wait long enough; use default wait. console.error(`Your retryOnInitFail function should include a delay before resolving; falling back to a ${delay}ms wait (attempts = ${attempts}) to avoid overwhelming the database.`); await sleep(delay); } } } catch (e) { throw Object.defineProperties(new graphql_1.GraphQLError('Failed to initialize GraphQL schema.', undefined, undefined, undefined, undefined, e), { status: { value: 503, enumerable: false, }, }); } } else { const exitOnFail = !options.retryOnInitFail; // If we fail to build our schema, log the error and either exit or retry shortly logSeriousError(error, 'building the initial schema' + (attempts > 1 ? ` (attempt ${attempts})` : ''), exitOnFail ? 'Exiting because `retryOnInitFail` is not set.' : `We'll try again in ${delay}ms.`); if (exitOnFail) { process.exit(34); } // Retry shortly await sleep(delay); } } } } async function exportGqlSchema(newGqlSchema) { try { await exportPostGraphileSchema_1.default(newGqlSchema, options); } catch (error) { // If we exit cleanly; let calling scripts know there was a problem. process.exitCode = 35; // If we fail to export our schema, log the error. logSeriousError(error, 'exporting the schema'); } } } exports.getPostgraphileSchemaBuilder = getPostgraphileSchemaBuilder; function postgraphile(poolOrConfig, schemaOrOptions, maybeOptions) { let schema; // These are the raw options we're passed in; getPostgraphileSchemaBuilder // must process them with `pluginHook` before we can rely on them. let incomingOptions; // 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 // `incomingOptions`. if (typeof schemaOrOptions === 'string' || Array.isArray(schemaOrOptions)) { schema = schemaOrOptions; incomingOptions = maybeOptions || {}; } // If the second argument is null or an object then use default `schema` // and set incomingOptions to second or third argument (or default). else if (typeof schemaOrOptions === 'object') { schema = 'public'; incomingOptions = schemaOrOptions || maybeOptions || {}; } // Otherwise if the second argument is present it's invalid: throw an error. else if (arguments.length > 1) { throw new Error('The second argument to postgraphile was invalid... did you mean to set a schema?'); } // No schema or options specified, use defaults. else { schema = 'public'; incomingOptions = {}; } if (typeof poolOrConfig === 'undefined' && arguments.length >= 1) { throw new Error('The first argument to postgraphile was `undefined`... did you mean to set pool options?'); } const shutdownActions = new shutdownActions_1.ShutdownActions(); // Do some things with `poolOrConfig` so that in the end, we actually get a // Postgres pool. const { pgPool, releasePgPool } = toPgPool(poolOrConfig); if (releasePgPool) { shutdownActions.add(releasePgPool); } pgPool.on('error', err => { /* * This handler is required so that client connection errors don't bring * the server down (via `unhandledError`). * * `pg` will automatically terminate the client and remove it from the * pool, so we don't actually need to take any action here, just ensure * that the event listener is registered. */ // tslint:disable-next-line no-console console.error('PostgreSQL client generated error: ', err.message); }); pgPool.on('connect', pgClient => { // Enhance our Postgres client with debugging stuffs. withPostGraphileContext_1.debugPgClient(pgClient, !!options.allowExplain); }); const { getGraphQLSchema, options, _emitter } = getPostgraphileSchemaBuilder(pgPool, schema, incomingOptions, shutdownActions); return createPostGraphileHttpRequestHandler_1.default({ ...(typeof poolOrConfig === 'string' ? { ownerConnectionString: poolOrConfig } : {}), ...options, getGqlSchema: getGraphQLSchema, pgPool, _emitter, shutdownActions, }); } exports.default = postgraphile; function logSeriousError(error, when, nextSteps) { // tslint:disable-next-line no-console console.error(`A ${chalk_1.default.bold('serious error')} occurred when ${chalk_1.default.bold(when)}. ${nextSteps ? nextSteps + ' ' : ''}Error details:\n\n${error.stack}\n`); } function hasPoolConstructor(obj) { return ( // tslint:disable-next-line no-any (obj && typeof obj.constructor === 'function' && obj.constructor === pg_1.Pool.super_) || false); } function constructorName(obj) { return ((obj && typeof obj.constructor === 'function' && obj.constructor.name && String(obj.constructor.name)) || null); } // tslint:disable-next-line no-any function toPgPool(poolOrConfig) { if (quacksLikePgPool(poolOrConfig)) { // If it is already a `Pool`, just use it. return { pgPool: poolOrConfig, releasePgPool: null }; } if (typeof poolOrConfig === 'string') { // If it is a string, let us parse it to get a config to create a `Pool`. const pgPool = new pg_1.Pool({ connectionString: poolOrConfig }); return { pgPool, releasePgPool: () => pgPool.end() }; } else if (!poolOrConfig) { // Use an empty config and let the defaults take over. const pgPool = new pg_1.Pool({}); return { pgPool, releasePgPool: () => pgPool.end() }; } else if (isPlainObject(poolOrConfig)) { // The user handed over a configuration object, pass it through const pgPool = new pg_1.Pool(poolOrConfig); return { pgPool, releasePgPool: () => pgPool.end() }; } else { throw new Error('Invalid connection string / Pool '); } } // tslint:disable-next-line no-any function quacksLikePgPool(pgConfig) { if (pgConfig instanceof pg_1.Pool) return true; if (hasPoolConstructor(pgConfig)) return true; // 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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicG9zdGdyYXBoaWxlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3Bvc3RncmFwaGlsZS9wb3N0Z3JhcGhpbGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsMkJBQXNDO0FBRXRDLHFDQUFzRDtBQUN0RCxtQ0FBc0M7QUFDdEMseURBQXNGO0FBQ3RGLHNHQUErRjtBQUMvRixnRkFBeUU7QUFDekUsNkNBQXFEO0FBRXJELGlDQUEwQjtBQUMxQix1RUFBMEQ7QUFDMUQsdURBQW9EO0FBRXBELE1BQU0sS0FBSyxHQUFHLENBQUMsRUFBVSxFQUFFLEVBQUUsQ0FBQyxJQUFJLE9BQU8sQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDLFVBQVUsQ0FBQyxPQUFPLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQztBQUU5RSxrQ0FBa0M7QUFDbEMsU0FBUyxhQUFhLENBQUMsR0FBUTtJQUM3QixJQUFJLENBQUMsR0FBRyxJQUFJLE9BQU8sR0FBRyxLQUFLLFFBQVEsSUFBSSxNQUFNLENBQUMsR0FBRyxDQUFDLEtBQUssaUJBQWlCO1FBQUUsT0FBTyxLQUFLLENBQUM7SUFDdkYsTUFBTSxLQUFLLEdBQUcsTUFBTSxDQUFDLGNBQWMsQ0FBQyxHQUFHLENBQUMsQ0FBQztJQUN6QyxJQUFJLEtBQUssS0FBSyxJQUFJLElBQUksS0FBSyxLQUFLLE1BQU0sQ0FBQyxTQUFTLEVBQUU7UUFDaEQsT0FBTyxJQUFJLENBQUM7S0FDYjtJQUNELE9BQU8sS0FBSyxDQUFDO0FBQ2YsQ0FBQztBQVdEOzs7O0dBSUc7QUFDSCxTQUFnQiw0QkFBNEIsQ0FJMUMsTUFBWSxFQUNaLE1BQThCLEVBQzlCLGVBQXVELEVBQ3ZELGtCQUFtQyxJQUFJLGlDQUFlLEVBQUU7SUFFeEQsSUFBSSxlQUFlLENBQUMsSUFBSSxJQUFJLGVBQWUsQ0FBQyxhQUFhLElBQUksSUFBSSxFQUFFO1FBQ2pFLDZCQUE2QjtRQUM3QixlQUFlLENBQUMsYUFBYSxHQUFHLElBQUksQ0FB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