Skip to content

Commit

Permalink
Make adapter in config the concrete adapter instance, not import path
Browse files Browse the repository at this point in the history
  • Loading branch information
hannesj committed Mar 27, 2024
1 parent 03c9969 commit c16e519
Show file tree
Hide file tree
Showing 11 changed files with 38 additions and 92 deletions.
4 changes: 2 additions & 2 deletions grafast/dataplan-pg/src/adaptors/pg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,7 @@ export function createWithPgClient(
}

// This is here as a TypeScript assertion, to ensure we conform to PgAdaptor
const _testValidAdaptor: PgAdaptor<"@dataplan/pg/adaptors/pg">["createWithPgClient"] =
const _testValidAdaptor: PgAdaptor<PgAdaptorOptions>["createWithPgClient"] =
createWithPgClient;

const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
Expand Down Expand Up @@ -766,7 +766,7 @@ export function makePgService(
pgSettings,
pgSettingsForIntrospection,
pgSubscriber,
adaptor: "@dataplan/pg/adaptors/pg",
adaptor: { createWithPgClient, makePgService },
adaptorSettings: {
pool,
superuserConnectionString,
Expand Down
14 changes: 3 additions & 11 deletions grafast/dataplan-pg/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -427,15 +427,12 @@ export { version } from "./version.js";

declare global {
namespace GraphileConfig {
interface PgServiceConfiguration<
TAdaptor extends
keyof GraphileConfig.PgDatabaseAdaptorOptions = keyof GraphileConfig.PgDatabaseAdaptorOptions,
> {
interface PgServiceConfiguration<TAdaptorOptions = PgAdaptorOptions> {
name: string;
schemas?: string[];

adaptor: TAdaptor;
adaptorSettings?: GraphileConfig.PgDatabaseAdaptorOptions[TAdaptor];
adaptor: PgAdaptor<TAdaptorOptions>;
adaptorSettings?: TAdaptorOptions;

/** The key on 'context' where the withPgClient function will be sourced */
withPgClientKey: KeysOfType<Grafast.Context & object, WithPgClient>;
Expand Down Expand Up @@ -473,11 +470,6 @@ declare global {
interface Preset {
pgServices?: ReadonlyArray<PgServiceConfiguration>;
}

interface PgDatabaseAdaptorOptions {
"@dataplan/pg/adaptors/pg": PgAdaptorOptions;
/* Add your own via declaration merging */
}
}
namespace DataplanPg {
interface PgConditionStepExtensions {}
Expand Down
66 changes: 10 additions & 56 deletions grafast/dataplan-pg/src/pgServices.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
import { pathToFileURL } from "node:url";
import type * as pg from "pg";

import type { PgClient, WithPgClient } from "./executor.ts";
import type { MakePgServiceOptions } from "./interfaces";

type PromiseOrDirect<T> = T | PromiseLike<T>;

/** @experimental */
export interface PgAdaptor<
TAdaptor extends
keyof GraphileConfig.PgDatabaseAdaptorOptions = keyof GraphileConfig.PgDatabaseAdaptorOptions,
> {
export interface PgAdaptor<TAdaptorSettings> {
createWithPgClient: (
adaptorSettings: GraphileConfig.PgServiceConfiguration<TAdaptor>["adaptorSettings"],
adaptorSettings: TAdaptorSettings | undefined,
variant?: "SUPERUSER" | null,
) => PromiseOrDirect<WithPgClient>;
makePgService: (
options: MakePgServiceOptions & { pool?: pg.Pool },
) => GraphileConfig.PgServiceConfiguration;
}

/**
Expand All @@ -32,55 +33,10 @@ interface PgClientBySourceCacheValue {
}

const withPgClientDetailsByConfigCache = new Map<
GraphileConfig.PgServiceConfiguration,
GraphileConfig.PgServiceConfiguration<any>,
PromiseOrDirect<PgClientBySourceCacheValue>
>();

function reallyLoadAdaptor<
TAdaptor extends
keyof GraphileConfig.PgDatabaseAdaptorOptions = keyof GraphileConfig.PgDatabaseAdaptorOptions,
>(adaptorString: TAdaptor): PromiseOrDirect<PgAdaptor<TAdaptor>> {
try {
const adaptor = require(adaptorString);
return adaptor?.createWithPgClient ? adaptor : adaptor?.default;
} catch (e) {
if (e.code === "ERR_REQUIRE_ESM") {
const importSpecifier = adaptorString.match(/^([a-z]:|\.\/|\/)/i)
? pathToFileURL(adaptorString).href
: adaptorString;
const adaptorPromise = import(importSpecifier);
return adaptorPromise.then((adaptor) =>
adaptor?.createWithPgClient ? adaptor : adaptor?.default,
);
} else {
throw e;
}
}
}

const loadAdaptorCache = new Map<string, PromiseOrDirect<PgAdaptor<any>>>();
function loadAdaptor<
TAdaptor extends
keyof GraphileConfig.PgDatabaseAdaptorOptions = keyof GraphileConfig.PgDatabaseAdaptorOptions,
>(adaptorString: TAdaptor): PromiseOrDirect<PgAdaptor<TAdaptor>> {
const cached = loadAdaptorCache.get(adaptorString);
if (cached) {
return cached;
} else {
const result = reallyLoadAdaptor(adaptorString);
loadAdaptorCache.set(adaptorString, result);
if (isPromiseLike(result)) {
result.then(
(resolved) => {
loadAdaptorCache.set(adaptorString, resolved);
},
() => {},
);
}
return result;
}
}

/**
* Get or build the 'withPgClient' callback function for a given database
* config, caching it to make future lookups faster.
Expand All @@ -101,8 +57,7 @@ export function getWithPgClientFromPgService(
}
} else {
const promise = (async () => {
const adaptor = await loadAdaptor(config.adaptor);
const factory = adaptor?.createWithPgClient;
const factory = config.adaptor?.createWithPgClient;
if (typeof factory !== "function") {
throw new Error(
`'${config.adaptor}' does not look like a withPgClient adaptor - please ensure it exports a method called 'createWithPgClient'`,
Expand Down Expand Up @@ -170,8 +125,7 @@ export async function withSuperuserPgClientFromPgService<T>(
pgSettings: { [key: string]: string } | null,
callback: (client: PgClient) => T | Promise<T>,
): Promise<T> {
const adaptor = await loadAdaptor(config.adaptor);
const withPgClient = await adaptor.createWithPgClient(
const withPgClient = await config.adaptor.createWithPgClient(
config.adaptorSettings,
"SUPERUSER",
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { pathToFileURL } from "node:url";
import { inspect } from "node:util";

import { getWithPgClientFromPgService } from "@dataplan/pg";
import { createWithPgClient, makePgService } from "@dataplan/pg/adaptors/pg";
import { envelop, useExtendContext, useSchema } from "@envelop/core";
import { useParserCache } from "@envelop/parser-cache";
import { useValidationCache } from "@envelop/validation-cache";
Expand Down Expand Up @@ -61,7 +62,7 @@ pool.on("error", (e) => {
schemas: ["a", "b", "c"],
pgSettingsKey: "pgSettings",
withPgClientKey: "withPgClient",
adaptor: "@dataplan/pg/adaptors/pg",
adaptor: { createWithPgClient, makePgService },
adaptorSettings: {
pool,
},
Expand Down
3 changes: 2 additions & 1 deletion graphile-build/graphile-build-pg/src/examples/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import "graphile-config";

import { getWithPgClientFromPgService } from "@dataplan/pg";
import { createWithPgClient, makePgService } from "@dataplan/pg/adaptors/pg";
import {
defaultPreset as graphileBuildPreset,
QueryQueryPlugin,
Expand Down Expand Up @@ -65,7 +66,7 @@ export async function makeSharedPresetAndClient(pool: Pool) {
schemas: DATABASE_SCHEMAS,
pgSettingsKey: "pgSettings",
withPgClientKey: "withPgClient",
adaptor: "@dataplan/pg/adaptors/pg",
adaptor: { createWithPgClient, makePgService },
adaptorSettings: {
pool,
},
Expand Down
3 changes: 2 additions & 1 deletion graphile-build/graphile-build-pg/src/examples/webpack.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* eslint-disable no-restricted-syntax */

import { createWithPgClient, makePgService } from "@dataplan/pg/adaptors/pg";
import {
buildInflection,
buildSchema,
Expand Down Expand Up @@ -44,7 +45,7 @@ async function main() {
schemas: ["public"],
pgSettingsKey: "pgSettings",
withPgClientKey: "withPgClient",
adaptor: "@dataplan/pg/adaptors/pg",
adaptor: { createWithPgClient, makePgService },
adaptorSettings: {
pool,
},
Expand Down
7 changes: 4 additions & 3 deletions postgraphile/postgraphile/__tests__/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// It's helpful to see the full error stack

Error.stackTraceLimit = Infinity;

if (process.env.DEBUG) {
Expand All @@ -15,7 +16,7 @@ import type {
PgClientResult,
WithPgClient,
} from "@dataplan/pg";
import { PgSubscriber } from "@dataplan/pg/adaptors/pg";
import { createWithPgClient, PgSubscriber } from "@dataplan/pg/adaptors/pg";
import { promises as fsp } from "fs";
import {
$$bypassGraphQL,
Expand Down Expand Up @@ -237,7 +238,7 @@ export async function runTestQuery(
plugins: [StreamDeferPlugin],
pgServices: [
{
adaptor: "@dataplan/pg/adaptors/pg",
adaptor: { createWithPgClient },
name: "main",
withPgClientKey: "withPgClient",
pgSettingsKey: "pgSettings",
Expand All @@ -263,7 +264,7 @@ export async function runTestQuery(
adaptorSettings: {
connectionString,
},
} as GraphileConfig.PgServiceConfiguration<"@dataplan/pg/adaptors/pg">,
} as GraphileConfig.PgServiceConfiguration,
],
schema: {
pgForbidSetofFunctionsToReturnNull:
Expand Down
5 changes: 3 additions & 2 deletions postgraphile/postgraphile/__tests__/schema/v4/core.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { createWithPgClient } from "@dataplan/pg/adaptors/pg";
import { writeFile } from "fs/promises";
import type { PromiseOrDirect } from "grafast";
import type { GraphQLSchema } from "grafast/graphql";
Expand Down Expand Up @@ -46,7 +47,7 @@ export const test =
plugins: [StripOidsPlugin],
pgServices: [
{
adaptor: "@dataplan/pg/adaptors/pg",
adaptor: { createWithPgClient },
name: "main",
withPgClientKey: "withPgClient",
pgSettingsKey: "pgSettings",
Expand All @@ -67,7 +68,7 @@ export const test =
adaptorSettings: {
poolClient: client,
},
} as any, //GraphileConfig.PgServiceConfiguration<"@dataplan/pg/adaptors/pg">,
} as GraphileConfig.PgServiceConfiguration,
],
schema: {
...graphileBuildOptions,
Expand Down
5 changes: 3 additions & 2 deletions postgraphile/postgraphile/__tests__/schema/v5/core.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { createWithPgClient } from "@dataplan/pg/adaptors/pg";
import { writeFile } from "fs/promises";
import type { PromiseOrDirect } from "grafast";
import type { GraphQLSchema } from "grafast/graphql";
Expand Down Expand Up @@ -42,7 +43,7 @@ export const test =
plugins: [StripOidsPlugin],
pgServices: [
{
adaptor: "@dataplan/pg/adaptors/pg",
adaptor: { createWithPgClient },
name: "main",
withPgClientKey: "withPgClient",
pgSettingsKey: "pgSettings",
Expand All @@ -52,7 +53,7 @@ export const test =
adaptorSettings: {
poolClient: client,
},
} as any, //GraphileConfig.PgServiceConfiguration<"@dataplan/pg/adaptors/pg">,
} as GraphileConfig.PgServiceConfiguration,
],
schema: {
...graphileBuildOptions,
Expand Down
13 changes: 3 additions & 10 deletions postgraphile/postgraphile/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { createServer } from "node:http";
import { pathToFileURL } from "node:url";
import { inspect } from "node:util";

import type { MakePgServiceOptions } from "@dataplan/pg";
import { grafserv } from "grafserv/node";
import { resolvePresets } from "graphile-config";
import type { ArgsFromOptions, Argv } from "graphile-config/cli";
Expand Down Expand Up @@ -186,16 +185,10 @@ export async function run(args: ArgsFromOptions<typeof options>) {
}
const schemas = rawSchema?.split(",") ?? ["public"];
const adaptor =
preset.pgServices?.[0]?.adaptor ?? "@dataplan/pg/adaptors/pg";
preset.pgServices?.[0]?.adaptor ??
(await import("@dataplan/pg/adaptors/pg"));

const importSpecifier = adaptor.match(/^([a-z]:|\.\/|\/)/i)
? pathToFileURL(adaptor).href
: adaptor;

const mod = await import(importSpecifier);
const makePgService = (mod.makePgService ?? mod.default?.makePgService) as (
options: MakePgServiceOptions,
) => GraphileConfig.PgServiceConfiguration;
const makePgService = adaptor.makePgService;
if (typeof makePgService !== "function") {
throw new Error(
`Loaded adaptor '${adaptor}' but it does not export a 'makePgService' helper`,
Expand Down
7 changes: 4 additions & 3 deletions postgraphile/website/postgraphile/config.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -309,8 +309,8 @@ nitty-gritty: each entry in the list is an object with the following keys (only
- `name: string` - an arbitrary unique name for this config; please keep it
alphanumeric! Don't set this unless you have more than one pgService; see
warning below.
- `adaptor: string` - the name of the module to use as the postgres adaptor;
e.g. `@dataplan/pg/adaptors/pg` for the `pg` module
- `adaptor: PgAdaptor` - the module to use as the postgres adaptor; e.g. the
result of `import(@dataplan/pg/adaptors/pg)` for the `pg` module
- `adaptorSettings` - options to pass to the adaptor, these are different for
each adaptor (see [`adaptorSettings`](#adaptorsettings) below)
- `schemas: string[]` - an array of PostgreSQL schema names to use
Expand Down Expand Up @@ -338,14 +338,15 @@ nitty-gritty: each entry in the list is an object with the following keys (only

```js title="Example manual configuration"
import * as pg from "pg";
import * as PgAdaptor from "@dataplan/pg/adaptors/pg";

const pgServices = [
{
name: "main",
schemas: ["app_public"],
pgSettingsKey: "pgSettings",
withPgClientKey: "withPgClient",
adaptor: "@dataplan/pg/adaptors/pg",
adaptor: PgAdaptor,
adaptorSettings: {
pool: new pg.Pool({ connectionString: process.env.DATABASE_URL }),
// superuserConnectionString: process.env.SUPERUSER_DATABASE_URL,
Expand Down

0 comments on commit c16e519

Please sign in to comment.