Skip to content

feat: tighten TypeScript types in satellite filter plugins#804

Open
pyramation wants to merge 20 commits intomainfrom
devin/1773368344-tighten-filter-types
Open

feat: tighten TypeScript types in satellite filter plugins#804
pyramation wants to merge 20 commits intomainfrom
devin/1773368344-tighten-filter-types

Conversation

@pyramation
Copy link
Contributor

feat: tighten TypeScript types in satellite filter plugins

Summary

Improves type safety across the satellite filter plugins (search, BM25, pgvector) by:

  1. Extracting shared getQueryBuilder utility into graphile-connection-filter/src/utils.ts — previously duplicated identically (~40-50 lines each) in search, BM25, and pgvector plugins
  2. Removing (build as any) casts where PostGraphile's Build interface already provides proper types:
    • (build as any).dataplanPgbuild.dataplanPg (typed as typeof import("@dataplan/pg"))
    • (build as any).behaviorbuild.behavior
    • (build as any).input?.pgRegistrybuild.input.pgRegistry
  3. Removing = {} as any scope castspgCodec is already declared on ScopeInputObject by graphile-build-pg, so scope: { isPgConnectionFilter, pgCodec } = {} works without the cast

Net result: 81 insertions, 134 deletions. Three duplicate function definitions consolidated into one shared utility. ~10 as any casts eliminated.

Remaining as any casts are intentional:

  • (build.dataplanPg?.TYPES as any)?.tsvectortsvector is runtime-registered, not in static type defs
  • codec as any on inflection calls — generic type mismatch in PostGraphile's inflection signatures
  • (current as any)['parent'] in getQueryBuilderPgCondition.parent is a protected property

Note: This branch is based on PR #801 (condition deprecation). The cumulative diff includes changes from both #797 and #801.

Review & Testing Checklist for Human

  • Verify build.behavior compiles correctly — this was previously (build as any).behavior. The build passes, but confirm behavior is actually on the Build interface at runtime (not just a compile-time coincidence from a loose type)
  • Verify getQueryBuilder works identically from the shared location — the function was moved from 3 plugins into graphile-connection-filter/src/utils.ts. The (current as any)['parent'] workaround for the protected PgCondition.parent property is the one notable difference from the original inline versions which also used (current as any).parent
  • CI passes (40/40 checks) — all tests green confirms no functional regressions
  • Spot check a filter query with search/BM25/pgvector — any query using these plugins should work identically (e.g., allDocuments(filter: { embedding: { vector: { ... } } }) for pgvector)

Test Plan

Since this is purely a type-safety refactor with no functional changes, CI passing is the primary validation. For additional confidence:

  1. Query with search filter (tsvector): allOrders(filter: { fullTextTsv: { matches: "apple" } })
  2. Query with BM25 filter: similar pattern
  3. Query with pgvector filter: allDocuments(filter: { embedding: { vector: { query: [...], metric: COSINE } } })

Notes

Implements a from-scratch PostGraphile v5 native connection filter plugin,
replacing the upstream postgraphile-plugin-connection-filter dependency.

New package: graphile/graphile-connection-filter/

Plugin architecture (7 plugins):
- ConnectionFilterInflectionPlugin: filter type naming conventions
- ConnectionFilterTypesPlugin: registers per-table and per-scalar filter types
- ConnectionFilterArgPlugin: injects filter arg on connections via applyPlan
- ConnectionFilterAttributesPlugin: adds per-column filter fields
- ConnectionFilterOperatorsPlugin: standard/sort/pattern/jsonb/inet/array/range operators
- ConnectionFilterCustomOperatorsPlugin: addConnectionFilterOperator API for satellite plugins
- ConnectionFilterLogicalOperatorsPlugin: and/or/not logical composition

Key features:
- Full v5 native: uses Grafast planning, PgCondition, codec system, behavior registry
- EXPORTABLE pattern for schema caching
- Preserves addConnectionFilterOperator API for PostGIS, search, pgvector, textsearch plugins
- No relation filter plugins (simplifies configuration vs upstream)
- Preset factory: ConnectionFilterPreset(options)

Also updates graphile-settings to use the new workspace package.
…Operator

filterType is for table-level filter types (UserFilter), while filterFieldType
is for scalar operator types (StringFilter). Satellite plugins pass scalar type
names, so the lookup must use filterFieldType to match the registration in
ConnectionFilterTypesPlugin. Previously worked by coincidence since both
inflections produce the same output, but would silently fail if a consumer
overrode one inflection but not the other.
Adds computed column filter support — allows filtering on PostgreSQL functions
that take a table row as their first argument and return a scalar.

