@pothos/plugin-prisma 3.7.0 → 3.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/README.md +191 -100
- package/dts/global-types.d.ts +2 -2
- package/dts/global-types.d.ts.map +1 -1
- package/dts/index.d.ts.map +1 -1
- package/dts/model-loader.d.ts +13 -2
- package/dts/model-loader.d.ts.map +1 -1
- package/dts/node-ref.d.ts.map +1 -1
- package/dts/object-ref.d.ts.map +1 -1
- package/dts/prisma-field-builder.d.ts +7 -7
- package/dts/prisma-field-builder.d.ts.map +1 -1
- package/dts/types.d.ts +19 -9
- package/dts/types.d.ts.map +1 -1
- package/dts/util/cursors.d.ts +5 -1
- package/dts/util/cursors.d.ts.map +1 -1
- package/dts/util/datamodel.d.ts +5 -1
- package/dts/util/datamodel.d.ts.map +1 -1
- package/dts/util/get-client.d.ts +5 -1
- package/dts/util/get-client.d.ts.map +1 -1
- package/dts/util/loader-map.d.ts +2 -2
- package/dts/util/loader-map.d.ts.map +1 -1
- package/dts/util/map-query.d.ts.map +1 -1
- package/esm/field-builder.js.map +1 -1
- package/esm/generator.js.map +1 -1
- package/esm/global-types.js.map +1 -1
- package/esm/index.js +5 -7
- package/esm/index.js.map +1 -1
- package/esm/model-loader.js +88 -3
- package/esm/model-loader.js.map +1 -1
- package/esm/node-ref.js.map +1 -1
- package/esm/prisma-field-builder.js +29 -24
- package/esm/prisma-field-builder.js.map +1 -1
- package/esm/schema-builder.js +24 -16
- package/esm/schema-builder.js.map +1 -1
- package/esm/util/cursors.js +95 -2
- package/esm/util/cursors.js.map +1 -1
- package/esm/util/datamodel.js.map +1 -1
- package/esm/util/get-client.js.map +1 -1
- package/esm/util/loader-map.js +2 -3
- package/esm/util/loader-map.js.map +1 -1
- package/esm/util/map-query.js +47 -9
- package/esm/util/map-query.js.map +1 -1
- package/esm/util/relation-map.js.map +1 -1
- package/esm/util/selections.js.map +1 -1
- package/lib/field-builder.js +3 -6
- package/lib/field-builder.js.map +1 -1
- package/lib/generator.js +8 -16
- package/lib/generator.js.map +1 -1
- package/lib/index.js +6 -21
- package/lib/index.js.map +1 -1
- package/lib/model-loader.js +82 -3
- package/lib/model-loader.js.map +1 -1
- package/lib/node-ref.js +1 -2
- package/lib/node-ref.js.map +1 -1
- package/lib/prisma-field-builder.js +36 -40
- package/lib/prisma-field-builder.js.map +1 -1
- package/lib/schema-builder.js +25 -19
- package/lib/schema-builder.js.map +1 -1
- package/lib/util/cursors.js +100 -6
- package/lib/util/cursors.js.map +1 -1
- package/lib/util/datamodel.js +2 -4
- package/lib/util/datamodel.js.map +1 -1
- package/lib/util/get-client.js +1 -3
- package/lib/util/get-client.js.map +1 -1
- package/lib/util/loader-map.js +3 -5
- package/lib/util/loader-map.js.map +1 -1
- package/lib/util/map-query.js +47 -9
- package/lib/util/map-query.js.map +1 -1
- package/lib/util/relation-map.js +1 -2
- package/lib/util/relation-map.js.map +1 -1
- package/lib/util/selections.js +2 -4
- package/lib/util/selections.js.map +1 -1
- package/package.json +8 -8
- package/src/global-types.ts +3 -1
- package/src/index.ts +14 -4
- package/src/model-loader.ts +144 -3
- package/src/node-ref.ts +1 -1
- package/src/object-ref.ts +1 -1
- package/src/prisma-field-builder.ts +45 -26
- package/src/schema-builder.ts +25 -20
- package/src/types.ts +52 -10
- package/src/util/cursors.ts +117 -1
- package/src/util/get-client.ts +2 -1
- package/src/util/loader-map.ts +3 -9
- package/src/util/map-query.ts +66 -3
- package/tsconfig.type.tsbuildinfo +1 -1
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
FieldKind,
|
|
6
6
|
FieldRef,
|
|
7
7
|
InputFieldMap,
|
|
8
|
+
isThenable,
|
|
8
9
|
MaybePromise,
|
|
9
10
|
NormalizeArgs,
|
|
10
11
|
ObjectRef,
|
|
@@ -136,18 +137,29 @@ export class PrismaObjectFieldBuilder<
|
|
|
136
137
|
const relationSelect = (
|
|
137
138
|
args: object,
|
|
138
139
|
context: object,
|
|
139
|
-
nestedQuery: (query: unknown) => unknown,
|
|
140
|
+
nestedQuery: (query: unknown, path: unknown) => unknown,
|
|
140
141
|
) => ({
|
|
141
142
|
select: {
|
|
142
|
-
[name]: nestedQuery(
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
143
|
+
[name]: nestedQuery(
|
|
144
|
+
{
|
|
145
|
+
...((typeof query === 'function' ? query(args, context) : query) as {}),
|
|
146
|
+
...prismaCursorConnectionQuery({
|
|
147
|
+
parseCursor,
|
|
148
|
+
maxSize,
|
|
149
|
+
defaultSize,
|
|
150
|
+
args,
|
|
151
|
+
}),
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
getType: () => {
|
|
155
|
+
if (!typeName) {
|
|
156
|
+
typeName = this.builder.configStore.getTypeConfig(ref).name;
|
|
157
|
+
}
|
|
158
|
+
return typeName;
|
|
159
|
+
},
|
|
160
|
+
path: [{ name: 'edges' }, { name: 'node' }],
|
|
161
|
+
},
|
|
162
|
+
),
|
|
151
163
|
},
|
|
152
164
|
});
|
|
153
165
|
|
|
@@ -215,18 +227,6 @@ export class PrismaObjectFieldBuilder<
|
|
|
215
227
|
...(connectionOptions as { fields?: (t: unknown) => {} }).fields?.(t),
|
|
216
228
|
})
|
|
217
229
|
: (connectionOptions as { fields: undefined }).fields,
|
|
218
|
-
extensions: {
|
|
219
|
-
...(connectionOptions as Record<string, {}> | undefined)?.extensions,
|
|
220
|
-
pothosPrismaIndirectInclude: {
|
|
221
|
-
getType: () => {
|
|
222
|
-
if (!typeName) {
|
|
223
|
-
typeName = this.builder.configStore.getTypeConfig(ref).name;
|
|
224
|
-
}
|
|
225
|
-
return typeName;
|
|
226
|
-
},
|
|
227
|
-
path: [{ name: 'edges' }, { name: 'node' }],
|
|
228
|
-
},
|
|
229
|
-
},
|
|
230
230
|
},
|
|
231
231
|
edgeOptions,
|
|
232
232
|
);
|
|
@@ -327,19 +327,26 @@ export class PrismaObjectFieldBuilder<
|
|
|
327
327
|
}) as FieldRef<number, 'Object'>;
|
|
328
328
|
}
|
|
329
329
|
|
|
330
|
-
variant<
|
|
330
|
+
variant<
|
|
331
|
+
Variant extends Model['Name'] | PrismaObjectRef<Model>,
|
|
332
|
+
Args extends InputFieldMap,
|
|
333
|
+
Nullable,
|
|
334
|
+
>(
|
|
331
335
|
...allArgs: NormalizeArgs<
|
|
332
336
|
[
|
|
333
337
|
variant: Variant,
|
|
334
338
|
options?: VariantFieldOptions<
|
|
335
339
|
Types,
|
|
336
340
|
Model,
|
|
337
|
-
Variant extends PrismaObjectRef<Model> ? Variant : PrismaObjectRef<Model
|
|
341
|
+
Variant extends PrismaObjectRef<Model> ? Variant : PrismaObjectRef<Model>,
|
|
342
|
+
Args,
|
|
343
|
+
Nullable,
|
|
344
|
+
Shape
|
|
338
345
|
>,
|
|
339
346
|
]
|
|
340
347
|
>
|
|
341
348
|
): FieldRef<Model['Shape'], 'Object'> {
|
|
342
|
-
const [variant, options = {} as never] = allArgs;
|
|
349
|
+
const [variant, { isNull, nullable, ...options } = {} as never] = allArgs;
|
|
343
350
|
const ref: PrismaObjectRef<PrismaModelTypes> =
|
|
344
351
|
typeof variant === 'string' ? getRefFromModel(variant, this.builder) : variant;
|
|
345
352
|
|
|
@@ -353,7 +360,19 @@ export class PrismaObjectFieldBuilder<
|
|
|
353
360
|
...options?.extensions,
|
|
354
361
|
pothosPrismaSelect: selfSelect,
|
|
355
362
|
},
|
|
356
|
-
|
|
363
|
+
nullable: nullable ?? !!isNull,
|
|
364
|
+
resolve: isNull
|
|
365
|
+
? (parent, args, context, info) => {
|
|
366
|
+
const parentIsNull = isNull(parent, args as never, context, info);
|
|
367
|
+
if (parentIsNull) {
|
|
368
|
+
if (isThenable(parentIsNull)) {
|
|
369
|
+
return parentIsNull.then((resolved) => (resolved ? null : parent)) as never;
|
|
370
|
+
}
|
|
371
|
+
return null as never;
|
|
372
|
+
}
|
|
373
|
+
return parent as never;
|
|
374
|
+
}
|
|
375
|
+
: (parent) => parent as never,
|
|
357
376
|
}) as FieldRef<Model['Shape'], 'Object'>;
|
|
358
377
|
}
|
|
359
378
|
|
package/src/schema-builder.ts
CHANGED
|
@@ -11,6 +11,7 @@ import { PrismaObjectFieldBuilder } from './field-builder';
|
|
|
11
11
|
import { ModelLoader } from './model-loader';
|
|
12
12
|
import PrismaNodeRef from './node-ref';
|
|
13
13
|
import { PrismaModelTypes, PrismaNodeOptions } from './types';
|
|
14
|
+
import { getDefaultIDParser, getDefaultIDSerializer } from './util/cursors';
|
|
14
15
|
import { getDelegateFromModel, getRefFromModel } from './util/datamodel';
|
|
15
16
|
import { getClient, getDMMF } from './util/get-client';
|
|
16
17
|
import { queryFromInfo } from './util/map-query';
|
|
@@ -18,10 +19,14 @@ import { getRelationMap } from './util/relation-map';
|
|
|
18
19
|
|
|
19
20
|
const schemaBuilderProto = SchemaBuilder.prototype as PothosSchemaTypes.SchemaBuilder<SchemaTypes>;
|
|
20
21
|
|
|
21
|
-
schemaBuilderProto.prismaObject = function prismaObject(
|
|
22
|
+
schemaBuilderProto.prismaObject = function prismaObject(
|
|
23
|
+
type,
|
|
24
|
+
{ fields, findUnique, select, include, ...options },
|
|
25
|
+
) {
|
|
22
26
|
const ref = options.variant ? this.objectRef(options.variant) : getRefFromModel(type, this);
|
|
23
27
|
const name = options.variant ?? options.name ?? type;
|
|
24
28
|
const fieldMap = getRelationMap(getDMMF(this)).get(type)!;
|
|
29
|
+
const idSelection = ModelLoader.getDefaultIDSelection(ref, type, this);
|
|
25
30
|
|
|
26
31
|
ref.name = name;
|
|
27
32
|
|
|
@@ -29,18 +34,11 @@ schemaBuilderProto.prismaObject = function prismaObject(type, { fields, findUniq
|
|
|
29
34
|
...(options as {}),
|
|
30
35
|
extensions: {
|
|
31
36
|
...options.extensions,
|
|
32
|
-
pothosPrismaInclude:
|
|
37
|
+
pothosPrismaInclude: include,
|
|
33
38
|
pothosPrismaModel: type,
|
|
34
39
|
pothosPrismaFieldMap: fieldMap,
|
|
35
|
-
pothosPrismaSelect:
|
|
36
|
-
pothosPrismaLoader: ModelLoader.forRef(
|
|
37
|
-
type,
|
|
38
|
-
(findUnique as never) ||
|
|
39
|
-
(() => {
|
|
40
|
-
throw new Error(`Missing findUnique for ${ref.name}`);
|
|
41
|
-
}),
|
|
42
|
-
this,
|
|
43
|
-
),
|
|
40
|
+
pothosPrismaSelect: select && { ...idSelection, ...(select as {}) },
|
|
41
|
+
pothosPrismaLoader: ModelLoader.forRef(ref, type, findUnique as never, this),
|
|
44
42
|
},
|
|
45
43
|
name,
|
|
46
44
|
fields: fields
|
|
@@ -65,26 +63,33 @@ schemaBuilderProto.prismaNode = function prismaNode(
|
|
|
65
63
|
},
|
|
66
64
|
type: keyof SchemaTypes['PrismaTypes'],
|
|
67
65
|
{
|
|
68
|
-
|
|
66
|
+
id: { field, resolve: rawResolve, ...idOptions },
|
|
67
|
+
findUnique: rawFindUnique,
|
|
69
68
|
name,
|
|
70
69
|
variant,
|
|
71
70
|
...options
|
|
72
|
-
}: PrismaNodeOptions<SchemaTypes, PrismaModelTypes, [], never, {}, {}>,
|
|
71
|
+
}: PrismaNodeOptions<SchemaTypes, PrismaModelTypes, [], never, {}, {}, undefined>,
|
|
73
72
|
) {
|
|
73
|
+
const fieldName = field as unknown as string;
|
|
74
74
|
const interfaceRef = this.nodeInterfaceRef?.();
|
|
75
|
+
const resolve = rawResolve ?? getDefaultIDSerializer(type, fieldName, this);
|
|
76
|
+
const idParser = fieldName ? getDefaultIDParser(type, fieldName, this) : undefined;
|
|
77
|
+
const typeName = variant ?? name ?? type;
|
|
78
|
+
const nodeRef = new PrismaNodeRef(typeName);
|
|
79
|
+
const findUnique = rawFindUnique
|
|
80
|
+
? (parent: unknown, context: {}) =>
|
|
81
|
+
rawFindUnique(resolve(parent as never, context) as string, context)
|
|
82
|
+
: ModelLoader.getFindUniqueForField(nodeRef, type, fieldName, this);
|
|
75
83
|
|
|
76
84
|
if (!interfaceRef) {
|
|
77
85
|
throw new TypeError('builder.prismaNode requires @pothos/plugin-relay to be installed');
|
|
78
86
|
}
|
|
79
87
|
|
|
80
|
-
const typeName = variant ?? name ?? type;
|
|
81
|
-
const nodeRef = new PrismaNodeRef(typeName);
|
|
82
88
|
const extendedOptions = {
|
|
83
89
|
...options,
|
|
84
90
|
variant,
|
|
85
91
|
interfaces: [interfaceRef],
|
|
86
|
-
findUnique
|
|
87
|
-
findUnique(options.id.resolve(parent as never, context) as string, context),
|
|
92
|
+
findUnique,
|
|
88
93
|
loadWithoutCache: async (
|
|
89
94
|
id: string,
|
|
90
95
|
context: SchemaTypes['Context'],
|
|
@@ -95,7 +100,7 @@ schemaBuilderProto.prismaNode = function prismaNode(
|
|
|
95
100
|
const record = await delegate.findUnique({
|
|
96
101
|
...query,
|
|
97
102
|
rejectOnNotFound: true,
|
|
98
|
-
where:
|
|
103
|
+
where: rawFindUnique ? rawFindUnique(id, context) : { [fieldName]: idParser!(id) },
|
|
99
104
|
} as never);
|
|
100
105
|
|
|
101
106
|
brandWithType(record, typeName as OutputType<SchemaTypes>);
|
|
@@ -117,7 +122,7 @@ schemaBuilderProto.prismaNode = function prismaNode(
|
|
|
117
122
|
globalID: (options: Record<string, unknown>) => FieldRef<unknown>;
|
|
118
123
|
}
|
|
119
124
|
).globalID({
|
|
120
|
-
...
|
|
125
|
+
...idOptions,
|
|
121
126
|
nullable: false,
|
|
122
127
|
args: {},
|
|
123
128
|
resolve: async (
|
|
@@ -127,7 +132,7 @@ schemaBuilderProto.prismaNode = function prismaNode(
|
|
|
127
132
|
info: GraphQLResolveInfo,
|
|
128
133
|
) => ({
|
|
129
134
|
type: nodeConfig.name,
|
|
130
|
-
id: await
|
|
135
|
+
id: await resolve(parent, context),
|
|
131
136
|
}),
|
|
132
137
|
}),
|
|
133
138
|
);
|
package/src/types.ts
CHANGED
|
@@ -89,6 +89,10 @@ export type PrismaObjectFieldOptions<
|
|
|
89
89
|
| ((
|
|
90
90
|
args: InputShapeFromFields<Args>,
|
|
91
91
|
ctx: Types['Context'],
|
|
92
|
+
nestedSelection: <Selection extends boolean | {}>(
|
|
93
|
+
selection?: Selection,
|
|
94
|
+
path?: string[],
|
|
95
|
+
) => Selection,
|
|
92
96
|
) => ExtractModel<Types, ParentShape>['Select'])
|
|
93
97
|
);
|
|
94
98
|
};
|
|
@@ -169,13 +173,13 @@ export type PrismaObjectTypeOptions<
|
|
|
169
173
|
| {
|
|
170
174
|
include?: Include & Model['Include'];
|
|
171
175
|
select?: never;
|
|
172
|
-
findUnique
|
|
176
|
+
findUnique?: FindUnique &
|
|
173
177
|
(((parent: Shape, context: Types['Context']) => Model['Where']) | null);
|
|
174
178
|
}
|
|
175
179
|
| {
|
|
176
180
|
select: Model['Select'] & Select;
|
|
177
181
|
include?: never;
|
|
178
|
-
findUnique
|
|
182
|
+
findUnique?: (parent: Shape, context: Types['Context']) => Model['Where'];
|
|
179
183
|
}
|
|
180
184
|
);
|
|
181
185
|
|
|
@@ -196,6 +200,7 @@ export type PrismaNodeOptions<
|
|
|
196
200
|
Include,
|
|
197
201
|
Select,
|
|
198
202
|
Shape extends object,
|
|
203
|
+
UniqueField,
|
|
199
204
|
> = NameOrVariant &
|
|
200
205
|
Omit<
|
|
201
206
|
| PothosSchemaTypes.ObjectTypeOptions<Types, Shape>
|
|
@@ -214,9 +219,20 @@ export type PrismaNodeOptions<
|
|
|
214
219
|
MaybePromise<OutputShape<Types, 'ID'>>
|
|
215
220
|
>,
|
|
216
221
|
'args' | 'nullable' | 'resolve' | 'type'
|
|
217
|
-
> &
|
|
218
|
-
|
|
219
|
-
|
|
222
|
+
> &
|
|
223
|
+
(
|
|
224
|
+
| {
|
|
225
|
+
field?: never;
|
|
226
|
+
resolve: (
|
|
227
|
+
parent: Shape,
|
|
228
|
+
context: Types['Context'],
|
|
229
|
+
) => MaybePromise<OutputShape<Types, 'ID'>>;
|
|
230
|
+
}
|
|
231
|
+
| {
|
|
232
|
+
resolve?: never;
|
|
233
|
+
field: UniqueField extends keyof Model['Where'] ? UniqueField : keyof Model['Where'];
|
|
234
|
+
}
|
|
235
|
+
);
|
|
220
236
|
fields?: PrismaObjectFieldsShape<
|
|
221
237
|
Types,
|
|
222
238
|
Model,
|
|
@@ -224,8 +240,14 @@ export type PrismaNodeOptions<
|
|
|
224
240
|
Shape & { [prismaModelName]?: Model['Name'] },
|
|
225
241
|
Select
|
|
226
242
|
>;
|
|
227
|
-
|
|
228
|
-
|
|
243
|
+
} & (UniqueField extends string
|
|
244
|
+
? {
|
|
245
|
+
findUnique?: (id: string, context: Types['Context']) => Model['Where'];
|
|
246
|
+
}
|
|
247
|
+
: {
|
|
248
|
+
findUnique: (id: string, context: Types['Context']) => Model['Where'];
|
|
249
|
+
}) &
|
|
250
|
+
(
|
|
229
251
|
| {
|
|
230
252
|
include?: Include & Model['Include'];
|
|
231
253
|
select?: never;
|
|
@@ -330,10 +352,28 @@ export type VariantFieldOptions<
|
|
|
330
352
|
Types extends SchemaTypes,
|
|
331
353
|
Model extends PrismaModelTypes,
|
|
332
354
|
Variant extends PrismaObjectRef<Model>,
|
|
355
|
+
Args extends InputFieldMap,
|
|
356
|
+
isNull,
|
|
357
|
+
Shape,
|
|
333
358
|
> = Omit<
|
|
334
|
-
PothosSchemaTypes.ObjectFieldOptions<
|
|
359
|
+
PothosSchemaTypes.ObjectFieldOptions<
|
|
360
|
+
Types,
|
|
361
|
+
Shape,
|
|
362
|
+
Variant,
|
|
363
|
+
unknown extends isNull ? false : true,
|
|
364
|
+
Args,
|
|
365
|
+
Model['Shape']
|
|
366
|
+
>,
|
|
335
367
|
'resolve' | 'type'
|
|
336
|
-
|
|
368
|
+
> & {
|
|
369
|
+
isNull?: isNull &
|
|
370
|
+
((
|
|
371
|
+
parent: Shape,
|
|
372
|
+
args: InputShapeFromFields<Args>,
|
|
373
|
+
context: Types['Context'],
|
|
374
|
+
info: GraphQLResolveInfo,
|
|
375
|
+
) => MaybePromise<boolean>);
|
|
376
|
+
};
|
|
337
377
|
|
|
338
378
|
export type RelationCountOptions<
|
|
339
379
|
Types extends SchemaTypes,
|
|
@@ -546,8 +586,9 @@ export type FieldSelection =
|
|
|
546
586
|
| ((
|
|
547
587
|
args: object,
|
|
548
588
|
context: object,
|
|
549
|
-
|
|
589
|
+
mergeNestedSelection: (
|
|
550
590
|
selection: SelectionMap | boolean | ((args: object, context: object) => SelectionMap),
|
|
591
|
+
path?: string[] | IndirectInclude,
|
|
551
592
|
) => SelectionMap | boolean,
|
|
552
593
|
) => SelectionMap);
|
|
553
594
|
|
|
@@ -555,6 +596,7 @@ export type LoaderMappings = Record<
|
|
|
555
596
|
string,
|
|
556
597
|
{
|
|
557
598
|
field: string;
|
|
599
|
+
type: string;
|
|
558
600
|
mappings: LoaderMappings;
|
|
559
601
|
indirectPath: string[];
|
|
560
602
|
}
|
package/src/util/cursors.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/* eslint-disable no-nested-ternary */
|
|
2
|
-
import { MaybePromise } from '@pothos/core';
|
|
2
|
+
import { MaybePromise, SchemaTypes } from '@pothos/core';
|
|
3
|
+
import { getModel } from './datamodel';
|
|
4
|
+
import { DMMFField } from './get-client';
|
|
3
5
|
|
|
4
6
|
const DEFAULT_MAX_SIZE = 100;
|
|
5
7
|
const DEFAULT_SIZE = 20;
|
|
@@ -57,6 +59,120 @@ export function parseRawCursor(cursor: unknown) {
|
|
|
57
59
|
}
|
|
58
60
|
}
|
|
59
61
|
|
|
62
|
+
export function parseID(id: string, dataType: string): unknown {
|
|
63
|
+
if (!id) {
|
|
64
|
+
return id;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
switch (dataType) {
|
|
68
|
+
case 'String':
|
|
69
|
+
return id;
|
|
70
|
+
case 'Int':
|
|
71
|
+
return Number.parseInt(id, 10);
|
|
72
|
+
case 'BigInt':
|
|
73
|
+
// eslint-disable-next-line node/no-unsupported-features/es-builtins
|
|
74
|
+
return BigInt(id);
|
|
75
|
+
case 'Boolean':
|
|
76
|
+
return id !== 'false';
|
|
77
|
+
case 'Float':
|
|
78
|
+
case 'Decimal':
|
|
79
|
+
return Number.parseFloat(id);
|
|
80
|
+
case 'DateTime':
|
|
81
|
+
return new Date(id);
|
|
82
|
+
case 'Json':
|
|
83
|
+
return JSON.parse(id) as unknown;
|
|
84
|
+
case 'Byte':
|
|
85
|
+
return Buffer.from(id, 'base64');
|
|
86
|
+
default:
|
|
87
|
+
return id;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function getDefaultIDSerializer<Types extends SchemaTypes>(
|
|
92
|
+
modelName: string,
|
|
93
|
+
fieldName: string,
|
|
94
|
+
builder: PothosSchemaTypes.SchemaBuilder<Types>,
|
|
95
|
+
): (parent: Record<string, unknown>) => unknown {
|
|
96
|
+
const model = getModel(modelName, builder);
|
|
97
|
+
|
|
98
|
+
const field = model.fields.find((f) => f.name === fieldName);
|
|
99
|
+
|
|
100
|
+
if (field) {
|
|
101
|
+
return (parent) => serializeID(parent[fieldName], field.type);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if ((model.primaryKey?.name ?? model.primaryKey?.fields.join('_')) === fieldName) {
|
|
105
|
+
const fields = model.primaryKey!.fields.map((n) => model.fields.find((f) => f.name === n)!);
|
|
106
|
+
return (parent) => JSON.stringify(fields.map((f) => serializeID(parent[f.name], f.kind)));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const index = model.uniqueIndexes.find((idx) => (idx.name ?? idx.fields.join('_')) === fieldName);
|
|
110
|
+
|
|
111
|
+
if (index) {
|
|
112
|
+
const fields = index.fields.map((n) => model.fields.find((f) => f.name === n)!);
|
|
113
|
+
return (parent) => JSON.stringify(fields.map((f) => serializeID(parent[f.name], f.kind)));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
throw new Error(`Unable to find ${fieldName} for model ${modelName}`);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function getDefaultIDParser<Types extends SchemaTypes>(
|
|
120
|
+
modelName: string,
|
|
121
|
+
fieldName: string,
|
|
122
|
+
builder: PothosSchemaTypes.SchemaBuilder<Types>,
|
|
123
|
+
): (id: string) => unknown {
|
|
124
|
+
if (!fieldName) {
|
|
125
|
+
throw new Error('Missing field name');
|
|
126
|
+
}
|
|
127
|
+
const model = getModel(modelName, builder);
|
|
128
|
+
|
|
129
|
+
const field = model.fields.find((f) => f.name === fieldName);
|
|
130
|
+
|
|
131
|
+
if (field) {
|
|
132
|
+
return (id) => parseID(id, field.type);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const index = model.uniqueIndexes.find((idx) => (idx.name ?? idx.fields.join('_')) === fieldName);
|
|
136
|
+
|
|
137
|
+
let fields: DMMFField[] | undefined;
|
|
138
|
+
if ((model.primaryKey?.name ?? model.primaryKey?.fields.join('_')) === fieldName) {
|
|
139
|
+
fields = model.primaryKey!.fields.map((n) => model.fields.find((f) => f.name === n)!);
|
|
140
|
+
} else if (index) {
|
|
141
|
+
fields = index.fields.map((n) => model.fields.find((f) => f.name === n)!);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (!fields) {
|
|
145
|
+
throw new Error(`Unable to find ${fieldName} for model ${modelName}`);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return (id) => {
|
|
149
|
+
const parts = JSON.parse(id) as unknown;
|
|
150
|
+
|
|
151
|
+
if (!Array.isArray(parts)) {
|
|
152
|
+
throw new TypeError(`Invalid id received for ${fieldName} of ${modelName}`);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const result: Record<string, unknown> = {};
|
|
156
|
+
|
|
157
|
+
for (let i = 0; i < fields!.length; i += 1) {
|
|
158
|
+
result[fields![i].name] = parseID(parts[i] as string, fields![i].type);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return result;
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export function serializeID(id: unknown, dataType: string) {
|
|
166
|
+
switch (dataType) {
|
|
167
|
+
case 'Json':
|
|
168
|
+
return JSON.stringify(id);
|
|
169
|
+
case 'Byte':
|
|
170
|
+
return (id as Buffer).toString('base64');
|
|
171
|
+
default:
|
|
172
|
+
return String(id);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
60
176
|
export function parseCompositeCursor(fields: string[]) {
|
|
61
177
|
return (cursor: string) => {
|
|
62
178
|
const parsed = parseRawCursor(cursor) as unknown[];
|
package/src/util/get-client.ts
CHANGED
|
@@ -20,7 +20,8 @@ interface DMMF {
|
|
|
20
20
|
name: string;
|
|
21
21
|
fields: DMMFField[];
|
|
22
22
|
fieldMap?: Record<string, DMMFField>;
|
|
23
|
-
primaryKey: { name: string; fields: string[] } | null;
|
|
23
|
+
primaryKey: { name: string | null; fields: string[] } | null;
|
|
24
|
+
uniqueIndexes: { name: string | null; fields: string[] }[];
|
|
24
25
|
}
|
|
25
26
|
>;
|
|
26
27
|
}
|
package/src/util/loader-map.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { GraphQLResolveInfo } from 'graphql';
|
|
2
2
|
import { createContextCache } from '@pothos/core';
|
|
3
3
|
import { LoaderMappings } from '../types';
|
|
4
|
-
import { getIndirectType } from './map-query';
|
|
5
4
|
|
|
6
5
|
const cache = createContextCache((ctx) => new Map<string, LoaderMappings>());
|
|
7
6
|
|
|
@@ -23,18 +22,13 @@ export function cacheKey(type: string, path: GraphQLResolveInfo['path'], subPath
|
|
|
23
22
|
return `${type}@${key}`;
|
|
24
23
|
}
|
|
25
24
|
|
|
26
|
-
export function setLoaderMappings(
|
|
27
|
-
ctx: object,
|
|
28
|
-
info: GraphQLResolveInfo,
|
|
29
|
-
value: LoaderMappings,
|
|
30
|
-
type: GraphQLNamedType,
|
|
31
|
-
) {
|
|
25
|
+
export function setLoaderMappings(ctx: object, info: GraphQLResolveInfo, value: LoaderMappings) {
|
|
32
26
|
Object.keys(value).forEach((field) => {
|
|
33
27
|
const map = cache(ctx);
|
|
34
28
|
|
|
35
29
|
const mapping = value[field];
|
|
36
30
|
const subPath = [...mapping.indirectPath, field];
|
|
37
|
-
const key = cacheKey(
|
|
31
|
+
const key = cacheKey(mapping.type, info.path, subPath);
|
|
38
32
|
|
|
39
33
|
map.set(key, mapping.mappings);
|
|
40
34
|
});
|
package/src/util/map-query.ts
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
FieldNode,
|
|
5
5
|
FragmentDefinitionNode,
|
|
6
6
|
getNamedType,
|
|
7
|
+
GraphQLField,
|
|
7
8
|
GraphQLNamedType,
|
|
8
9
|
GraphQLObjectType,
|
|
9
10
|
GraphQLResolveInfo,
|
|
@@ -232,16 +233,45 @@ function addFieldSelection(
|
|
|
232
233
|
unknown
|
|
233
234
|
>;
|
|
234
235
|
|
|
235
|
-
fieldSelectionMap = fieldSelect(args, context, (rawQuery) => {
|
|
236
|
+
fieldSelectionMap = fieldSelect(args, context, (rawQuery, indirectInclude) => {
|
|
236
237
|
const returnType = getNamedType(field.type);
|
|
237
238
|
const query = typeof rawQuery === 'function' ? rawQuery(args, context) : rawQuery;
|
|
238
239
|
|
|
239
|
-
const
|
|
240
|
+
const normalizedIndirectInclude = Array.isArray(indirectInclude)
|
|
241
|
+
? normalizeInclude(indirectInclude, getIndirectType(returnType, info))
|
|
242
|
+
: indirectInclude;
|
|
243
|
+
|
|
244
|
+
const fieldState = createStateForType(
|
|
245
|
+
getIndirectType(
|
|
246
|
+
normalizedIndirectInclude
|
|
247
|
+
? info.schema.getType(normalizedIndirectInclude.getType())!
|
|
248
|
+
: returnType,
|
|
249
|
+
info,
|
|
250
|
+
),
|
|
251
|
+
info,
|
|
252
|
+
state,
|
|
253
|
+
);
|
|
240
254
|
|
|
241
255
|
if (typeof query === 'object' && Object.keys(query).length > 0) {
|
|
242
256
|
mergeSelection(fieldState, { select: {}, ...query });
|
|
243
257
|
}
|
|
244
258
|
|
|
259
|
+
if (normalizedIndirectInclude && normalizedIndirectInclude.path.length > 0) {
|
|
260
|
+
resolveIndirectInclude(
|
|
261
|
+
returnType,
|
|
262
|
+
info,
|
|
263
|
+
selection,
|
|
264
|
+
[
|
|
265
|
+
...((returnType.extensions?.pothosPrismaIndirectInclude as { path: [] })?.path ?? []),
|
|
266
|
+
...normalizedIndirectInclude.path,
|
|
267
|
+
],
|
|
268
|
+
[],
|
|
269
|
+
(resolvedType, resolvedField, path) => {
|
|
270
|
+
addTypeSelectionsForField(resolvedType, context, info, fieldState, resolvedField, path);
|
|
271
|
+
},
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
|
|
245
275
|
addTypeSelectionsForField(returnType, context, info, fieldState, selection, []);
|
|
246
276
|
|
|
247
277
|
// eslint-disable-next-line prefer-destructuring
|
|
@@ -257,6 +287,7 @@ function addFieldSelection(
|
|
|
257
287
|
mergeSelection(state, fieldSelectionMap);
|
|
258
288
|
state.mappings[selection.alias?.value ?? selection.name.value] = {
|
|
259
289
|
field: selection.name.value,
|
|
290
|
+
type: type.name,
|
|
260
291
|
mappings,
|
|
261
292
|
indirectPath,
|
|
262
293
|
};
|
|
@@ -268,6 +299,7 @@ function addFieldSelection(
|
|
|
268
299
|
mergeSelection(state.parent, { select: fieldParentSelect });
|
|
269
300
|
state.mappings[selection.alias?.value ?? selection.name.value] = {
|
|
270
301
|
field: selection.name.value,
|
|
302
|
+
type: type.name,
|
|
271
303
|
mappings,
|
|
272
304
|
indirectPath,
|
|
273
305
|
};
|
|
@@ -280,7 +312,7 @@ export function queryFromInfo(context: object, info: GraphQLResolveInfo, typeNam
|
|
|
280
312
|
|
|
281
313
|
addTypeSelectionsForField(type, context, info, state, info.fieldNodes[0], []);
|
|
282
314
|
|
|
283
|
-
setLoaderMappings(context, info, state.mappings
|
|
315
|
+
setLoaderMappings(context, info, state.mappings);
|
|
284
316
|
|
|
285
317
|
return selectionToQuery(state);
|
|
286
318
|
}
|
|
@@ -330,3 +362,34 @@ export function getIndirectType(type: GraphQLNamedType, info: GraphQLResolveInfo
|
|
|
330
362
|
|
|
331
363
|
return targetType;
|
|
332
364
|
}
|
|
365
|
+
|
|
366
|
+
function normalizeInclude(path: string[], type: GraphQLNamedType) {
|
|
367
|
+
let currentType = type;
|
|
368
|
+
|
|
369
|
+
const normalized: { name: string; type: string }[] = [];
|
|
370
|
+
|
|
371
|
+
if (!isObjectType(currentType)) {
|
|
372
|
+
throw new Error(`Expected ${currentType} to be an Object type`);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
for (const fieldName of path) {
|
|
376
|
+
const field: GraphQLField<unknown, unknown> = currentType.getFields()[fieldName];
|
|
377
|
+
|
|
378
|
+
if (!field) {
|
|
379
|
+
throw new Error(`Expected ${currentType} to have a field ${fieldName}`);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
currentType = getNamedType(field.type);
|
|
383
|
+
|
|
384
|
+
if (!isObjectType(currentType)) {
|
|
385
|
+
throw new Error(`Expected ${currentType} to be an Object type`);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
normalized.push({ name: fieldName, type: currentType.name });
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
return {
|
|
392
|
+
getType: () => (normalized.length > 0 ? normalized[normalized.length - 1].type : type.name),
|
|
393
|
+
path: normalized,
|
|
394
|
+
};
|
|
395
|
+
}
|