Skip to content

Commit 61fef36

Browse files
authored
Merge pull request #972 from constructive-io/feat/remove-condition-from-codegen
fix(codegen): remove condition generation from ORM codegen
2 parents 3bf7c52 + 28ad7e9 commit 61fef36

20 files changed

Lines changed: 59 additions & 563 deletions

graphql/codegen/src/__tests__/codegen/__snapshots__/cli-generator.test.ts.snap

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -757,7 +757,7 @@ async function handleList(argv: Partial<Record<string, unknown>>, _prompter: Inq
757757
isElectric: true,
758758
createdAt: true
759759
};
760-
const findManyArgs = parseFindManyArgs<FindManyArgs<CarSelect, CarFilter, never, CarsOrderBy> & {
760+
const findManyArgs = parseFindManyArgs<FindManyArgs<CarSelect, CarFilter, CarsOrderBy> & {
761761
select: CarSelect;
762762
}>(argv, defaultSelect);
763763
const client = getClient();
@@ -781,7 +781,7 @@ async function handleFindFirst(argv: Partial<Record<string, unknown>>, _prompter
781781
isElectric: true,
782782
createdAt: true
783783
};
784-
const findFirstArgs = parseFindFirstArgs<FindFirstArgs<CarSelect, CarFilter, never> & {
784+
const findFirstArgs = parseFindFirstArgs<FindFirstArgs<CarSelect, CarFilter> & {
785785
select: CarSelect;
786786
}>(argv, defaultSelect);
787787
const client = getClient();
@@ -1219,7 +1219,7 @@ async function handleList(argv: Partial<Record<string, unknown>>, _prompter: Inq
12191219
name: true,
12201220
licenseNumber: true
12211221
};
1222-
const findManyArgs = parseFindManyArgs<FindManyArgs<DriverSelect, DriverFilter, never, DriversOrderBy> & {
1222+
const findManyArgs = parseFindManyArgs<FindManyArgs<DriverSelect, DriverFilter, DriversOrderBy> & {
12231223
select: DriverSelect;
12241224
}>(argv, defaultSelect);
12251225
const client = getClient();
@@ -1240,7 +1240,7 @@ async function handleFindFirst(argv: Partial<Record<string, unknown>>, _prompter
12401240
name: true,
12411241
licenseNumber: true
12421242
};
1243-
const findFirstArgs = parseFindFirstArgs<FindFirstArgs<DriverSelect, DriverFilter, never> & {
1243+
const findFirstArgs = parseFindFirstArgs<FindFirstArgs<DriverSelect, DriverFilter> & {
12441244
select: DriverSelect;
12451245
}>(argv, defaultSelect);
12461246
const client = getClient();
@@ -3255,7 +3255,7 @@ async function handleList(argv: Partial<Record<string, unknown>>, _prompter: Inq
32553255
email: true,
32563256
name: true
32573257
};
3258-
const findManyArgs = parseFindManyArgs<FindManyArgs<UserSelect, UserFilter, never, UsersOrderBy> & {
3258+
const findManyArgs = parseFindManyArgs<FindManyArgs<UserSelect, UserFilter, UsersOrderBy> & {
32593259
select: UserSelect;
32603260
}>(argv, defaultSelect);
32613261
const client = getClient("auth");
@@ -3276,7 +3276,7 @@ async function handleFindFirst(argv: Partial<Record<string, unknown>>, _prompter
32763276
email: true,
32773277
name: true
32783278
};
3279-
const findFirstArgs = parseFindFirstArgs<FindFirstArgs<UserSelect, UserFilter, never> & {
3279+
const findFirstArgs = parseFindFirstArgs<FindFirstArgs<UserSelect, UserFilter> & {
32803280
select: UserSelect;
32813281
}>(argv, defaultSelect);
32823282
const client = getClient("auth");
@@ -3487,7 +3487,7 @@ async function handleList(argv: Partial<Record<string, unknown>>, _prompter: Inq
34873487
id: true,
34883488
role: true
34893489
};
3490-
const findManyArgs = parseFindManyArgs<FindManyArgs<MemberSelect, MemberFilter, never, MembersOrderBy> & {
3490+
const findManyArgs = parseFindManyArgs<FindManyArgs<MemberSelect, MemberFilter, MembersOrderBy> & {
34913491
select: MemberSelect;
34923492
}>(argv, defaultSelect);
34933493
const client = getClient("members");
@@ -3507,7 +3507,7 @@ async function handleFindFirst(argv: Partial<Record<string, unknown>>, _prompter
35073507
id: true,
35083508
role: true
35093509
};
3510-
const findFirstArgs = parseFindFirstArgs<FindFirstArgs<MemberSelect, MemberFilter, never> & {
3510+
const findFirstArgs = parseFindFirstArgs<FindFirstArgs<MemberSelect, MemberFilter> & {
35113511
select: MemberSelect;
35123512
}>(argv, defaultSelect);
35133513
const client = getClient("members");
@@ -3711,7 +3711,7 @@ async function handleList(argv: Partial<Record<string, unknown>>, _prompter: Inq
37113711
isElectric: true,
37123712
createdAt: true
37133713
};
3714-
const findManyArgs = parseFindManyArgs<FindManyArgs<CarSelect, CarFilter, never, CarsOrderBy> & {
3714+
const findManyArgs = parseFindManyArgs<FindManyArgs<CarSelect, CarFilter, CarsOrderBy> & {
37153715
select: CarSelect;
37163716
}>(argv, defaultSelect);
37173717
const client = getClient("app");
@@ -3735,7 +3735,7 @@ async function handleFindFirst(argv: Partial<Record<string, unknown>>, _prompter
37353735
isElectric: true,
37363736
createdAt: true
37373737
};
3738-
const findFirstArgs = parseFindFirstArgs<FindFirstArgs<CarSelect, CarFilter, never> & {
3738+
const findFirstArgs = parseFindFirstArgs<FindFirstArgs<CarSelect, CarFilter> & {
37393739
select: CarSelect;
37403740
}>(argv, defaultSelect);
37413741
const client = getClient("app");

graphql/codegen/src/__tests__/codegen/__snapshots__/client-generator.test.ts.snap

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -276,10 +276,9 @@ export interface PageInfo {
276276
endCursor?: string | null;
277277
}
278278
279-
export interface FindManyArgs<TSelect, TWhere, TCondition = never, TOrderBy = never> {
279+
export interface FindManyArgs<TSelect, TWhere, TOrderBy = never> {
280280
select?: TSelect;
281281
where?: TWhere;
282-
condition?: TCondition;
283282
orderBy?: TOrderBy[];
284283
first?: number;
285284
last?: number;
@@ -288,10 +287,9 @@ export interface FindManyArgs<TSelect, TWhere, TCondition = never, TOrderBy = ne
288287
offset?: number;
289288
}
290289
291-
export interface FindFirstArgs<TSelect, TWhere, TCondition = never> {
290+
export interface FindFirstArgs<TSelect, TWhere> {
292291
select?: TSelect;
293292
where?: TWhere;
294-
condition?: TCondition;
295293
}
296294
297295
export interface CreateArgs<TSelect, TData> {

graphql/codegen/src/__tests__/codegen/__snapshots__/model-generator.test.ts.snap

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import type { User, UserWithRelations, UserSelect, UserFilter, UsersOrderBy, Cre
1313
import { connectionFieldsMap } from "../input-types";
1414
export class UserModel {
1515
constructor(private client: OrmClient) {}
16-
findMany<S extends UserSelect>(args: FindManyArgs<S, UserFilter, never, UsersOrderBy> & {
16+
findMany<S extends UserSelect>(args: FindManyArgs<S, UserFilter, UsersOrderBy> & {
1717
select: S;
1818
} & StrictSelect<S, UserSelect>): QueryBuilder<{
1919
users: ConnectionResult<InferSelectResult<UserWithRelations, S>>;
@@ -162,7 +162,7 @@ import type { AuditLog, AuditLogWithRelations, AuditLogSelect, AuditLogFilter, A
162162
import { connectionFieldsMap } from "../input-types";
163163
export class AuditLogModel {
164164
constructor(private client: OrmClient) {}
165-
findMany<S extends AuditLogSelect>(args: FindManyArgs<S, AuditLogFilter, never, AuditLogsOrderBy> & {
165+
findMany<S extends AuditLogSelect>(args: FindManyArgs<S, AuditLogFilter, AuditLogsOrderBy> & {
166166
select: S;
167167
} & StrictSelect<S, AuditLogSelect>): QueryBuilder<{
168168
auditLogs: ConnectionResult<InferSelectResult<AuditLogWithRelations, S>>;
@@ -265,7 +265,7 @@ import type { Organization, OrganizationWithRelations, OrganizationSelect, Organ
265265
import { connectionFieldsMap } from "../input-types";
266266
export class OrganizationModel {
267267
constructor(private client: OrmClient) {}
268-
findMany<S extends OrganizationSelect>(args: FindManyArgs<S, OrganizationFilter, never, OrganizationsOrderBy> & {
268+
findMany<S extends OrganizationSelect>(args: FindManyArgs<S, OrganizationFilter, OrganizationsOrderBy> & {
269269
select: S;
270270
} & StrictSelect<S, OrganizationSelect>): QueryBuilder<{
271271
allOrganizations: ConnectionResult<InferSelectResult<OrganizationWithRelations, S>>;

graphql/codegen/src/__tests__/codegen/__snapshots__/react-query-hooks.test.ts.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1672,7 +1672,7 @@ import type { ListSelectionConfig } from "../selection";
16721672
import type { UserSelect, UserWithRelations, UserFilter, UsersOrderBy } from "../../orm/input-types";
16731673
import type { FindManyArgs, InferSelectResult, ConnectionResult, HookStrictSelect } from "../../orm/select-types";
16741674
export type { UserSelect, UserWithRelations, UserFilter, UsersOrderBy } from "../../orm/input-types";
1675-
export const usersQueryKey = (variables?: FindManyArgs<unknown, UserFilter, never, UsersOrderBy>) => ["user", "list", variables] as const;
1675+
export const usersQueryKey = (variables?: FindManyArgs<unknown, UserFilter, UsersOrderBy>) => ["user", "list", variables] as const;
16761676
/**
16771677
* Query hook for fetching User list
16781678
*

graphql/codegen/src/__tests__/codegen/input-types-generator.test.ts

Lines changed: 5 additions & 160 deletions
Original file line numberDiff line numberDiff line change
@@ -893,13 +893,7 @@ describe('collectPayloadTypeNames', () => {
893893
// Tests - Plugin-Injected Condition Fields (e.g., VectorSearchPlugin)
894894
// ============================================================================
895895

896-
describe('plugin-injected condition fields', () => {
897-
/**
898-
* Simulates a table with a vector embedding column.
899-
* The VectorSearchPlugin adds extra condition fields (e.g., embeddingNearby)
900-
* that are NOT derived from the table's own columns, but are injected into
901-
* the GraphQL schema's condition type via plugin hooks.
902-
*/
896+
describe('plugin-injected orderBy values', () => {
903897
const contactTable = createTable({
904898
name: 'Contact',
905899
fields: [
@@ -920,151 +914,6 @@ describe('plugin-injected condition fields', () => {
920914
},
921915
});
922916

923-
it('includes plugin-injected condition fields from TypeRegistry', () => {
924-
// Registry simulates what PostGraphile + VectorSearchPlugin produce:
925-
// The ContactCondition type has the regular columns PLUS an extra
926-
// "embeddingNearby" field of type VectorNearbyInput injected by the plugin.
927-
const registry = createTypeRegistry({
928-
ContactCondition: {
929-
kind: 'INPUT_OBJECT',
930-
name: 'ContactCondition',
931-
inputFields: [
932-
{ name: 'id', type: createTypeRef('SCALAR', 'UUID') },
933-
{ name: 'name', type: createTypeRef('SCALAR', 'String') },
934-
{ name: 'email', type: createTypeRef('SCALAR', 'String') },
935-
{ name: 'embedding', type: createTypeRef('SCALAR', 'Vector') },
936-
{
937-
name: 'embeddingNearby',
938-
type: createTypeRef('INPUT_OBJECT', 'VectorNearbyInput'),
939-
description: 'Find contacts near a vector embedding',
940-
},
941-
],
942-
},
943-
VectorNearbyInput: {
944-
kind: 'INPUT_OBJECT',
945-
name: 'VectorNearbyInput',
946-
inputFields: [
947-
{
948-
name: 'vector',
949-
type: createNonNull(createTypeRef('SCALAR', 'Vector')),
950-
},
951-
{
952-
name: 'metric',
953-
type: createTypeRef('ENUM', 'VectorMetric'),
954-
},
955-
{
956-
name: 'threshold',
957-
type: createTypeRef('SCALAR', 'Float'),
958-
},
959-
],
960-
},
961-
VectorMetric: {
962-
kind: 'ENUM',
963-
name: 'VectorMetric',
964-
enumValues: ['L2', 'INNER_PRODUCT', 'COSINE'],
965-
},
966-
});
967-
968-
const result = generateInputTypesFile(registry, new Set(), [contactTable], undefined, true, { condition: true });
969-
970-
// Regular table column fields should still be present
971-
expect(result.content).toContain('export interface ContactCondition {');
972-
expect(result.content).toContain('id?: string | null;');
973-
expect(result.content).toContain('name?: string | null;');
974-
expect(result.content).toContain('email?: string | null;');
975-
976-
// Plugin-injected field should also be present
977-
expect(result.content).toContain('embeddingNearby?: VectorNearbyInput');
978-
979-
// The referenced VectorNearbyInput type should be generated as a custom input type
980-
expect(result.content).toContain('export interface VectorNearbyInput {');
981-
982-
// Transitively referenced enum type (VectorMetric) should also be generated
983-
expect(result.content).toContain('VectorMetric');
984-
expect(result.content).toContain('"L2"');
985-
expect(result.content).toContain('"INNER_PRODUCT"');
986-
expect(result.content).toContain('"COSINE"');
987-
});
988-
989-
it('generates transitively referenced enum types from input fields', () => {
990-
// This specifically tests that enum types referenced by input object fields
991-
// are followed and generated, not just types ending with "Input".
992-
// VectorNearbyInput.metric references VectorMetric (an ENUM),
993-
// which must be included in the output.
994-
const registry = createTypeRegistry({
995-
ContactCondition: {
996-
kind: 'INPUT_OBJECT',
997-
name: 'ContactCondition',
998-
inputFields: [
999-
{ name: 'id', type: createTypeRef('SCALAR', 'UUID') },
1000-
{ name: 'name', type: createTypeRef('SCALAR', 'String') },
1001-
{
1002-
name: 'embeddingNearby',
1003-
type: createTypeRef('INPUT_OBJECT', 'VectorNearbyInput'),
1004-
},
1005-
],
1006-
},
1007-
VectorNearbyInput: {
1008-
kind: 'INPUT_OBJECT',
1009-
name: 'VectorNearbyInput',
1010-
inputFields: [
1011-
{
1012-
name: 'vector',
1013-
type: createNonNull(createTypeRef('SCALAR', 'Vector')),
1014-
},
1015-
{
1016-
name: 'metric',
1017-
type: createTypeRef('ENUM', 'VectorMetric'),
1018-
},
1019-
],
1020-
},
1021-
VectorMetric: {
1022-
kind: 'ENUM',
1023-
name: 'VectorMetric',
1024-
enumValues: ['L2', 'INNER_PRODUCT', 'COSINE'],
1025-
},
1026-
});
1027-
1028-
const result = generateInputTypesFile(registry, new Set(), [contactTable], undefined, true, { condition: true });
1029-
1030-
// VectorNearbyInput should be generated (follows *Input pattern)
1031-
expect(result.content).toContain('export interface VectorNearbyInput {');
1032-
1033-
// VectorMetric enum should ALSO be generated (transitive enum resolution)
1034-
expect(result.content).toMatch(/export type VectorMetric\s*=/);
1035-
expect(result.content).toContain('"L2"');
1036-
expect(result.content).toContain('"INNER_PRODUCT"');
1037-
expect(result.content).toContain('"COSINE"');
1038-
});
1039-
1040-
it('does not duplicate fields already derived from table columns', () => {
1041-
const registry = createTypeRegistry({
1042-
ContactCondition: {
1043-
kind: 'INPUT_OBJECT',
1044-
name: 'ContactCondition',
1045-
inputFields: [
1046-
{ name: 'id', type: createTypeRef('SCALAR', 'UUID') },
1047-
{ name: 'name', type: createTypeRef('SCALAR', 'String') },
1048-
{ name: 'email', type: createTypeRef('SCALAR', 'String') },
1049-
{ name: 'embedding', type: createTypeRef('SCALAR', 'Vector') },
1050-
],
1051-
},
1052-
});
1053-
1054-
const result = generateInputTypesFile(registry, new Set(), [contactTable], undefined, true, { condition: true });
1055-
1056-
// Count occurrences of 'id?' in the ContactCondition interface
1057-
const conditionMatch = result.content.match(
1058-
/export interface ContactCondition \{([^}]*)\}/s,
1059-
);
1060-
expect(conditionMatch).toBeTruthy();
1061-
const conditionBody = conditionMatch![1];
1062-
1063-
// Each field should appear only once
1064-
const idOccurrences = (conditionBody.match(/\bid\?/g) || []).length;
1065-
expect(idOccurrences).toBe(1);
1066-
});
1067-
1068917
it('includes plugin-injected orderBy values from TypeRegistry', () => {
1069918
const registry = createTypeRegistry({
1070919
ContactsOrderBy: {
@@ -1100,15 +949,11 @@ describe('plugin-injected condition fields', () => {
1100949
expect(result.content).toContain('"EMBEDDING_DISTANCE_DESC"');
1101950
});
1102951

1103-
it('works without typeRegistry (backwards compatible)', () => {
1104-
// When no typeRegistry has the condition type, only table columns are used
1105-
const result = generateInputTypesFile(new Map(), new Set(), [contactTable], undefined, true, { condition: true });
952+
it('does not generate Condition types', () => {
953+
const result = generateInputTypesFile(new Map(), new Set(), [contactTable]);
1106954

1107-
expect(result.content).toContain('export interface ContactCondition {');
1108-
expect(result.content).toContain('id?: string | null;');
1109-
expect(result.content).toContain('name?: string | null;');
1110-
// No plugin-injected fields
1111-
expect(result.content).not.toContain('embeddingNearby');
955+
// Condition types should NOT be generated
956+
expect(result.content).not.toContain('ContactCondition');
1112957
});
1113958
});
1114959

graphql/codegen/src/__tests__/codegen/model-generator.test.ts

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ describe('model-generator', () => {
233233
expect(result.content).toContain('ProductPatch');
234234
});
235235

236-
it('imports and wires Condition type for findMany and findFirst', () => {
236+
it('does not include Condition type in findMany or findFirst', () => {
237237
const table = createTable({
238238
name: 'Contact',
239239
fields: [
@@ -249,25 +249,10 @@ describe('model-generator', () => {
249249
},
250250
});
251251

252-
const result = generateModelFile(table, false, { condition: true });
253-
254-
// Condition type should be imported
255-
expect(result.content).toContain('ContactCondition');
256-
257-
// findMany should include condition in its args type
258-
expect(result.content).toContain(
259-
'FindManyArgs<S, ContactFilter, ContactCondition, ContactsOrderBy>',
260-
);
261-
262-
// findFirst should include condition in its args type
263-
expect(result.content).toContain(
264-
'FindFirstArgs<S, ContactFilter, ContactCondition>',
265-
);
266-
267-
// condition should be forwarded in the body args object
268-
expect(result.content).toContain('condition: args?.condition');
252+
const result = generateModelFile(table, false);
269253

270-
// conditionTypeName should be passed as a string literal to the document builder
271-
expect(result.content).toContain('"ContactCondition"');
254+
// Condition type should NOT be imported or referenced
255+
expect(result.content).not.toContain('ContactCondition');
256+
expect(result.content).not.toContain('condition');
272257
});
273258
});

0 commit comments

Comments
 (0)