Skip to content

feat: refactor ConstructivePreset into createConstructivePreset() factory with feature flag options#1092

Merged
pyramation merged 1 commit intomainfrom
feat/phase3-typed-settings-reads
May 10, 2026
Merged

feat: refactor ConstructivePreset into createConstructivePreset() factory with feature flag options#1092
pyramation merged 1 commit intomainfrom
feat/phase3-typed-settings-reads

Conversation

@pyramation
Copy link
Copy Markdown
Contributor

Summary

Converts the static ConstructivePreset object into a createConstructivePreset(options?) factory function. Each optional boolean flag in ConstructivePresetOptions maps 1-to-1 with the database_settings / api_settings tables (from constructive-db#1060), controlling whether its corresponding Graphile plugin preset is included in the generated config.

Key changes:

  • New ConstructivePresetOptions interface with 9 flags: enableAggregates, enablePostgis, enableSearch, enableDirectUploads, enablePresignedUploads, enableManyToMany, enableConnectionFilter, enableLtree, enableLlm
  • Factory conditionally builds the extends array, connectionFilterOperatorFactories, disablePlugins, and schema options based on which flags are enabled
  • Server wiring: buildPreset() now passes databaseSettings directly to createConstructivePreset() — all flags flow through, replacing the previous approach where only enableAggregates was handled manually
  • Backwards compat: export const ConstructivePreset = createConstructivePreset() preserves the static import for existing consumers (defaults match previous behavior: everything on except aggregates and LLM)

This completes the end-to-end feature flag wiring: database_settings row → server reads → createConstructivePreset(settings) → each plugin conditionally included/excluded.

Review & Testing Checklist for Human

  • Verify DatabaseSettings property names match ConstructivePresetOptions — the factory receives databaseSettings directly as options via spread ({ ...DEFAULTS, ...options }). If any property name differs (e.g., enableDirectUploads vs enable_direct_uploads), that flag silently falls back to the default. Check graphql/server/src/types.ts to confirm alignment.
  • Verify operatorFactories gating logic is correct — operator factories are only built when enableConnectionFilter is true AND the satellite feature (search/postgis/ltree) is also enabled. Previously these were always included. If connection filter is disabled, no operator factories or disablePlugins are set — confirm the app works correctly with PgConditionArgumentPlugin still active in that case.
  • Side-effect timing of factory helperscreateBucketNameResolver(), createEnsureBucketProvisioned(), getAllowedOrigins(), getPresignedUrlS3Config are now called per-createConstructivePreset() invocation (once per tenant) rather than once at module load. Verify these are safe to call multiple times / return fresh-but-equivalent results.
  • Deploy to a test environment and verify: (1) default preset works identically to before (no regressions), (2) setting enable_aggregates = true in database_settings actually exposes aggregates in the GraphQL schema, (3) setting a feature to false actually removes its types/fields from the schema.

Notes

  • No unit tests for the factory function yet — tracked in constructive-planning#823.
  • enableLlm is reserved/no-op (no plugin exists yet).
  • operatorFactories is typed as unknown[] — matches the existing pattern but is worth tightening in a follow-up.

Link to Devin session: https://app.devin.ai/sessions/94a2728a9c414500bead29cbbc829c15
Requested by: @pyramation

…tory

Convert static ConstructivePreset into a factory function that accepts
ConstructivePresetOptions (mirrors database_settings/api_settings flags).

Each feature flag controls whether its corresponding plugin preset is
included in the generated Graphile config:

  enableConnectionFilter  -> ConnectionFilterPreset, EnableAllFilterColumnsPreset
  enableManyToMany        -> ManyToManyOptInPreset
  enableSearch            -> UnifiedSearchPreset
  enablePostgis           -> GraphilePostgisPreset
  enableLtree             -> GraphileLtreePreset
  enableDirectUploads     -> UploadPreset
  enablePresignedUploads  -> PresignedUrlPreset, BucketProvisionerPreset
  enableAggregates        -> PgAggregatesPreset
  enableLlm               -> reserved (no plugin yet)

connectionFilterOperatorFactories are dynamically built based on which
satellite plugins (search, postgis, ltree) are enabled.

The static ConstructivePreset export is preserved for backwards compat
as createConstructivePreset() with no args (all defaults).

Server's buildPreset() now passes databaseSettings directly to the
factory, wiring the full feature flag cascade end-to-end.
@devin-ai-integration
Copy link
Copy Markdown
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

@pyramation pyramation merged commit f5abd2c into main May 10, 2026
54 checks passed
@pyramation pyramation deleted the feat/phase3-typed-settings-reads branch May 10, 2026 01:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant