Skip to content

Commit 7de7e64

Browse files
authored
Merge pull request #1026 from constructive-io/fix/orm-interfaces-findx-752
fix: orm interfaces findX
2 parents f53de30 + d7aea85 commit 7de7e64

1,093 files changed

Lines changed: 13608 additions & 11538 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

graphql/codegen/SPEC.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -326,8 +326,8 @@ export class UserModel {
326326
}
327327

328328
findFirst<const S extends UserSelect>(
329-
args?: FindFirstArgs<DeepExact<S, UserSelect>, UserFilter>
330-
): QueryBuilder<{ users: { nodes: InferSelectResult<UserWithRelations, S>[] } }> {
329+
args?: FindFirstArgs<DeepExact<S, UserSelect>, UserFilter, UsersOrderBy>
330+
): QueryBuilder<{ user: InferSelectResult<UserWithRelations, S> | null }> {
331331
// ...
332332
}
333333

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

Lines changed: 10 additions & 10 deletions
Large diffs are not rendered by default.

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -397,9 +397,10 @@ export interface FindManyArgs<TSelect, TWhere, TOrderBy = never> {
397397
offset?: number;
398398
}
399399
400-
export interface FindFirstArgs<TSelect, TWhere> {
400+
export interface FindFirstArgs<TSelect, TWhere, TOrderBy> {
401401
select?: TSelect;
402402
where?: TWhere;
403+
orderBy?: TOrderBy[];
403404
}
404405
405406
export interface CreateArgs<TSelect, TData> {

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

Lines changed: 45 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -39,26 +39,32 @@ export class UserModel {
3939
variables
4040
});
4141
}
42-
findFirst<S extends UserSelect>(args: FindFirstArgs<S, UserFilter> & {
42+
findFirst<S extends UserSelect>(args: FindFirstArgs<S, UserFilter, UsersOrderBy> & {
4343
select: S;
4444
} & StrictSelect<S, UserSelect>): QueryBuilder<{
45-
users: {
46-
nodes: InferSelectResult<UserWithRelations, S>[];
47-
};
45+
user: InferSelectResult<UserWithRelations, S> | null;
4846
}> {
4947
const {
5048
document,
5149
variables
5250
} = buildFindFirstDocument("User", "users", args.select, {
53-
where: args?.where
54-
}, "UserFilter", connectionFieldsMap);
51+
where: args?.where,
52+
orderBy: args?.orderBy as string[] | undefined
53+
}, "UserFilter", "UsersOrderBy", connectionFieldsMap);
5554
return new QueryBuilder({
5655
client: this.client,
5756
operation: "query",
5857
operationName: "User",
59-
fieldName: "users",
58+
fieldName: "user",
6059
document,
61-
variables
60+
variables,
61+
transform: (data: {
62+
users?: {
63+
nodes?: InferSelectResult<UserWithRelations, S>[];
64+
};
65+
}) => ({
66+
"user": data.users?.nodes?.[0] ?? null
67+
})
6268
});
6369
}
6470
findOne<S extends UserSelect>(args: {
@@ -188,26 +194,32 @@ export class AuditLogModel {
188194
variables
189195
});
190196
}
191-
findFirst<S extends AuditLogSelect>(args: FindFirstArgs<S, AuditLogFilter> & {
197+
findFirst<S extends AuditLogSelect>(args: FindFirstArgs<S, AuditLogFilter, AuditLogsOrderBy> & {
192198
select: S;
193199
} & StrictSelect<S, AuditLogSelect>): QueryBuilder<{
194-
auditLogs: {
195-
nodes: InferSelectResult<AuditLogWithRelations, S>[];
196-
};
200+
auditLog: InferSelectResult<AuditLogWithRelations, S> | null;
197201
}> {
198202
const {
199203
document,
200204
variables
201205
} = buildFindFirstDocument("AuditLog", "auditLogs", args.select, {
202-
where: args?.where
203-
}, "AuditLogFilter", connectionFieldsMap);
206+
where: args?.where,
207+
orderBy: args?.orderBy as string[] | undefined
208+
}, "AuditLogFilter", "AuditLogsOrderBy", connectionFieldsMap);
204209
return new QueryBuilder({
205210
client: this.client,
206211
operation: "query",
207212
operationName: "AuditLog",
208-
fieldName: "auditLogs",
213+
fieldName: "auditLog",
209214
document,
210-
variables
215+
variables,
216+
transform: (data: {
217+
auditLogs?: {
218+
nodes?: InferSelectResult<AuditLogWithRelations, S>[];
219+
};
220+
}) => ({
221+
"auditLog": data.auditLogs?.nodes?.[0] ?? null
222+
})
211223
});
212224
}
213225
findOne<S extends AuditLogSelect>(args: {
@@ -291,33 +303,39 @@ export class OrganizationModel {
291303
variables
292304
});
293305
}
294-
findFirst<S extends OrganizationSelect>(args: FindFirstArgs<S, OrganizationFilter> & {
306+
findFirst<S extends OrganizationSelect>(args: FindFirstArgs<S, OrganizationFilter, OrganizationsOrderBy> & {
295307
select: S;
296308
} & StrictSelect<S, OrganizationSelect>): QueryBuilder<{
297-
allOrganizations: {
298-
nodes: InferSelectResult<OrganizationWithRelations, S>[];
299-
};
309+
organization: InferSelectResult<OrganizationWithRelations, S> | null;
300310
}> {
301311
const {
302312
document,
303313
variables
304314
} = buildFindFirstDocument("Organization", "allOrganizations", args.select, {
305-
where: args?.where
306-
}, "OrganizationFilter", connectionFieldsMap);
315+
where: args?.where,
316+
orderBy: args?.orderBy as string[] | undefined
317+
}, "OrganizationFilter", "OrganizationsOrderBy", connectionFieldsMap);
307318
return new QueryBuilder({
308319
client: this.client,
309320
operation: "query",
310321
operationName: "Organization",
311-
fieldName: "allOrganizations",
312-
document,
313-
variables
322+
fieldName: "organization",
323+
document,
324+
variables,
325+
transform: (data: {
326+
allOrganizations?: {
327+
nodes?: InferSelectResult<OrganizationWithRelations, S>[];
328+
};
329+
}) => ({
330+
"organization": data.allOrganizations?.nodes?.[0] ?? null
331+
})
314332
});
315333
}
316334
findOne<S extends OrganizationSelect>(args: {
317335
id: string;
318336
select: S;
319337
} & StrictSelect<S, OrganizationSelect>): QueryBuilder<{
320-
organizationById: InferSelectResult<OrganizationWithRelations, S> | null;
338+
organization: InferSelectResult<OrganizationWithRelations, S> | null;
321339
}> {
322340
const {
323341
document,
@@ -327,7 +345,7 @@ export class OrganizationModel {
327345
client: this.client,
328346
operation: "query",
329347
operationName: "Organization",
330-
fieldName: "organizationById",
348+
fieldName: "organization",
331349
document,
332350
variables
333351
});

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1843,7 +1843,7 @@ export async function fetchUserQuery<S extends UserSelect>(params: {
18431843
export async function fetchUserQuery(params: {
18441844
id: string;
18451845
selection: SelectionConfig<UserSelect>;
1846-
}) {
1846+
}): Promise<any> {
18471847
const args = buildSelectionArgs<UserSelect>(params.selection);
18481848
return getClient().user.findOne({
18491849
id: params.id,
@@ -1975,7 +1975,7 @@ export async function fetchPostQuery<S extends PostSelect>(params: {
19751975
export async function fetchPostQuery(params: {
19761976
id: string;
19771977
selection: SelectionConfig<PostSelect>;
1978-
}) {
1978+
}): Promise<any> {
19791979
const args = buildSelectionArgs<PostSelect>(params.selection);
19801980
return getClient().post.findOne({
19811981
id: params.id,
@@ -2094,7 +2094,7 @@ export async function fetchUserQuery<S extends UserSelect>(params: {
20942094
export async function fetchUserQuery(params: {
20952095
id: string;
20962096
selection: SelectionConfig<UserSelect>;
2097-
}) {
2097+
}): Promise<any> {
20982098
const args = buildSelectionArgs<UserSelect>(params.selection);
20992099
return getClient().user.findOne({
21002100
id: params.id,

graphql/codegen/src/core/codegen/cli/table-command-generator.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -520,7 +520,7 @@ function buildFindManyArgsType(table: Table): t.TSType {
520520

521521
/**
522522
* Build the FindFirstArgs type instantiation for a table:
523-
* FindFirstArgs<SelectType, FilterType> & { select: SelectType }
523+
* FindFirstArgs<SelectType, FilterType, OrderByType> & { select: SelectType }
524524
*
525525
* The intersection with { select: SelectType } makes select required,
526526
* matching what the ORM's findFirst method expects.
@@ -529,11 +529,13 @@ function buildFindFirstArgsType(table: Table): t.TSType {
529529
const { typeName } = getTableNames(table);
530530
const selectTypeName = `${typeName}Select`;
531531
const whereTypeName = getFilterTypeName(table);
532+
const orderByTypeName = getOrderByTypeName(table);
532533
const findFirstType = t.tsTypeReference(
533534
t.identifier('FindFirstArgs'),
534535
t.tsTypeParameterInstantiation([
535536
t.tsTypeReference(t.identifier(selectTypeName)),
536537
t.tsTypeReference(t.identifier(whereTypeName)),
538+
t.tsTypeReference(t.identifier(orderByTypeName)),
537539
]),
538540
);
539541
// Intersect with { select: SelectType } to make select required
@@ -1766,6 +1768,7 @@ export function generateTableCommand(table: Table, options?: TableCommandOptions
17661768
' --select <fields> Comma-separated list of fields to return',
17671769
' --where.<field>.<op> Filter (dot-notation, e.g. --where.status.equalTo active)',
17681770
' --condition.<f>.<op> Condition filter (dot-notation)',
1771+
' --orderBy <values> Comma-separated ordering values (e.g. NAME_ASC,CREATED_AT_DESC)',
17691772
'',
17701773
);
17711774
if (hasSearchFields) {

graphql/codegen/src/core/codegen/orm/client-generator.ts

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -231,22 +231,6 @@ export function generateCreateClientFile(
231231
// Re-export all models
232232
statements.push(t.exportAllDeclaration(t.stringLiteral('./models')));
233233

234-
// Re-export NodeHttpAdapter when enabled (for use in any Node.js application)
235-
if (options?.nodeHttpAdapter) {
236-
statements.push(
237-
t.exportNamedDeclaration(
238-
null,
239-
[
240-
t.exportSpecifier(
241-
t.identifier('NodeHttpAdapter'),
242-
t.identifier('NodeHttpAdapter'),
243-
),
244-
],
245-
t.stringLiteral('./node-fetch'),
246-
),
247-
);
248-
}
249-
250234
// Re-export custom operations
251235
if (hasCustomQueries) {
252236
statements.push(

graphql/codegen/src/core/codegen/orm/model-generator.ts

Lines changed: 76 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import {
1919
getGeneratedFileHeader,
2020
getOrderByTypeName,
2121
getPrimaryKeyInfo,
22-
getSingleRowQueryName,
2322
getTableNames,
2423
hasValidPrimaryKey,
2524
lcFirst,
@@ -193,7 +192,10 @@ export function generateModelFile(
193192
const pkField = pkFields[0];
194193
const pluralQueryName = table.query?.all ?? pluralName;
195194
const singleQueryName = table.query?.one;
196-
const singleResultFieldName = getSingleRowQueryName(table);
195+
// The unwrapped result key for findFirst/findOne — must be the friendly
196+
// singular noun (e.g. "animal"), NOT the GraphQL by-id query name (e.g.
197+
// "animalById"), so the surface aligns with the rest of the SDK.
198+
const singleResultFieldName = singularName;
197199
const createMutationName = table.query?.create ?? `create${typeName}`;
198200
const updateMutationName = table.query?.update;
199201
const deleteMutationName = table.query?.delete;
@@ -452,6 +454,7 @@ export function generateModelFile(
452454
const findFirstTypeArgs: Array<(sel: t.TSType) => t.TSType> = [
453455
(sel: t.TSType) => sel,
454456
() => t.tsTypeReference(t.identifier(whereTypeName)),
457+
() => t.tsTypeReference(t.identifier(orderByTypeName)),
455458
];
456459
const argsType = (sel: t.TSType) =>
457460
t.tsTypeReference(
@@ -467,23 +470,17 @@ export function generateModelFile(
467470
t.tsTypeParameterInstantiation([
468471
t.tsTypeLiteral([
469472
t.tsPropertySignature(
470-
t.identifier(pluralQueryName),
473+
t.identifier(singleResultFieldName),
471474
t.tsTypeAnnotation(
472-
t.tsTypeLiteral([
473-
t.tsPropertySignature(
474-
t.identifier('nodes'),
475-
t.tsTypeAnnotation(
476-
t.tsArrayType(
477-
t.tsTypeReference(
478-
t.identifier('InferSelectResult'),
479-
t.tsTypeParameterInstantiation([
480-
t.tsTypeReference(t.identifier(relationTypeName)),
481-
sel,
482-
]),
483-
),
484-
),
485-
),
475+
t.tsUnionType([
476+
t.tsTypeReference(
477+
t.identifier('InferSelectResult'),
478+
t.tsTypeParameterInstantiation([
479+
t.tsTypeReference(t.identifier(relationTypeName)),
480+
sel,
481+
]),
486482
),
483+
t.tsNullKeyword(),
487484
]),
488485
),
489486
),
@@ -514,15 +511,75 @@ export function generateModelFile(
514511
true,
515512
),
516513
),
514+
t.objectProperty(
515+
t.identifier('orderBy'),
516+
t.tsAsExpression(
517+
t.optionalMemberExpression(
518+
t.identifier('args'),
519+
t.identifier('orderBy'),
520+
false,
521+
true,
522+
),
523+
t.tsUnionType([
524+
t.tsArrayType(t.tsStringKeyword()),
525+
t.tsUndefinedKeyword(),
526+
]),
527+
),
528+
),
517529
];
518530
const bodyArgs = [
519531
t.stringLiteral(typeName),
520532
t.stringLiteral(pluralQueryName),
521533
selectExpr,
522534
t.objectExpression(findFirstObjProps),
523535
t.stringLiteral(whereTypeName),
536+
t.stringLiteral(orderByTypeName),
524537
t.identifier('connectionFieldsMap'),
525538
];
539+
const transformDataParam = t.identifier('data');
540+
const transformedNodesProp = t.tsPropertySignature(
541+
t.identifier('nodes'),
542+
t.tsTypeAnnotation(
543+
t.tsArrayType(
544+
t.tsTypeReference(
545+
t.identifier('InferSelectResult'),
546+
t.tsTypeParameterInstantiation([
547+
t.tsTypeReference(t.identifier(relationTypeName)),
548+
sRef(),
549+
]),
550+
),
551+
),
552+
),
553+
);
554+
transformedNodesProp.optional = true;
555+
const transformedCollectionProp = t.tsPropertySignature(
556+
t.identifier(pluralQueryName),
557+
t.tsTypeAnnotation(t.tsTypeLiteral([transformedNodesProp])),
558+
);
559+
transformedCollectionProp.optional = true;
560+
transformDataParam.typeAnnotation = t.tsTypeAnnotation(
561+
t.tsTypeLiteral([transformedCollectionProp]),
562+
);
563+
const firstNodeExpr = t.optionalMemberExpression(
564+
t.optionalMemberExpression(
565+
t.memberExpression(t.identifier('data'), t.identifier(pluralQueryName)),
566+
t.identifier('nodes'),
567+
false,
568+
true,
569+
),
570+
t.numericLiteral(0),
571+
true,
572+
true,
573+
);
574+
const transformFn = t.arrowFunctionExpression(
575+
[transformDataParam],
576+
t.objectExpression([
577+
t.objectProperty(
578+
t.stringLiteral(singleResultFieldName),
579+
t.logicalExpression('??', firstNodeExpr, t.nullLiteral()),
580+
),
581+
]),
582+
);
526583
classBody.push(
527584
createClassMethod(
528585
'findFirst',
@@ -534,7 +591,8 @@ export function generateModelFile(
534591
bodyArgs,
535592
'query',
536593
typeName,
537-
pluralQueryName,
594+
singleResultFieldName,
595+
[t.objectProperty(t.identifier('transform'), transformFn)],
538596
),
539597
),
540598
);

0 commit comments

Comments
 (0)