Controlled by connectionFilterComputedColumns schema option. The preset factory
includes the plugin only when the option is truthy (default in preset: true,
but constructive-preset sets it to false).
- Remove phantom postgraphile-plugin-connection-filter dep from graphile-pgvector-plugin (never used)
- Remove phantom postgraphile-plugin-connection-filter dep from graphile-pg-textsearch-plugin (never used)
- Update graphile-plugin-connection-filter-postgis to use graphile-connection-filter workspace dep with typed imports
- Update graphile-search-plugin to use graphile-connection-filter workspace dep with typed imports
- Replace (build as any).addConnectionFilterOperator casts with properly typed build.addConnectionFilterOperator
…on-filter

- Update search plugin, pgvector, and postgis test files to import from
  graphile-connection-filter instead of postgraphile-plugin-connection-filter
- Use ConnectionFilterPreset() factory instead of PostGraphileConnectionFilterPreset
- Import ConnectionFilterOperatorSpec type from graphile-connection-filter
- Fix smart quote characters in filter descriptions to match existing snapshots
…ion filter tests

- Add graphile-connection-filter as devDependency in graphile-pgvector-plugin
  (test file imports ConnectionFilterPreset but package had no dependency)
- Skip connectionFilterRelations tests in search plugin (relation filters
  are intentionally not included in the v5-native plugin; they were disabled
  in production via disablePlugins with the old plugin)
…toggle

- ConnectionFilterForwardRelationsPlugin: filter by FK parent relations
- ConnectionFilterBackwardRelationsPlugin: filter by backward relations (one-to-one + one-to-many with some/every/none)
- connectionFilterRelations toggle in preset (default: false)
- Un-skip relation filter tests in search plugin
- Updated augmentations, types, and exports
… at runtime

The preset factory now always includes relation plugins in the plugin list.
Each plugin checks build.options.connectionFilterRelations at runtime and
early-returns if disabled. This allows the toggle to be set by any preset
in the chain, not just the ConnectionFilterPreset() call.
Enables relation filter fields in the production schema:
- Forward: filter by FK parent (e.g. clientByClientId on OrderFilter)
- Backward: filter by children with some/every/none
- Codegen will pick up the new filter fields automatically
- Search plugin: isPgCondition → isPgConnectionFilter scope
- BM25 plugin: isPgCondition → isPgConnectionFilter scope
- Disable PgConditionArgumentPlugin and PgConditionCustomFieldsPlugin in preset
- Update all tests from condition: {...} to filter: {...}
- Add graphile-connection-filter devDependency to BM25 plugin
- Update search plugin graceful degradation tests to use filter

BREAKING CHANGE: The condition argument has been removed entirely.
All filtering now uses the filter argument exclusively.
- Search plugin plugin.test.ts: condition → filter syntax, add ConnectionFilterPreset
- Server-test: condition → filter in query with equalTo operator
- Clear stale snapshots (schema-snapshot, introspection) for regeneration
- Search plugin: update snapshot keys to match renamed filter-based tests
- Schema snapshot: remove all condition arguments and XxxCondition input types
- Introspection snapshot: remove condition arg and UserCondition type
- Kept conditionType in _meta schema (unrelated to deprecated condition arg)
… behavior for pgCodecRelation, update schema snapshot with relation filter types
…y filter at applyPlan level

Top-level empty filter {} is now treated as 'no filter' (skipped) instead of
throwing an error. Nested empty objects in and/or/not and relation filters are
still rejected. This removes the need for the connectionFilterAllowEmptyObjectInput
workaround in pgvector tests.
- Extract shared getQueryBuilder utility into graphile-connection-filter/src/utils.ts
- Remove duplicate getQueryBuilder from search, BM25, and pgvector plugins
- Replace (build as any).dataplanPg with build.dataplanPg (already typed on Build)
- Replace (build as any).behavior with build.behavior (already typed on Build)
- Replace (build as any).input.pgRegistry with build.input.pgRegistry (already typed)
- Remove scope destructuring as any casts (pgCodec already typed on ScopeInputObject)
- Add pgCodec comment to augmentations.ts noting it's already declared by graphile-build-pg
- Export getQueryBuilder from graphile-connection-filter for satellite plugin use
@devin-ai-integration
Copy link
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

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