/* eslint-disable no-console */ import { Application, Express } from 'express'; import { Pool, PoolConfig } from 'pg'; import { DbLogger, DbShutdownActions, EnvOwnerPgPool, LabelledPgPool, LoginPgPool, OwnerPgPool, } from '../common'; /** * Creates a PostgreSQL Pool for a given connection string, and returns it. * * @param connectionString * @param poolLabel * @param logger * @param shutdownActions * @param poolConfig * @returns */ export const createPgPool = ( connectionString: string, poolLabel: T, logger: DbLogger, shutdownActions: DbShutdownActions, poolConfig?: PoolConfig, ): LabelledPgPool => { const config: PoolConfig = { connectionString, ...poolConfig, }; const pgPool = new Pool(config) as LabelledPgPool; pgPool.label = poolLabel; // If a pg pool error happens and triggers the exit of node process (e.g postgres server stopped while postgraphile service is running) - // it is possible for it to not be caught using this handler, because node process will close faster. // But such errors can always be caught using 'uncaughtException' process event. pgPool.on('error', (err) => { logger.error( err, `Database error occurred on ${poolLabel} PostgreSQL connection Pool.`, ); }); shutdownActions.push(async () => { logger.debug(`Shutting down ${poolLabel} PostgreSQL connection Pool.`); await pgPool.end(); }); return pgPool; }; const ownerPoolKey = 'ownerPool'; const envOwnerPoolKey = 'envOwnerPool'; const loginPoolKey = 'loginPool'; /** * Returns the previously set up PostgreSQL pool for the database owner. The owner pool has full access to the database. * @param app express app */ export const getOwnerPgPool = (app: Express | Application): OwnerPgPool => { return app.get(ownerPoolKey); }; /** * Sets a given pool as the Owner Pool for an Express Application * @param app * @param ownerPool */ export const setOwnerPgPool = ( app: Application, ownerPool: OwnerPgPool, ): void => { app.set(ownerPoolKey, ownerPool); }; /** * Returns the previously set up PostgreSQL pool for the environment owner. The environment owner cannot bypass Row Level Security and database requests will only operate on specific tenant and environment rows. * @param app express app */ export const getEnvOwnerPgPool = ( app: Express | Application, ): EnvOwnerPgPool => { return app.get(envOwnerPoolKey); }; /** * Sets a given pool as the Environment Owner Pool for an Express Application * @param app * @param envOwnerPool */ export const setEnvOwnerPgPool = ( app: Application, envOwnerPool: EnvOwnerPgPool, ): void => { app.set(envOwnerPoolKey, envOwnerPool); }; /** * Returns the previously set up PostgreSQL pool for the login user. The login pool is internally used by postgraphile to access the database via GraphQL API endpoints. * @param app express app */ export const getLoginPgPool = (app: Express | Application): LoginPgPool => { return app.get(loginPoolKey); }; /** * Sets a given pool as the Login Pool for an Express Application * @param app * @param loginPool */ export const setLoginPgPool = ( app: Application, loginPool: LoginPgPool, ): void => { app.set(loginPoolKey, loginPool); }; /** * Sets up a PostgreSQL pool for the owner to be reused by other app components. The owner pool has full access to the database. * @param app express app * @param connectionString full database connection string * @param logger logger to log errors and shutdown action messages * @param shutdownActions shutdown actions middleware * @param poolConfig optional pool configuration object */ export const setupOwnerPgPool = ( app: Express | Application, connectionString: string, logger: DbLogger, shutdownActions: DbShutdownActions, poolConfig?: PoolConfig, ): OwnerPgPool => { const ownerPool = createOwnerPgPool( connectionString, logger, shutdownActions, poolConfig, ); setOwnerPgPool(app, ownerPool); return ownerPool; }; /** * Sets up a PostgreSQL pool for the environment owner to be reused by other app components. The environment owner cannot bypass Row Level Security and database requests will only operate on specific tenant and environment rows. * @param app express app * @param connectionString full database connection string * @param logger logger to log errors and shutdown action messages * @param shutdownActions shutdown actions middleware * @param poolConfig optional pool configuration object */ export const setupEnvOwnerPgPool = ( app: Express | Application, connectionString: string, logger: DbLogger, shutdownActions: DbShutdownActions, poolConfig?: PoolConfig, ): EnvOwnerPgPool => { const envOwnerPool = createEnvOwnerPgPool( connectionString, logger, shutdownActions, poolConfig, ); setEnvOwnerPgPool(app, envOwnerPool); return envOwnerPool; }; /** * Sets up a PostgreSQL pool for the login user to be reused by other app components. The login pool is internally used by postgraphile to access the database via GraphQL API endpoints. * @param app express app * @param connectionString full database connection string * @param logger logger to log errors and shutdown action messages * @param shutdownActions shutdown actions middleware * @param poolConfig optional pool configuration object */ export const setupLoginPgPool = ( app: Express | Application, connectionString: string, logger: DbLogger, shutdownActions: DbShutdownActions, poolConfig?: PoolConfig, ): LoginPgPool => { const loginPool = createLoginPgPool( connectionString, logger, shutdownActions, poolConfig, ); setLoginPgPool(app, loginPool); return loginPool; }; /** * Sets up a PostgreSQL pool for the owner to be reused by other app components. The owner pool has full access to the database. * @param connectionString full database connection string * @param logger logger to log errors and shutdown action messages * @param shutdownActions shutdown actions middleware * @param poolConfig optional pool configuration object */ export const createOwnerPgPool = ( connectionString: string, logger: DbLogger, shutdownActions: DbShutdownActions, poolConfig?: PoolConfig, ): OwnerPgPool => { return createPgPool( connectionString, 'Owner', logger, shutdownActions, poolConfig, ); }; /** * Sets up a PostgreSQL pool for the environment owner. The environment owner cannot bypass Row Level Security and database requests will only operate on specific tenant and environment rows. * @param connectionString full database connection string * @param logger logger to log errors and shutdown action messages * @param shutdownActions shutdown actions middleware * @param poolConfig optional pool configuration object */ export const createEnvOwnerPgPool = ( connectionString: string, logger: DbLogger, shutdownActions: DbShutdownActions, poolConfig?: PoolConfig, ): EnvOwnerPgPool => { return createPgPool( connectionString, 'EnvOwner', logger, shutdownActions, poolConfig, ); }; /** * Sets up a PostgreSQL pool for the login user. The login pool is internally used by postgraphile to access the database via GraphQL API endpoints. * @param connectionString full database connection string * @param logger logger to log errors and shutdown action messages * @param shutdownActions shutdown actions middleware * @param poolConfig optional pool configuration object */ export const createLoginPgPool = ( connectionString: string, logger: DbLogger, shutdownActions: DbShutdownActions, poolConfig?: PoolConfig, ): LoginPgPool => { return createPgPool( connectionString, 'Login', logger, shutdownActions, poolConfig, ); };