From de83dd28c01b1c07471a735f7c2b94dd3f45ab0a Mon Sep 17 00:00:00 2001 From: Dotan Simha Date: Thu, 30 Jan 2025 12:40:54 +0200 Subject: [PATCH] improvement(cli): allow to pass a factory function to `cache` field (#568) Co-authored-by: Arda TANRIKULU --- .changeset/green-gifts-serve.md | 7 ++++++ .changeset/tame-crabs-act.md | 18 +++++++++++++ packages/gateway/src/cli.ts | 16 ++++++++++-- .../src/commands/handleLoggingOption.ts | 25 +++++++++++++++++++ packages/gateway/src/commands/proxy.ts | 12 +++++++-- packages/gateway/src/commands/subgraph.ts | 10 +++++++- packages/gateway/src/commands/supergraph.ts | 12 +++++++-- packages/gateway/src/config.ts | 16 +++++++++--- 8 files changed, 105 insertions(+), 11 deletions(-) create mode 100644 .changeset/green-gifts-serve.md create mode 100644 .changeset/tame-crabs-act.md create mode 100644 packages/gateway/src/commands/handleLoggingOption.ts diff --git a/.changeset/green-gifts-serve.md b/.changeset/green-gifts-serve.md new file mode 100644 index 000000000..43c66cec2 --- /dev/null +++ b/.changeset/green-gifts-serve.md @@ -0,0 +1,7 @@ +--- +'@graphql-hive/gateway': patch +--- + +Use the same logging instance across different components whenever possible + +For example if the log level is set in the configuration, change it immediately for the cache storages etc. diff --git a/.changeset/tame-crabs-act.md b/.changeset/tame-crabs-act.md new file mode 100644 index 000000000..a39937e76 --- /dev/null +++ b/.changeset/tame-crabs-act.md @@ -0,0 +1,18 @@ +--- +'@graphql-hive/gateway': minor +--- + +Improve `cache` configuration signature. + +The `cache` configuration key now allow you to pass a custom factory function to get the cache instance: + +```ts +import { defineConfig } from '@graphql-hive/gateway' + +export const gatewayConfig = defineConfig({ + // ... + cache: (ctx) => { + // Here you may create/retrieve your cache store instance, and return a KeyValueCache instance + } +}) +``` diff --git a/packages/gateway/src/cli.ts b/packages/gateway/src/cli.ts index c26509356..bb5f78a81 100644 --- a/packages/gateway/src/cli.ts +++ b/packages/gateway/src/cli.ts @@ -19,7 +19,12 @@ import type { JWTAuthPluginOptions } from '@graphql-mesh/plugin-jwt-auth'; import type { OpenTelemetryMeshPluginOptions } from '@graphql-mesh/plugin-opentelemetry'; import type { PrometheusPluginOptions } from '@graphql-mesh/plugin-prometheus'; import useMeshRateLimit from '@graphql-mesh/plugin-rate-limit'; -import type { KeyValueCache, Logger, YamlConfig } from '@graphql-mesh/types'; +import type { + KeyValueCache, + Logger, + MeshPubSub, + YamlConfig, +} from '@graphql-mesh/types'; import { DefaultLogger } from '@graphql-mesh/utils'; import parseDuration from 'parse-duration'; import { addCommands } from './commands/index'; @@ -93,6 +98,12 @@ export interface GatewayCLIProxyConfig proxy?: GatewayConfigProxy['proxy']; } +export type KeyValueCacheFactoryFn = (ctx: { + logger: Logger; + pubsub: MeshPubSub; + cwd: string; +}) => KeyValueCache; + export interface GatewayCLIBuiltinPluginConfig { /** * Configure JWT Auth @@ -129,6 +140,7 @@ export interface GatewayCLIBuiltinPluginConfig { jit?: boolean; cache?: | KeyValueCache + | KeyValueCacheFactoryFn | GatewayCLILocalforageCacheConfig | GatewayCLIRedisCacheConfig | GatewayCLICloudflareKVCacheConfig; @@ -326,7 +338,7 @@ let cli = new Command() export async function run(userCtx: Partial) { const ctx: CLIContext = { - log: new DefaultLogger(), + log: userCtx.log || new DefaultLogger(), productName: 'Hive Gateway', productDescription: 'Federated GraphQL Gateway', productPackageName: '@graphql-hive/gateway', diff --git a/packages/gateway/src/commands/handleLoggingOption.ts b/packages/gateway/src/commands/handleLoggingOption.ts new file mode 100644 index 000000000..89c032f34 --- /dev/null +++ b/packages/gateway/src/commands/handleLoggingOption.ts @@ -0,0 +1,25 @@ +import { Logger } from '@graphql-mesh/types'; +import { CLIContext, DefaultLogger, LogLevel } from '..'; + +export function handleLoggingConfig( + loggingConfig: boolean | Logger | LogLevel | undefined, + ctx: CLIContext, +) { + if (typeof loggingConfig === 'object') { + ctx.log = loggingConfig; + } else if (typeof loggingConfig === 'boolean') { + if (!loggingConfig) { + if (ctx.log instanceof DefaultLogger) { + ctx.log.logLevel = LogLevel.silent; + } else { + ctx.log = new DefaultLogger(ctx.log.name, LogLevel.silent); + } + } + } else if (typeof loggingConfig === 'number') { + if (ctx.log instanceof DefaultLogger) { + ctx.log.logLevel = loggingConfig; + } else { + ctx.log = new DefaultLogger(ctx.log.name, loggingConfig); + } + } +} diff --git a/packages/gateway/src/commands/proxy.ts b/packages/gateway/src/commands/proxy.ts index e70418873..cd58c5a6e 100644 --- a/packages/gateway/src/commands/proxy.ts +++ b/packages/gateway/src/commands/proxy.ts @@ -17,6 +17,7 @@ import { } from '../config'; import { startServerForRuntime } from '../servers/startServerForRuntime'; import { handleFork } from './handleFork'; +import { handleLoggingConfig } from './handleLoggingOption'; export const addCommand: AddCommand = (ctx, cli) => cli @@ -94,9 +95,14 @@ export const addCommand: AddCommand = (ctx, cli) => } const pubsub = loadedConfig.pubsub || new PubSub(); + const cwd = loadedConfig.cwd || process.cwd(); + if (loadedConfig.logging != null) { + handleLoggingConfig(loadedConfig.logging, ctx); + } const cache = await getCacheInstanceFromConfig(loadedConfig, { pubsub, logger: ctx.log, + cwd, }); const builtinPlugins = await getBuiltinPluginsFromConfig( { @@ -106,6 +112,8 @@ export const addCommand: AddCommand = (ctx, cli) => { logger: ctx.log, cache, + pubsub, + cwd, }, ); @@ -124,8 +132,8 @@ export const addCommand: AddCommand = (ctx, cli) => } : {}), proxy, - ...(schema ? { schema } : {}), - logging: loadedConfig.logging ?? ctx.log, + schema, + logging: ctx.log, productName: ctx.productName, productDescription: ctx.productDescription, productPackageName: ctx.productPackageName, diff --git a/packages/gateway/src/commands/subgraph.ts b/packages/gateway/src/commands/subgraph.ts index 659c535b8..77979f61b 100644 --- a/packages/gateway/src/commands/subgraph.ts +++ b/packages/gateway/src/commands/subgraph.ts @@ -21,6 +21,7 @@ import { } from '../config'; import { startServerForRuntime } from '../servers/startServerForRuntime'; import { handleFork } from './handleFork'; +import { handleLoggingConfig } from './handleLoggingOption'; export const addCommand: AddCommand = (ctx, cli) => cli @@ -55,9 +56,14 @@ export const addCommand: AddCommand = (ctx, cli) => } const pubsub = loadedConfig.pubsub || new PubSub(); + const cwd = loadedConfig.cwd || process.cwd(); + if (loadedConfig.logging != null) { + handleLoggingConfig(loadedConfig.logging, ctx); + } const cache = await getCacheInstanceFromConfig(loadedConfig, { pubsub, logger: ctx.log, + cwd, }); const builtinPlugins = await getBuiltinPluginsFromConfig( { @@ -67,6 +73,8 @@ export const addCommand: AddCommand = (ctx, cli) => { logger: ctx.log, cache, + pubsub, + cwd, }, ); @@ -90,7 +98,7 @@ export const addCommand: AddCommand = (ctx, cli) => productDescription: ctx.productDescription, productPackageName: ctx.productPackageName, productLink: ctx.productLink, - ...(ctx.productLogo ? { productLogo: ctx.productLogo } : {}), + productLogo: ctx.productLogo, pubsub, cache, plugins(ctx) { diff --git a/packages/gateway/src/commands/supergraph.ts b/packages/gateway/src/commands/supergraph.ts index 4c7012359..3b24ec2be 100644 --- a/packages/gateway/src/commands/supergraph.ts +++ b/packages/gateway/src/commands/supergraph.ts @@ -27,6 +27,7 @@ import { } from '../config'; import { startServerForRuntime } from '../servers/startServerForRuntime'; import { handleFork } from './handleFork'; +import { handleLoggingConfig } from './handleLoggingOption'; export const addCommand: AddCommand = (ctx, cli) => cli @@ -179,9 +180,14 @@ export const addCommand: AddCommand = (ctx, cli) => } const pubsub = loadedConfig.pubsub || new PubSub(); + const cwd = loadedConfig.cwd || process.cwd(); + if (loadedConfig.logging != null) { + handleLoggingConfig(loadedConfig.logging, ctx); + } const cache = await getCacheInstanceFromConfig(loadedConfig, { pubsub, logger: ctx.log, + cwd, }); const builtinPlugins = await getBuiltinPluginsFromConfig( { @@ -191,6 +197,8 @@ export const addCommand: AddCommand = (ctx, cli) => { logger: ctx.log, cache, + pubsub, + cwd, }, ); @@ -201,12 +209,12 @@ export const addCommand: AddCommand = (ctx, cli) => pollingInterval: opts.polling, ...registryConfig, supergraph, - logging: loadedConfig.logging ?? ctx.log, + logging: ctx.log, productName: ctx.productName, productDescription: ctx.productDescription, productPackageName: ctx.productPackageName, productLink: ctx.productLink, - ...(ctx.productLogo ? { productLogo: ctx.productLogo } : {}), + productLogo: ctx.productLogo, pubsub, cache, plugins(ctx) { diff --git a/packages/gateway/src/config.ts b/packages/gateway/src/config.ts index f14b33e63..8f6c2e5c7 100644 --- a/packages/gateway/src/config.ts +++ b/packages/gateway/src/config.ts @@ -3,10 +3,9 @@ import { isAbsolute, join } from 'node:path'; import { pathToFileURL } from 'node:url'; import type { GatewayConfig, - GatewayConfigContext, GatewayPlugin, } from '@graphql-hive/gateway-runtime'; -import type { KeyValueCache, Logger } from '@graphql-mesh/types'; +import type { KeyValueCache, Logger, MeshPubSub } from '@graphql-mesh/types'; import type { GatewayCLIBuiltinPluginConfig } from './cli'; import type { ServerConfig } from './servers/types'; @@ -99,7 +98,12 @@ export async function loadConfig< export async function getBuiltinPluginsFromConfig( config: GatewayCLIBuiltinPluginConfig, - ctx: { cache: KeyValueCache; logger: Logger }, + ctx: { + cache: KeyValueCache; + logger: Logger; + pubsub: MeshPubSub; + cwd: string; + }, ) { const plugins: GatewayPlugin[] = []; if (config.jwt) { @@ -146,8 +150,12 @@ export async function getBuiltinPluginsFromConfig( export async function getCacheInstanceFromConfig( config: GatewayCLIBuiltinPluginConfig, - ctx: Pick, + ctx: { logger: Logger; pubsub: MeshPubSub; cwd: string }, ): Promise { + if (typeof config.cache === 'function') { + return config.cache(ctx); + } + if (config.cache && 'type' in config.cache) { switch (config.cache.type) { case 'redis': {