From 5d1c8e3d09a77ef5fa84c0796aa4f2bf71cd0067 Mon Sep 17 00:00:00 2001 From: Dan Caddigan Date: Fri, 28 May 2021 16:01:30 -0400 Subject: [PATCH] feat(connection): bring your own DB connection --- README.md | 19 ++++++++++--------- src/core/server.test.ts | 20 ++++++++++++++++++-- src/core/server.ts | 28 +++++++++++++++++----------- 3 files changed, 45 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 6c7d5eb4..618e7b22 100644 --- a/README.md +++ b/README.md @@ -287,15 +287,16 @@ When you start your server, there will be a new `generated` folder that Warthog Most of the config in Warthog is done via environment variables (see `Config - Environment Variables` below). However, more complex/dynamic objects should be passed via the server config. -| attribute | description | default | -| ------------------------- | --------------------------------------------------------------------------------------------------------- | --------------------------------------------- | -| container | TypeDI container. Warthog uses dependency injection under the hood. | empty container | -| authChecker | An instance of an [AuthChecker](https://typegraphql.ml/docs/authorization.html) to secure your resolvers. | | -| context | Context getter of form `(request: Request) => Promise` | empty | -| logger | Logger | [debug](https://github.com/visionmedia/debug) | -| middlewares | [TypeGraphQL](https://typegraphql.ml/docs/middlewares.html) middlewares to add to your server | none | -| onBeforeGraphQLMiddleware | Callback executed just before the Graphql server is started. The Express app is passed. | none | -| onAfterGraphQLMiddleware | Callback executed just after the Graphql server is started. The Express app is passed. | none | +| attribute | description | default | +| ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------- | +| container | TypeDI container. Warthog uses dependency injection under the hood. | empty container | +| authChecker | An instance of an [AuthChecker](https://typegraphql.ml/docs/authorization.html) to secure your resolvers. | | +| connectionGetter | Connection getter of form `(config: Config) => Connection or Promise`, asks for getter instead of raw connection in case we build in connection retries should a connection go down | none | +| context | Context getter of form `(request: Request) => Promise` | empty | +| logger | Logger | [debug](https://github.com/visionmedia/debug) | +| middlewares | [TypeGraphQL](https://typegraphql.ml/docs/middlewares.html) middlewares to add to your server | none | +| onBeforeGraphQLMiddleware | Callback executed just before the Graphql server is started. The Express app is passed. | none | +| onAfterGraphQLMiddleware | Callback executed just after the Graphql server is started. The Express app is passed. | none | ## Config - Environment Variables diff --git a/src/core/server.test.ts b/src/core/server.test.ts index c50c449a..351ccf28 100644 --- a/src/core/server.test.ts +++ b/src/core/server.test.ts @@ -1,8 +1,9 @@ -import { Server, ServerOptions } from './server'; +import { setTestServerEnvironmentVariables } from '../test/server-vars'; import { getTestServer } from '../test/test-server'; +import { createDBConnection } from '../torm'; +import { Server, ServerOptions } from './server'; import express = require('express'); -import { setTestServerEnvironmentVariables } from '../test/server-vars'; describe('Server', () => { let server: Server; @@ -44,6 +45,21 @@ describe('Server', () => { expect(appListenSpy).toHaveBeenCalledTimes(1); expect(hasGraphQlRoute(server.expressApp._router)).toBeTruthy(); }); + + test('start a server with a custom DB connection', async () => { + const testConnectionName = 'custom-test-connection'; + const connection = await createDBConnection({ name: testConnectionName }); + + const connectionGetter = () => { + return Promise.resolve(connection); + }; + + server = buildServer({ connectionGetter }); + await server.start(); + + expect(server.getConnection().name).toEqual(testConnectionName); + expect(server.getConnection()).toEqual(connection); + }); }); function buildServer(options: ServerOptions): Server { diff --git a/src/core/server.ts b/src/core/server.ts index 3e91fd88..f66211cb 100644 --- a/src/core/server.ts +++ b/src/core/server.ts @@ -1,31 +1,28 @@ // TODO-MVP: Add custom scalars such as graphql-iso-date // import { GraphQLDate, GraphQLDateTime, GraphQLTime } from 'graphql-iso-date'; -import { ApolloServer, OptionsJson, ApolloServerExpressConfig } from 'apollo-server-express'; -import { PubSubEngine, PubSubOptions } from 'graphql-subscriptions'; +import { ApolloServer, ApolloServerExpressConfig, OptionsJson } from 'apollo-server-express'; +import * as Debug from 'debug'; import { Request } from 'express'; -import express = require('express'); import { GraphQLID, GraphQLSchema } from 'graphql'; import { Binding } from 'graphql-binding'; import { DateResolver } from 'graphql-scalars'; +import { PubSubEngine, PubSubOptions } from 'graphql-subscriptions'; import { Server as HttpServer } from 'http'; import { Server as HttpsServer } from 'https'; -const open = require('open'); // eslint-disable-line @typescript-eslint/no-var-requires import { AuthChecker, buildSchema } from 'type-graphql'; // formatArgumentValidationError import { Container } from 'typedi'; import { Connection, ConnectionOptions, useContainer as TypeORMUseContainer } from 'typeorm'; - import { logger, Logger } from '../core/logger'; import { getRemoteBinding } from '../gql'; import { DataLoaderMiddleware, healthCheckMiddleware } from '../middleware'; import { createDBConnection } from '../torm'; - import { CodeGenerator } from './code-generator'; import { Config } from './config'; - import { BaseContext } from './Context'; -import * as Debug from 'debug'; +import express = require('express'); +const open = require('open'); // eslint-disable-line @typescript-eslint/no-var-requires const debug = Debug('warthog:server'); @@ -34,6 +31,7 @@ export interface ServerOptions { apolloConfig?: ApolloServerExpressConfig; authChecker?: AuthChecker; autoGenerateFiles?: boolean; + connectionGetter?: (config: Config) => Connection | Promise; context?: (request: Request) => object; expressApp?: express.Application; host?: string; @@ -124,10 +122,14 @@ export class Server { } async establishDBConnection(): Promise { - if (!this.connection) { - debug('establishDBConnection:start'); + if (this.connection) { + return this.connection; + } + + if (this.appOptions.connectionGetter) { + this.connection = await this.appOptions.connectionGetter(this.config); + } else { this.connection = await createDBConnection(this.dbOptions); - debug('establishDBConnection:end'); } return this.connection; @@ -143,6 +145,10 @@ export class Server { return `${this.getServerUrl()}/graphql`; } + getConnection(): Connection { + return this.connection; + } + async getBinding(options: { origin?: string; token?: string } = {}): Promise { let binding; try {