@pothos/plugin-prisma 0.15.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/.turbo/turbo-build.log +17 -0
- package/.turbo/turbo-test.log +17 -0
- package/CHANGELOG.md +312 -0
- package/LICENSE +6 -0
- package/README.md +808 -0
- package/babel.config.js +3 -0
- package/bin/generator.js +2 -0
- package/esm/cursors.d.ts +55 -0
- package/esm/cursors.d.ts.map +1 -0
- package/esm/cursors.js +106 -0
- package/esm/cursors.js.map +1 -0
- package/esm/field-builder.d.ts +2 -0
- package/esm/field-builder.d.ts.map +1 -0
- package/esm/field-builder.js +51 -0
- package/esm/field-builder.js.map +1 -0
- package/esm/generator.d.ts +2 -0
- package/esm/generator.d.ts.map +1 -0
- package/esm/generator.js +82 -0
- package/esm/generator.js.map +1 -0
- package/esm/global-types.d.ts +52 -0
- package/esm/global-types.d.ts.map +1 -0
- package/esm/global-types.js +2 -0
- package/esm/global-types.js.map +1 -0
- package/esm/index.d.ts +11 -0
- package/esm/index.d.ts.map +1 -0
- package/esm/index.js +14 -0
- package/esm/index.js.map +1 -0
- package/esm/loader-map.d.ts +6 -0
- package/esm/loader-map.d.ts.map +1 -0
- package/esm/loader-map.js +35 -0
- package/esm/loader-map.js.map +1 -0
- package/esm/model-loader.d.ts +17 -0
- package/esm/model-loader.d.ts.map +1 -0
- package/esm/model-loader.js +89 -0
- package/esm/model-loader.js.map +1 -0
- package/esm/node-ref.d.ts +10 -0
- package/esm/node-ref.d.ts.map +1 -0
- package/esm/node-ref.js +18 -0
- package/esm/node-ref.js.map +1 -0
- package/esm/package.json +3 -0
- package/esm/prisma-field-builder.d.ts +22 -0
- package/esm/prisma-field-builder.d.ts.map +1 -0
- package/esm/prisma-field-builder.js +178 -0
- package/esm/prisma-field-builder.js.map +1 -0
- package/esm/refs.d.ts +13 -0
- package/esm/refs.d.ts.map +1 -0
- package/esm/refs.js +63 -0
- package/esm/refs.js.map +1 -0
- package/esm/schema-builder.d.ts +2 -0
- package/esm/schema-builder.d.ts.map +1 -0
- package/esm/schema-builder.js +69 -0
- package/esm/schema-builder.js.map +1 -0
- package/esm/types.d.ts +164 -0
- package/esm/types.d.ts.map +1 -0
- package/esm/types.js +2 -0
- package/esm/types.js.map +1 -0
- package/esm/util/index.d.ts +5 -0
- package/esm/util/index.d.ts.map +1 -0
- package/esm/util/index.js +16 -0
- package/esm/util/index.js.map +1 -0
- package/esm/util/map-includes.d.ts +5 -0
- package/esm/util/map-includes.d.ts.map +1 -0
- package/esm/util/map-includes.js +168 -0
- package/esm/util/map-includes.js.map +1 -0
- package/esm/util/merge-includes.d.ts +3 -0
- package/esm/util/merge-includes.d.ts.map +1 -0
- package/esm/util/merge-includes.js +91 -0
- package/esm/util/merge-includes.js.map +1 -0
- package/generated.ts +54 -0
- package/lib/cursors.d.ts +55 -0
- package/lib/cursors.d.ts.map +1 -0
- package/lib/cursors.js +112 -0
- package/lib/cursors.js.map +1 -0
- package/lib/field-builder.d.ts +2 -0
- package/lib/field-builder.d.ts.map +1 -0
- package/lib/field-builder.js +63 -0
- package/lib/field-builder.js.map +1 -0
- package/lib/generator.d.ts +2 -0
- package/lib/generator.d.ts.map +1 -0
- package/lib/generator.js +103 -0
- package/lib/generator.js.map +1 -0
- package/lib/global-types.d.ts +52 -0
- package/lib/global-types.d.ts.map +1 -0
- package/lib/global-types.js +3 -0
- package/lib/global-types.js.map +1 -0
- package/lib/index.d.ts +11 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +40 -0
- package/lib/index.js.map +1 -0
- package/lib/loader-map.d.ts +6 -0
- package/lib/loader-map.d.ts.map +1 -0
- package/lib/loader-map.js +41 -0
- package/lib/loader-map.js.map +1 -0
- package/lib/model-loader.d.ts +17 -0
- package/lib/model-loader.d.ts.map +1 -0
- package/lib/model-loader.js +93 -0
- package/lib/model-loader.js.map +1 -0
- package/lib/node-ref.d.ts +10 -0
- package/lib/node-ref.d.ts.map +1 -0
- package/lib/node-ref.js +21 -0
- package/lib/node-ref.js.map +1 -0
- package/lib/prisma-field-builder.d.ts +22 -0
- package/lib/prisma-field-builder.d.ts.map +1 -0
- package/lib/prisma-field-builder.js +182 -0
- package/lib/prisma-field-builder.js.map +1 -0
- package/lib/refs.d.ts +13 -0
- package/lib/refs.d.ts.map +1 -0
- package/lib/refs.js +72 -0
- package/lib/refs.js.map +1 -0
- package/lib/schema-builder.d.ts +2 -0
- package/lib/schema-builder.d.ts.map +1 -0
- package/lib/schema-builder.js +93 -0
- package/lib/schema-builder.js.map +1 -0
- package/lib/types.d.ts +164 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/types.js +4 -0
- package/lib/types.js.map +1 -0
- package/lib/util/index.d.ts +5 -0
- package/lib/util/index.d.ts.map +1 -0
- package/lib/util/index.js +30 -0
- package/lib/util/index.js.map +1 -0
- package/lib/util/map-includes.d.ts +5 -0
- package/lib/util/map-includes.d.ts.map +1 -0
- package/lib/util/map-includes.js +173 -0
- package/lib/util/map-includes.js.map +1 -0
- package/lib/util/merge-includes.d.ts +3 -0
- package/lib/util/merge-includes.d.ts.map +1 -0
- package/lib/util/merge-includes.js +96 -0
- package/lib/util/merge-includes.js.map +1 -0
- package/package.json +70 -0
- package/src/cursors.ts +159 -0
- package/src/field-builder.ts +115 -0
- package/src/generator.ts +184 -0
- package/src/global-types.ts +180 -0
- package/src/index.ts +18 -0
- package/src/loader-map.ts +48 -0
- package/src/model-loader.ts +135 -0
- package/src/node-ref.ts +29 -0
- package/src/prisma-field-builder.ts +324 -0
- package/src/refs.ts +110 -0
- package/src/schema-builder.ts +124 -0
- package/src/types.ts +470 -0
- package/src/util/index.ts +26 -0
- package/src/util/map-includes.ts +298 -0
- package/src/util/merge-includes.ts +121 -0
- package/tsconfig.json +21 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
/* eslint-disable no-underscore-dangle */
|
|
2
|
+
import { GraphQLResolveInfo } from 'graphql';
|
|
3
|
+
import {
|
|
4
|
+
FieldRef,
|
|
5
|
+
InputFieldMap,
|
|
6
|
+
MaybePromise,
|
|
7
|
+
NormalizeArgs,
|
|
8
|
+
ObjectFieldBuilder,
|
|
9
|
+
ObjectRef,
|
|
10
|
+
PluginName,
|
|
11
|
+
SchemaTypes,
|
|
12
|
+
} from '@pothos/core';
|
|
13
|
+
import { prismaCursorConnectionQuery, wrapConnectionResult } from './cursors';
|
|
14
|
+
import { getLoaderMapping, setLoaderMappings } from './loader-map';
|
|
15
|
+
import { ModelLoader } from './model-loader';
|
|
16
|
+
import { getDelegateFromModel, getFindUniqueForRef, getRefFromModel, getRelation } from './refs';
|
|
17
|
+
import {
|
|
18
|
+
PrismaDelegate,
|
|
19
|
+
PrismaModelTypes,
|
|
20
|
+
RelatedConnectionOptions,
|
|
21
|
+
RelatedFieldOptions,
|
|
22
|
+
RelationCountOptions,
|
|
23
|
+
} from './types';
|
|
24
|
+
import { queryFromInfo } from './util';
|
|
25
|
+
|
|
26
|
+
export class PrismaObjectFieldBuilder<
|
|
27
|
+
Types extends SchemaTypes,
|
|
28
|
+
Model extends PrismaModelTypes,
|
|
29
|
+
NeedsResolve extends boolean,
|
|
30
|
+
Shape extends object = Model['Shape'],
|
|
31
|
+
> extends ObjectFieldBuilder<Types, Shape> {
|
|
32
|
+
delegate: PrismaDelegate;
|
|
33
|
+
model: string;
|
|
34
|
+
|
|
35
|
+
relatedConnection: 'relay' extends PluginName
|
|
36
|
+
? <
|
|
37
|
+
Field extends Model['ListRelations'],
|
|
38
|
+
Nullable extends boolean,
|
|
39
|
+
Args extends InputFieldMap,
|
|
40
|
+
ResolveReturnShape,
|
|
41
|
+
>(
|
|
42
|
+
...args: NormalizeArgs<
|
|
43
|
+
[
|
|
44
|
+
field: Field,
|
|
45
|
+
options: RelatedConnectionOptions<Types, Model, Field, Nullable, Args, NeedsResolve>,
|
|
46
|
+
connectionOptions?: PothosSchemaTypes.ConnectionObjectOptions<
|
|
47
|
+
Types,
|
|
48
|
+
ObjectRef<Shape>,
|
|
49
|
+
ResolveReturnShape
|
|
50
|
+
>,
|
|
51
|
+
edgeOptions?: PothosSchemaTypes.ConnectionEdgeObjectOptions<
|
|
52
|
+
Types,
|
|
53
|
+
ObjectRef<Shape>,
|
|
54
|
+
ResolveReturnShape
|
|
55
|
+
>,
|
|
56
|
+
]
|
|
57
|
+
>
|
|
58
|
+
) => FieldRef<PothosSchemaTypes.ConnectionShapeHelper<Types, Shape, Nullable>['shape']>
|
|
59
|
+
: '@pothos/plugin-relay is required to use this method' = function relatedConnection(
|
|
60
|
+
this: PrismaObjectFieldBuilder<SchemaTypes, Model, boolean>,
|
|
61
|
+
name: string,
|
|
62
|
+
{
|
|
63
|
+
maxSize,
|
|
64
|
+
defaultSize,
|
|
65
|
+
cursor,
|
|
66
|
+
query,
|
|
67
|
+
resolve,
|
|
68
|
+
extensions,
|
|
69
|
+
totalCount,
|
|
70
|
+
...options
|
|
71
|
+
}: {
|
|
72
|
+
totalCount?: boolean;
|
|
73
|
+
maxSize?: number;
|
|
74
|
+
defaultSize?: number;
|
|
75
|
+
cursor: string;
|
|
76
|
+
extensions: {};
|
|
77
|
+
query: ((args: {}, ctx: {}) => {}) | {};
|
|
78
|
+
resolve: (query: {}, parent: unknown, args: {}, ctx: {}, info: {}) => MaybePromise<{}[]>;
|
|
79
|
+
},
|
|
80
|
+
connectionOptions = {},
|
|
81
|
+
edgeOptions = {},
|
|
82
|
+
) {
|
|
83
|
+
const relationField = getRelation(this.model, this.builder, name);
|
|
84
|
+
const parentRef = getRefFromModel(this.model, this.builder);
|
|
85
|
+
const relationTypeName =
|
|
86
|
+
typeof relationField.type === 'string' ? relationField.type : relationField.type.name;
|
|
87
|
+
const ref = getRefFromModel(relationTypeName, this.builder);
|
|
88
|
+
const findUnique = getFindUniqueForRef(parentRef, this.builder);
|
|
89
|
+
const loaderCache = ModelLoader.forModel(this.model, this.builder);
|
|
90
|
+
let typeName: string | undefined;
|
|
91
|
+
|
|
92
|
+
const getQuery = (args: PothosSchemaTypes.DefaultConnectionArguments, ctx: {}) => ({
|
|
93
|
+
...((typeof query === 'function' ? query(args, ctx) : query) as {}),
|
|
94
|
+
...prismaCursorConnectionQuery({
|
|
95
|
+
column: cursor,
|
|
96
|
+
maxSize,
|
|
97
|
+
defaultSize,
|
|
98
|
+
args,
|
|
99
|
+
}),
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
const fieldRef = (
|
|
103
|
+
this as unknown as {
|
|
104
|
+
connection: (...args: unknown[]) => FieldRef<unknown>;
|
|
105
|
+
}
|
|
106
|
+
).connection(
|
|
107
|
+
{
|
|
108
|
+
...options,
|
|
109
|
+
extensions: {
|
|
110
|
+
...extensions,
|
|
111
|
+
pothosPrismaQuery: getQuery,
|
|
112
|
+
pothosPrismaRelation: name,
|
|
113
|
+
},
|
|
114
|
+
type: ref,
|
|
115
|
+
resolve: async (
|
|
116
|
+
parent: object,
|
|
117
|
+
args: PothosSchemaTypes.DefaultConnectionArguments,
|
|
118
|
+
context: {},
|
|
119
|
+
info: GraphQLResolveInfo,
|
|
120
|
+
) => {
|
|
121
|
+
const connectionQuery = getQuery(args, context);
|
|
122
|
+
const getResult = () => {
|
|
123
|
+
const mapping = getLoaderMapping(context, info.path);
|
|
124
|
+
const loadedValue = (parent as Record<string, unknown>)[name];
|
|
125
|
+
|
|
126
|
+
if (
|
|
127
|
+
// if we attempted to load the relation, and its missing it will be null
|
|
128
|
+
// undefined means that the query was not constructed in a way that requested the relation
|
|
129
|
+
loadedValue !== undefined &&
|
|
130
|
+
mapping
|
|
131
|
+
) {
|
|
132
|
+
if (loadedValue !== null && loadedValue !== undefined) {
|
|
133
|
+
setLoaderMappings(context, info.path, mapping);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return loadedValue as {}[];
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (!resolve && !findUnique) {
|
|
140
|
+
throw new Error(`Missing findUnique for Prisma type ${this.model}`);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const mergedQuery = { ...queryFromInfo(context, info), ...connectionQuery };
|
|
144
|
+
|
|
145
|
+
if (resolve) {
|
|
146
|
+
return resolve(mergedQuery, parent, args, context, info);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return loaderCache(parent).loadRelation(name, mergedQuery, context) as Promise<{}[]>;
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
return wrapConnectionResult(
|
|
153
|
+
await getResult(),
|
|
154
|
+
args,
|
|
155
|
+
connectionQuery.take,
|
|
156
|
+
cursor,
|
|
157
|
+
(parent as { _count?: Record<string, number> })._count?.[name],
|
|
158
|
+
);
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
...connectionOptions,
|
|
163
|
+
fields: totalCount
|
|
164
|
+
? (t: PothosSchemaTypes.ObjectFieldBuilder<SchemaTypes, { totalCount?: number }>) => ({
|
|
165
|
+
totalCount: t.int({
|
|
166
|
+
extensions: {
|
|
167
|
+
pothosPrismaRelationCountForParent: name,
|
|
168
|
+
},
|
|
169
|
+
resolve: (parent, args, context) => {
|
|
170
|
+
const loadedValue = parent.totalCount;
|
|
171
|
+
|
|
172
|
+
if (loadedValue !== undefined) {
|
|
173
|
+
return loadedValue;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return loaderCache(parent).loadCount(name, context);
|
|
177
|
+
},
|
|
178
|
+
}),
|
|
179
|
+
...(connectionOptions as { fields?: (t: unknown) => {} }).fields?.(t),
|
|
180
|
+
})
|
|
181
|
+
: (connectionOptions as { fields: undefined }).fields,
|
|
182
|
+
extensions: {
|
|
183
|
+
...(connectionOptions as Record<string, {}> | undefined)?.extensions,
|
|
184
|
+
pothosPrismaIndirectInclude: {
|
|
185
|
+
getType: () => {
|
|
186
|
+
if (!typeName) {
|
|
187
|
+
typeName = this.builder.configStore.getTypeConfig(ref).name;
|
|
188
|
+
}
|
|
189
|
+
return typeName;
|
|
190
|
+
},
|
|
191
|
+
path: [{ name: 'edges' }, { name: 'node' }],
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
edgeOptions,
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
return fieldRef;
|
|
199
|
+
} as never;
|
|
200
|
+
|
|
201
|
+
constructor(name: string, builder: PothosSchemaTypes.SchemaBuilder<Types>, model: string) {
|
|
202
|
+
super(name, builder);
|
|
203
|
+
|
|
204
|
+
this.model = model;
|
|
205
|
+
this.delegate = getDelegateFromModel(builder.options.prisma.client, model);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
relation<
|
|
209
|
+
Field extends Model['Fields'],
|
|
210
|
+
Nullable extends boolean,
|
|
211
|
+
Args extends InputFieldMap,
|
|
212
|
+
ResolveReturnShape,
|
|
213
|
+
>(
|
|
214
|
+
...allArgs: NormalizeArgs<
|
|
215
|
+
[
|
|
216
|
+
name: Field,
|
|
217
|
+
options?: RelatedFieldOptions<
|
|
218
|
+
Types,
|
|
219
|
+
Model,
|
|
220
|
+
Field,
|
|
221
|
+
Nullable,
|
|
222
|
+
Args,
|
|
223
|
+
ResolveReturnShape,
|
|
224
|
+
NeedsResolve,
|
|
225
|
+
Shape
|
|
226
|
+
>,
|
|
227
|
+
]
|
|
228
|
+
>
|
|
229
|
+
): FieldRef<Model['Relations'][Field]['Shape'], 'Object'> {
|
|
230
|
+
const [name, options = {} as never] = allArgs;
|
|
231
|
+
const relationField = getRelation(this.model, this.builder, name);
|
|
232
|
+
const parentRef = getRefFromModel(this.model, this.builder);
|
|
233
|
+
const relationTypeName =
|
|
234
|
+
typeof relationField.type === 'string' ? relationField.type : relationField.type.name;
|
|
235
|
+
const ref = getRefFromModel(relationTypeName, this.builder);
|
|
236
|
+
const findUnique = getFindUniqueForRef(parentRef, this.builder);
|
|
237
|
+
const loaderCache = ModelLoader.forModel(this.model, this.builder);
|
|
238
|
+
|
|
239
|
+
const { query = {}, resolve, ...rest } = options;
|
|
240
|
+
|
|
241
|
+
return this.field({
|
|
242
|
+
...rest,
|
|
243
|
+
type: relationField.isList ? [ref] : ref,
|
|
244
|
+
extensions: {
|
|
245
|
+
...options.extensions,
|
|
246
|
+
pothosPrismaQuery: query,
|
|
247
|
+
pothosPrismaRelation: name,
|
|
248
|
+
},
|
|
249
|
+
resolve: (parent, args, context, info) => {
|
|
250
|
+
const mapping = getLoaderMapping(context, info.path);
|
|
251
|
+
|
|
252
|
+
const loadedValue = (parent as Record<string, unknown>)[name];
|
|
253
|
+
|
|
254
|
+
if (
|
|
255
|
+
// if we attempted to load the relation, and its missing it will be null
|
|
256
|
+
// undefined means that the query was not constructed in a way that requested the relation
|
|
257
|
+
loadedValue !== undefined &&
|
|
258
|
+
mapping
|
|
259
|
+
) {
|
|
260
|
+
if (loadedValue !== null && loadedValue !== undefined) {
|
|
261
|
+
setLoaderMappings(context, info.path, mapping);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return loadedValue as never;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const queryOptions = {
|
|
268
|
+
...((typeof query === 'function' ? query(args, context) : query) as {}),
|
|
269
|
+
...queryFromInfo(context, info),
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
if (resolve) {
|
|
273
|
+
return resolve(queryOptions, parent, args as never, context, info) as never;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if (!findUnique) {
|
|
277
|
+
throw new Error(`Missing findUnique for Prisma type ${this.model}`);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return loaderCache(parent).loadRelation(name, queryOptions, context) as never;
|
|
281
|
+
},
|
|
282
|
+
}) as FieldRef<Model['Relations'][Field]['Shape'], 'Object'>;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
relationCount<Field extends Model['Fields']>(
|
|
286
|
+
...allArgs: NormalizeArgs<
|
|
287
|
+
[name: Field, options?: RelationCountOptions<Types, Shape, NeedsResolve>]
|
|
288
|
+
>
|
|
289
|
+
): FieldRef<number, 'Object'> {
|
|
290
|
+
const [name, options = {} as never] = allArgs;
|
|
291
|
+
const parentRef = getRefFromModel(this.model, this.builder);
|
|
292
|
+
const findUnique = getFindUniqueForRef(parentRef, this.builder);
|
|
293
|
+
const loaderCache = ModelLoader.forModel(this.model, this.builder);
|
|
294
|
+
|
|
295
|
+
const { resolve, ...rest } = options;
|
|
296
|
+
|
|
297
|
+
return this.field({
|
|
298
|
+
...rest,
|
|
299
|
+
type: 'Int',
|
|
300
|
+
nullable: false,
|
|
301
|
+
extensions: {
|
|
302
|
+
...options.extensions,
|
|
303
|
+
pothosPrismaRelationCount: name,
|
|
304
|
+
},
|
|
305
|
+
resolve: (parent, args, context, info) => {
|
|
306
|
+
const loadedValue = (parent as { _count: Record<string, unknown> })._count?.[name];
|
|
307
|
+
|
|
308
|
+
if (loadedValue !== undefined) {
|
|
309
|
+
return loadedValue as never;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if (resolve) {
|
|
313
|
+
return resolve(parent, args, context, info) as never;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
if (!findUnique) {
|
|
317
|
+
throw new Error(`Missing findUnique for Prisma type ${this.model}`);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return loaderCache(parent).loadCount(name, context) as never;
|
|
321
|
+
},
|
|
322
|
+
}) as FieldRef<number, 'Object'>;
|
|
323
|
+
}
|
|
324
|
+
}
|
package/src/refs.ts
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { ObjectRef, SchemaTypes } from '@pothos/core';
|
|
2
|
+
import { Prisma } from '../tests/client';
|
|
3
|
+
import { PrismaDelegate } from './types';
|
|
4
|
+
|
|
5
|
+
export const refMap = new WeakMap<object, Map<string, ObjectRef<unknown>>>();
|
|
6
|
+
export const findUniqueMap = new WeakMap<
|
|
7
|
+
object,
|
|
8
|
+
Map<ObjectRef<unknown>, ((args: unknown, ctx: {}) => unknown) | null>
|
|
9
|
+
>();
|
|
10
|
+
|
|
11
|
+
export const includeForRefMap = new WeakMap<
|
|
12
|
+
object,
|
|
13
|
+
Map<ObjectRef<unknown>, Record<string, unknown> | null>
|
|
14
|
+
>();
|
|
15
|
+
|
|
16
|
+
export function getRefFromModel<Types extends SchemaTypes>(
|
|
17
|
+
name: string,
|
|
18
|
+
builder: PothosSchemaTypes.SchemaBuilder<Types>,
|
|
19
|
+
): ObjectRef<unknown> {
|
|
20
|
+
if (!refMap.has(builder)) {
|
|
21
|
+
refMap.set(builder, new Map());
|
|
22
|
+
}
|
|
23
|
+
const cache = refMap.get(builder)!;
|
|
24
|
+
|
|
25
|
+
if (!cache.has(name)) {
|
|
26
|
+
cache.set(name, builder.objectRef(name));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return cache.get(name)!;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function getFindUniqueForRef<Types extends SchemaTypes>(
|
|
33
|
+
ref: ObjectRef<unknown>,
|
|
34
|
+
builder: PothosSchemaTypes.SchemaBuilder<Types>,
|
|
35
|
+
) {
|
|
36
|
+
if (!findUniqueMap.has(builder)) {
|
|
37
|
+
findUniqueMap.set(builder, new Map());
|
|
38
|
+
}
|
|
39
|
+
const cache = findUniqueMap.get(builder)!;
|
|
40
|
+
|
|
41
|
+
if (!cache.has(ref)) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return cache.get(ref)! as (args: unknown, context: Types['Context']) => unknown;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function setFindUniqueForRef<Types extends SchemaTypes>(
|
|
49
|
+
ref: ObjectRef<unknown>,
|
|
50
|
+
builder: PothosSchemaTypes.SchemaBuilder<Types>,
|
|
51
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
52
|
+
findUnique: ((args: any, context: Types['Context']) => unknown) | null,
|
|
53
|
+
) {
|
|
54
|
+
if (!findUniqueMap.has(builder)) {
|
|
55
|
+
findUniqueMap.set(builder, new Map());
|
|
56
|
+
}
|
|
57
|
+
const cache = findUniqueMap.get(builder)!;
|
|
58
|
+
|
|
59
|
+
cache.set(ref, findUnique);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function getRelation<Types extends SchemaTypes>(
|
|
63
|
+
name: string,
|
|
64
|
+
builder: PothosSchemaTypes.SchemaBuilder<Types>,
|
|
65
|
+
relation: string,
|
|
66
|
+
) {
|
|
67
|
+
const { client } = builder.options.prisma;
|
|
68
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
69
|
+
const dmmf = (client as unknown as { _dmmf: { modelMap: Record<string, Prisma.DMMF.Model> } })
|
|
70
|
+
._dmmf;
|
|
71
|
+
const modelData = dmmf.modelMap[name];
|
|
72
|
+
|
|
73
|
+
if (!modelData) {
|
|
74
|
+
throw new Error(`Model '${name}' not found in DMMF`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const fieldData = modelData.fields.find((field) => field.name === relation);
|
|
78
|
+
|
|
79
|
+
if (!fieldData) {
|
|
80
|
+
throw new Error(`Field '${relation}' not found in model '${name}'`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (fieldData.kind !== 'object') {
|
|
84
|
+
throw new Error(`Field ${relation} of model '${name}' is not a relation (${fieldData.kind})`);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return fieldData;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function getRelatedDelegate<Types extends SchemaTypes>(
|
|
91
|
+
name: string,
|
|
92
|
+
builder: PothosSchemaTypes.SchemaBuilder<Types>,
|
|
93
|
+
relation: string,
|
|
94
|
+
) {
|
|
95
|
+
const fieldData = getRelation(name, builder, relation);
|
|
96
|
+
|
|
97
|
+
return fieldData.type;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function getDelegateFromModel(client: Record<string, unknown>, model: string) {
|
|
101
|
+
const lowerCase = `${model.slice(0, 1).toLowerCase()}${model.slice(1)}`;
|
|
102
|
+
|
|
103
|
+
const delegate = lowerCase in client ? client[lowerCase] : null;
|
|
104
|
+
|
|
105
|
+
if (!delegate) {
|
|
106
|
+
throw new Error(`Unable to find delegate for model ${model}`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return delegate as PrismaDelegate;
|
|
110
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import './global-types';
|
|
2
|
+
import { GraphQLResolveInfo } from 'graphql';
|
|
3
|
+
import SchemaBuilder, {
|
|
4
|
+
brandWithType,
|
|
5
|
+
FieldNullability,
|
|
6
|
+
FieldRef,
|
|
7
|
+
InterfaceRef,
|
|
8
|
+
OutputType,
|
|
9
|
+
SchemaTypes,
|
|
10
|
+
TypeParam,
|
|
11
|
+
} from '@pothos/core';
|
|
12
|
+
import { PrismaObjectFieldBuilder } from './field-builder';
|
|
13
|
+
import PrismaNodeRef from './node-ref';
|
|
14
|
+
import { getDelegateFromModel, getRefFromModel, setFindUniqueForRef } from './refs';
|
|
15
|
+
import { ModelTypes, PrismaDelegate, PrismaNodeOptions } from './types';
|
|
16
|
+
import { queryFromInfo } from './util';
|
|
17
|
+
|
|
18
|
+
const schemaBuilderProto = SchemaBuilder.prototype as PothosSchemaTypes.SchemaBuilder<SchemaTypes>;
|
|
19
|
+
|
|
20
|
+
schemaBuilderProto.prismaObject = function prismaObject(type, { fields, findUnique, ...options }) {
|
|
21
|
+
const ref = getRefFromModel(type, this);
|
|
22
|
+
const name = options.name ?? type;
|
|
23
|
+
|
|
24
|
+
ref.name = name;
|
|
25
|
+
|
|
26
|
+
setFindUniqueForRef(ref, this, findUnique);
|
|
27
|
+
|
|
28
|
+
this.objectType(ref, {
|
|
29
|
+
...(options as {} as PothosSchemaTypes.ObjectFieldOptions<
|
|
30
|
+
SchemaTypes,
|
|
31
|
+
unknown,
|
|
32
|
+
TypeParam<SchemaTypes>,
|
|
33
|
+
FieldNullability<SchemaTypes>,
|
|
34
|
+
{},
|
|
35
|
+
unknown
|
|
36
|
+
>),
|
|
37
|
+
extensions: {
|
|
38
|
+
...options.extensions,
|
|
39
|
+
pothosPrismaInclude: options.include,
|
|
40
|
+
},
|
|
41
|
+
name,
|
|
42
|
+
fields: fields ? () => fields(new PrismaObjectFieldBuilder(name, this, type)) : undefined,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
return ref as never;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
schemaBuilderProto.prismaNode = function prismaNode(
|
|
49
|
+
this: PothosSchemaTypes.SchemaBuilder<SchemaTypes> & {
|
|
50
|
+
nodeInterfaceRef?: () => InterfaceRef<unknown>;
|
|
51
|
+
},
|
|
52
|
+
type: keyof SchemaTypes['PrismaTypes'],
|
|
53
|
+
{
|
|
54
|
+
findUnique,
|
|
55
|
+
name,
|
|
56
|
+
...options
|
|
57
|
+
}: PrismaNodeOptions<SchemaTypes, ModelTypes<PrismaDelegate>, [], never, {}>,
|
|
58
|
+
) {
|
|
59
|
+
const interfaceRef = this.nodeInterfaceRef?.();
|
|
60
|
+
|
|
61
|
+
if (!interfaceRef) {
|
|
62
|
+
throw new TypeError('builder.prismaNode requires @pothos/plugin-relay to be installed');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const typeName = name ?? type;
|
|
66
|
+
const delegate = getDelegateFromModel(this.options.prisma.client, type);
|
|
67
|
+
const nodeRef = new PrismaNodeRef(typeName);
|
|
68
|
+
const extendedOptions = {
|
|
69
|
+
...options,
|
|
70
|
+
interfaces: [interfaceRef, ...(options.interfaces ?? [])],
|
|
71
|
+
extensions: {
|
|
72
|
+
...options.extensions,
|
|
73
|
+
pothosPrismaInclude: options.include,
|
|
74
|
+
},
|
|
75
|
+
isTypeOf: (val: unknown) => nodeRef.hasBrand(val),
|
|
76
|
+
findUnique: (parent: unknown, context: {}) =>
|
|
77
|
+
findUnique(options.id.resolve(parent as never, context) as string, context),
|
|
78
|
+
loadWithoutCache: async (
|
|
79
|
+
id: string,
|
|
80
|
+
context: SchemaTypes['Context'],
|
|
81
|
+
info: GraphQLResolveInfo,
|
|
82
|
+
) => {
|
|
83
|
+
const query = queryFromInfo(context, info, typeName);
|
|
84
|
+
const record = await delegate.findUnique({
|
|
85
|
+
...query,
|
|
86
|
+
rejectOnNotFound: true,
|
|
87
|
+
where: findUnique(id, context),
|
|
88
|
+
} as never);
|
|
89
|
+
|
|
90
|
+
brandWithType(record, typeName as OutputType<SchemaTypes>);
|
|
91
|
+
|
|
92
|
+
return record;
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const ref = this.prismaObject(type, extendedOptions as never);
|
|
97
|
+
|
|
98
|
+
this.configStore.onTypeConfig(ref, (nodeConfig) => {
|
|
99
|
+
this.objectField(ref, 'id', (t) =>
|
|
100
|
+
(
|
|
101
|
+
t as unknown as {
|
|
102
|
+
globalID: (options: Record<string, unknown>) => FieldRef<unknown>;
|
|
103
|
+
}
|
|
104
|
+
).globalID({
|
|
105
|
+
...options.id,
|
|
106
|
+
nullable: false,
|
|
107
|
+
args: {},
|
|
108
|
+
resolve: async (
|
|
109
|
+
parent: never,
|
|
110
|
+
args: object,
|
|
111
|
+
context: object,
|
|
112
|
+
info: GraphQLResolveInfo,
|
|
113
|
+
) => ({
|
|
114
|
+
type: nodeConfig.name,
|
|
115
|
+
id: await options.id.resolve(parent, context),
|
|
116
|
+
}),
|
|
117
|
+
}),
|
|
118
|
+
);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
this.configStore.associateRefWithName(nodeRef, typeName);
|
|
122
|
+
|
|
123
|
+
return nodeRef;
|
|
124
|
+
} as never;
|