orchid-graphql 0.0.3 → 1.0.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/README.md +13 -11
- package/dist/index.cjs +249 -216
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +127 -83
- package/dist/index.js +249 -216
- package/dist/index.js.map +1 -1
- package/package.json +13 -27
- package/src/index.ts +12 -12
- package/src/orm/orm.ts +112 -0
- package/src/resolvers/graph.ts +9 -86
- package/src/resolvers/table.ts +5 -139
- package/src/filters/filters.ts +0 -93
- package/src/paginators/base.ts +0 -40
- package/src/paginators/cursor.ts +0 -113
- package/src/resolvers/field.ts +0 -51
- package/src/resolvers/page.ts +0 -34
- package/src/resolvers/relation.ts +0 -27
- package/src/utils/is-plain-object.ts +0 -3
- package/src/utils/modifier.ts +0 -3
- package/src/utils/query-context.ts +0 -11
- package/src/utils/run-after.ts +0 -19
package/README.md
CHANGED
|
@@ -2,10 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
A helper library to resolve GraphQL queries directly with [Orchid ORM](https://orchid-orm.netlify.app) tables and relations.
|
|
4
4
|
|
|
5
|
-
This is a fork of [objection-graphql-resolver](https://github.com/IlyaSemenov/objection-graphql-resolver) which does the same for [Objection.js ORM](https://vincit.github.io/objection.js/).
|
|
6
|
-
|
|
7
|
-
## Features
|
|
8
|
-
|
|
9
5
|
- Highly effective: selects only requested fields and relations.
|
|
10
6
|
- Unlimited nested resolvers.
|
|
11
7
|
- Pagination.
|
|
@@ -13,11 +9,13 @@ This is a fork of [objection-graphql-resolver](https://github.com/IlyaSemenov/ob
|
|
|
13
9
|
- Hook into (sub)queries with query modifiers.
|
|
14
10
|
- Hook into field results to restrict access to sensitive information.
|
|
15
11
|
|
|
12
|
+
_Note that there is a sister project: `objection-graphql-resolver` which does the same for [Objection.js ORM](https://vincit.github.io/objection.js/)._
|
|
13
|
+
|
|
16
14
|
## Status and limitations
|
|
17
15
|
|
|
18
|
-
This is pre-release and not battle tested. `orchid-graphql` is a quick
|
|
16
|
+
This is pre-release and not battle tested. `orchid-graphql` is a quick adaptation of `objection-graphql-resolver`.
|
|
19
17
|
|
|
20
|
-
In particular
|
|
18
|
+
In particular the typings are not ready. The resolver returns a generic `Query` most of the time.
|
|
21
19
|
|
|
22
20
|
## Install
|
|
23
21
|
|
|
@@ -299,15 +297,14 @@ const graph = r.graph(
|
|
|
299
297
|
}),
|
|
300
298
|
preview2: r.field({
|
|
301
299
|
// Modify query
|
|
302
|
-
modify: (
|
|
303
|
-
query.select(raw("substr(text,1,100) as preview2")),
|
|
300
|
+
modify: (q) => q.select({ preview2: q.raw("substr(text,1,100)") }),
|
|
304
301
|
// Post-process selected value
|
|
305
302
|
transform(
|
|
306
303
|
// Selected value
|
|
307
304
|
preview,
|
|
308
305
|
// Current instance
|
|
309
306
|
post,
|
|
310
|
-
//
|
|
307
|
+
// Field resolve context: graph, tree, type, field, filters, context
|
|
311
308
|
context
|
|
312
309
|
) {
|
|
313
310
|
if (preview.length < 100) {
|
|
@@ -351,7 +348,12 @@ const graph = r.graph(
|
|
|
351
348
|
),
|
|
352
349
|
},
|
|
353
350
|
// Modify all queries to this table
|
|
354
|
-
modify: (
|
|
351
|
+
modify: (
|
|
352
|
+
// ORM (sub)query
|
|
353
|
+
query,
|
|
354
|
+
// Table resolve context: graph, tree, type, filters, context
|
|
355
|
+
context
|
|
356
|
+
) => query.where(context.tree.args).order({ id: "DESC" }),
|
|
355
357
|
// Allow all fields (`fields` will be used for overrides)
|
|
356
358
|
allowAllFields: true,
|
|
357
359
|
// Allow filters in all relations
|
|
@@ -385,7 +387,7 @@ const resolvers = {
|
|
|
385
387
|
}
|
|
386
388
|
)
|
|
387
389
|
},
|
|
388
|
-
posts_page: (parent, args, context, info) => {
|
|
390
|
+
posts_page: async (parent, args, context, info) => {
|
|
389
391
|
return await graph.resolvePage(
|
|
390
392
|
// Root query (required)
|
|
391
393
|
db.post,
|
package/dist/index.cjs
CHANGED
|
@@ -35,29 +35,7 @@ __export(src_exports, {
|
|
|
35
35
|
});
|
|
36
36
|
module.exports = __toCommonJS(src_exports);
|
|
37
37
|
|
|
38
|
-
// src/paginators/
|
|
39
|
-
var s = Symbol();
|
|
40
|
-
function set_query_page_result(query, get_page) {
|
|
41
|
-
function _get_page(data) {
|
|
42
|
-
if (!Array.isArray(data)) {
|
|
43
|
-
throw new Error(`Paginator called for single result query.`);
|
|
44
|
-
}
|
|
45
|
-
return get_page(data);
|
|
46
|
-
}
|
|
47
|
-
const _query = query.query;
|
|
48
|
-
const { handleResult } = _query;
|
|
49
|
-
_query.handleResult = (q, result, s2) => {
|
|
50
|
-
const data = handleResult(q, result, s2);
|
|
51
|
-
return s2 ? data : _get_page(data);
|
|
52
|
-
};
|
|
53
|
-
_query[s] = _get_page;
|
|
54
|
-
return query;
|
|
55
|
-
}
|
|
56
|
-
function get_query_pagination_handler(query) {
|
|
57
|
-
return query.query[s];
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// src/paginators/cursor.ts
|
|
38
|
+
// ../graphql-orm-resolver/src/paginators/cursor.ts
|
|
61
39
|
function defineCursorPaginator(options = {}) {
|
|
62
40
|
return new CursorPaginator(options);
|
|
63
41
|
}
|
|
@@ -79,18 +57,22 @@ var CursorPaginator = class {
|
|
|
79
57
|
}
|
|
80
58
|
});
|
|
81
59
|
}
|
|
82
|
-
paginate(query,
|
|
60
|
+
paginate(query, context) {
|
|
61
|
+
const { orm: orm2 } = context.graph;
|
|
62
|
+
const { args } = context.tree;
|
|
83
63
|
const take = args.take ?? this.options.take;
|
|
84
|
-
const
|
|
85
|
-
query =
|
|
64
|
+
const cursor = args.cursor;
|
|
65
|
+
query = orm2.reset_query_order(query);
|
|
86
66
|
for (const field of this.fields) {
|
|
87
|
-
query =
|
|
67
|
+
query = orm2.select_field(query, { field: field.name, as: field.name });
|
|
68
|
+
query = orm2.add_query_order(query, field.name, field.desc);
|
|
88
69
|
}
|
|
89
70
|
if (cursor) {
|
|
90
|
-
|
|
71
|
+
const { expression, bindings } = this._parse_cursor(cursor);
|
|
72
|
+
query = orm2.where_raw(query, expression, bindings);
|
|
91
73
|
}
|
|
92
|
-
query =
|
|
93
|
-
query = set_query_page_result(query, (nodes) => {
|
|
74
|
+
query = orm2.set_query_limit(query, take + 1);
|
|
75
|
+
query = orm2.set_query_page_result(query, (nodes) => {
|
|
94
76
|
let cursor2;
|
|
95
77
|
if (nodes.length > take) {
|
|
96
78
|
cursor2 = this._create_cursor(nodes[take - 1]);
|
|
@@ -113,39 +95,29 @@ var CursorPaginator = class {
|
|
|
113
95
|
})
|
|
114
96
|
);
|
|
115
97
|
}
|
|
116
|
-
|
|
98
|
+
_parse_cursor(cursor) {
|
|
117
99
|
const values = JSON.parse(cursor);
|
|
118
100
|
const left = [];
|
|
119
101
|
const right = [];
|
|
120
|
-
const
|
|
102
|
+
const bindings = {};
|
|
121
103
|
for (let i = 0; i < this.fields.length; ++i) {
|
|
122
104
|
const field = this.fields[i];
|
|
123
105
|
const expressions = field.desc ? right : left;
|
|
124
106
|
const placeholders = field.desc ? left : right;
|
|
125
107
|
expressions.push(`"${field.name}"`);
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
expr_values[placeholder] = values[i];
|
|
108
|
+
placeholders.push("$" + field.name);
|
|
109
|
+
bindings[field.name] = values[i];
|
|
129
110
|
}
|
|
130
|
-
return
|
|
131
|
-
|
|
132
|
-
|
|
111
|
+
return {
|
|
112
|
+
expression: `(${left.join(",")}) > (${right.join(",")})`,
|
|
113
|
+
bindings
|
|
114
|
+
};
|
|
133
115
|
}
|
|
134
116
|
};
|
|
135
117
|
|
|
136
|
-
// src/utils/
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
;
|
|
140
|
-
query.query[graphql] = context;
|
|
141
|
-
}
|
|
142
|
-
function get_query_context(query) {
|
|
143
|
-
return query.query[graphql];
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// src/utils/run-after.ts
|
|
147
|
-
function run_after_query(query, transform) {
|
|
148
|
-
return query.afterQuery(async (_, result) => {
|
|
118
|
+
// ../graphql-orm-resolver/src/utils/run-after.ts
|
|
119
|
+
function run_after_query(orm2, query, transform) {
|
|
120
|
+
return orm2.run_after_query(query, async (result) => {
|
|
149
121
|
if (Array.isArray(result)) {
|
|
150
122
|
const result1 = await Promise.all(result.map(transform));
|
|
151
123
|
return result1.filter(Boolean);
|
|
@@ -157,19 +129,21 @@ function run_after_query(query, transform) {
|
|
|
157
129
|
});
|
|
158
130
|
}
|
|
159
131
|
|
|
160
|
-
// src/resolvers/field.ts
|
|
132
|
+
// ../graphql-orm-resolver/src/resolvers/field.ts
|
|
161
133
|
function defineFieldResolver(options = {}) {
|
|
162
134
|
const { tableField, modify, transform } = options;
|
|
163
|
-
return function resolve(query,
|
|
164
|
-
const { field } =
|
|
135
|
+
return function resolve(query, context) {
|
|
136
|
+
const { field } = context;
|
|
165
137
|
if (modify) {
|
|
166
|
-
|
|
138
|
+
query = modify(query, context);
|
|
167
139
|
} else {
|
|
168
|
-
query =
|
|
140
|
+
query = context.graph.orm.select_field(query, {
|
|
141
|
+
field: tableField || field,
|
|
142
|
+
as: field
|
|
143
|
+
});
|
|
169
144
|
}
|
|
170
145
|
if (transform) {
|
|
171
|
-
|
|
172
|
-
query = run_after_query(query, async (instance) => {
|
|
146
|
+
query = run_after_query(context.graph.orm, query, async (instance) => {
|
|
173
147
|
instance[field] = await transform(instance[field], instance, context);
|
|
174
148
|
return instance;
|
|
175
149
|
});
|
|
@@ -178,118 +152,103 @@ function defineFieldResolver(options = {}) {
|
|
|
178
152
|
};
|
|
179
153
|
}
|
|
180
154
|
|
|
181
|
-
// src/resolvers/graph.ts
|
|
155
|
+
// ../graphql-orm-resolver/src/resolvers/graph.ts
|
|
182
156
|
var import_graphql_parse_resolve_info = require("graphql-parse-resolve-info");
|
|
183
|
-
function createGraphResolver(types, options) {
|
|
184
|
-
return new GraphResolver(types, options);
|
|
185
|
-
}
|
|
186
157
|
var GraphResolver = class {
|
|
187
|
-
constructor(
|
|
188
|
-
this.
|
|
158
|
+
constructor(orm2, type_resolvers, options = {}) {
|
|
159
|
+
this.orm = orm2;
|
|
160
|
+
this.type_resolvers = type_resolvers;
|
|
189
161
|
this.options = options;
|
|
190
162
|
}
|
|
191
163
|
resolve(query, { info, context, filters }) {
|
|
192
|
-
set_query_context(query, context);
|
|
193
164
|
const tree = this._get_resolve_tree(info);
|
|
194
|
-
return this._resolve_type(
|
|
165
|
+
return this._resolve_type(query, { tree, filters, context });
|
|
195
166
|
}
|
|
196
167
|
resolvePage(query, paginator, { info, context, filters }) {
|
|
197
|
-
set_query_context(query, context);
|
|
198
168
|
const tree = this._get_resolve_tree(info);
|
|
199
|
-
return this._resolve_page(
|
|
169
|
+
return this._resolve_page(query, paginator, { tree, filters, context });
|
|
200
170
|
}
|
|
201
171
|
_get_resolve_tree(info) {
|
|
202
172
|
return (0, import_graphql_parse_resolve_info.parseResolveInfo)(info);
|
|
203
173
|
}
|
|
204
|
-
_resolve_type({
|
|
205
|
-
tree
|
|
206
|
-
query,
|
|
207
|
-
filters
|
|
208
|
-
}) {
|
|
174
|
+
_resolve_type(query, context) {
|
|
175
|
+
const { tree } = context;
|
|
209
176
|
const type = Object.keys(tree.fieldsByTypeName)[0];
|
|
210
|
-
const
|
|
211
|
-
if (!
|
|
212
|
-
throw new Error(`
|
|
177
|
+
const type_resolver = this.type_resolvers[type];
|
|
178
|
+
if (!type_resolver) {
|
|
179
|
+
throw new Error(`Resolver not found for type ${type}.`);
|
|
213
180
|
}
|
|
214
|
-
return
|
|
181
|
+
return type_resolver.resolve(query, {
|
|
182
|
+
...context,
|
|
183
|
+
graph: this,
|
|
184
|
+
tree,
|
|
185
|
+
type
|
|
186
|
+
});
|
|
215
187
|
}
|
|
216
|
-
_resolve_page({
|
|
217
|
-
tree
|
|
218
|
-
query,
|
|
219
|
-
paginator,
|
|
220
|
-
filters
|
|
221
|
-
}) {
|
|
222
|
-
const { args } = tree;
|
|
188
|
+
_resolve_page(query, paginator, context) {
|
|
189
|
+
let { tree } = context;
|
|
223
190
|
for (const field of paginator.path) {
|
|
224
191
|
const type = Object.keys(tree.fieldsByTypeName)[0];
|
|
225
192
|
tree = tree.fieldsByTypeName[type][field];
|
|
226
193
|
}
|
|
227
|
-
query = this._resolve_type({
|
|
228
|
-
return paginator.paginate(query,
|
|
194
|
+
query = this._resolve_type(query, { ...context, tree });
|
|
195
|
+
return paginator.paginate(query, {
|
|
196
|
+
...context,
|
|
197
|
+
graph: this
|
|
198
|
+
});
|
|
229
199
|
}
|
|
230
200
|
};
|
|
231
201
|
|
|
232
|
-
// src/resolvers/page.ts
|
|
233
|
-
var import_orchid_core = require("orchid-core");
|
|
202
|
+
// ../graphql-orm-resolver/src/resolvers/page.ts
|
|
234
203
|
function definePageResolver(paginator, options = {}) {
|
|
235
|
-
const { tableField,
|
|
236
|
-
return
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
});
|
|
204
|
+
const { tableField, modify } = options;
|
|
205
|
+
return function resolve(query, context) {
|
|
206
|
+
const { graph, field } = context;
|
|
207
|
+
const pagination_context = {};
|
|
208
|
+
query = graph.orm.select_relation(query, {
|
|
209
|
+
relation: tableField || field,
|
|
210
|
+
as: field,
|
|
211
|
+
modify(subquery) {
|
|
212
|
+
subquery = graph._resolve_page(subquery, paginator, context);
|
|
213
|
+
subquery = graph.orm.modify_subquery_pagination(
|
|
214
|
+
subquery,
|
|
215
|
+
pagination_context
|
|
216
|
+
);
|
|
217
|
+
return modify ? modify(subquery, context) : subquery;
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
query = graph.orm.finish_query_pagination(query, field, pagination_context);
|
|
221
|
+
return query;
|
|
222
|
+
};
|
|
255
223
|
}
|
|
256
224
|
|
|
257
|
-
// src/resolvers/relation.ts
|
|
225
|
+
// ../graphql-orm-resolver/src/resolvers/relation.ts
|
|
258
226
|
function defineRelationResolver(options = {}) {
|
|
259
227
|
const { tableField, filters, modify } = options;
|
|
260
|
-
return
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
return modify ? modify(relation_query, tree) : relation_query;
|
|
228
|
+
return function resolve(query, context) {
|
|
229
|
+
const { graph, field } = context;
|
|
230
|
+
return graph.orm.select_relation(query, {
|
|
231
|
+
relation: tableField || field,
|
|
232
|
+
as: field,
|
|
233
|
+
modify(subquery) {
|
|
234
|
+
subquery = graph._resolve_type(subquery, { ...context, filters });
|
|
235
|
+
return modify ? modify(subquery, context) : subquery;
|
|
269
236
|
}
|
|
270
|
-
})
|
|
271
|
-
}
|
|
237
|
+
});
|
|
238
|
+
};
|
|
272
239
|
}
|
|
273
240
|
|
|
274
|
-
// src/utils/is-plain-object.ts
|
|
241
|
+
// ../graphql-orm-resolver/src/utils/is-plain-object.ts
|
|
275
242
|
function is_plain_object(obj) {
|
|
276
243
|
return typeof obj === "object" && !Array.isArray(obj);
|
|
277
244
|
}
|
|
278
245
|
|
|
279
|
-
// src/filters/filters.ts
|
|
280
|
-
function
|
|
281
|
-
return value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean";
|
|
282
|
-
}
|
|
283
|
-
function apply_filters({
|
|
284
|
-
query,
|
|
285
|
-
filters,
|
|
286
|
-
args,
|
|
287
|
-
modifiers
|
|
288
|
-
}) {
|
|
246
|
+
// ../graphql-orm-resolver/src/filters/filters.ts
|
|
247
|
+
function apply_filters(query, { filters, modifiers, context }) {
|
|
289
248
|
if (!filters) {
|
|
290
249
|
return query;
|
|
291
250
|
}
|
|
292
|
-
const filter_obj = args?.filter;
|
|
251
|
+
const filter_obj = context.tree.args?.filter;
|
|
293
252
|
if (!filter_obj) {
|
|
294
253
|
return query;
|
|
295
254
|
}
|
|
@@ -301,122 +260,196 @@ function apply_filters({
|
|
|
301
260
|
continue;
|
|
302
261
|
}
|
|
303
262
|
if (modifiers?.[field]) {
|
|
304
|
-
query = modifiers[field](query, value);
|
|
263
|
+
query = modifiers[field](query, value, context);
|
|
305
264
|
} else {
|
|
306
|
-
const [
|
|
307
|
-
const op = op0?.toLowerCase()
|
|
308
|
-
|
|
309
|
-
// TODO: add the rest of orchid ops (or allow all of them?)
|
|
310
|
-
[
|
|
311
|
-
"exact",
|
|
312
|
-
"lt",
|
|
313
|
-
"lte",
|
|
314
|
-
"gt",
|
|
315
|
-
"gte",
|
|
316
|
-
"like",
|
|
317
|
-
"ilike",
|
|
318
|
-
"contains"
|
|
319
|
-
].includes(op)
|
|
320
|
-
) {
|
|
321
|
-
if (!is_scalar(value)) {
|
|
322
|
-
throw new Error(
|
|
323
|
-
`Unsupported filter value for ${query.table}.${field}: must be scalar.`
|
|
324
|
-
);
|
|
325
|
-
}
|
|
326
|
-
if (op === "exact") {
|
|
327
|
-
query = query.where({ [dbfield]: value });
|
|
328
|
-
} else {
|
|
329
|
-
const query_op = { like: "contains" }[op] || op;
|
|
330
|
-
query = query.where({ [dbfield]: { [query_op]: value } });
|
|
331
|
-
}
|
|
332
|
-
} else if (op === "in") {
|
|
333
|
-
if (!(Array.isArray(value) && value.every(is_scalar))) {
|
|
334
|
-
throw new Error(
|
|
335
|
-
`Invalid filter value for ${query.table}.${field}: must be an array of scalars.`
|
|
336
|
-
);
|
|
337
|
-
}
|
|
338
|
-
query = query.whereIn(dbfield, value);
|
|
339
|
-
} else {
|
|
340
|
-
throw new Error(
|
|
341
|
-
`Invalid filter ${query.table}.${field}: unsupported operator ${op}.`
|
|
342
|
-
);
|
|
343
|
-
}
|
|
265
|
+
const [table_field, op0] = field.split("__");
|
|
266
|
+
const op = op0?.toLowerCase();
|
|
267
|
+
query = context.graph.orm.where(query, table_field, op, value);
|
|
344
268
|
}
|
|
345
269
|
}
|
|
346
270
|
return query;
|
|
347
271
|
}
|
|
348
272
|
|
|
349
|
-
// src/resolvers/table.ts
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
273
|
+
// ../graphql-orm-resolver/src/resolvers/table.ts
|
|
274
|
+
var TableResolver = class {
|
|
275
|
+
constructor(orm2, table, options = {}) {
|
|
276
|
+
this.orm = orm2;
|
|
277
|
+
this.table = table;
|
|
278
|
+
this.options = options;
|
|
279
|
+
this.relations = new Set(orm2.get_table_relations(table));
|
|
280
|
+
this.virtual_fields = new Set(orm2.get_table_virtual_fields(table));
|
|
281
|
+
this.modifiers = { ...orm2.get_table_modifiers(table), ...options.modifiers };
|
|
282
|
+
const { fields } = options;
|
|
283
|
+
if (fields) {
|
|
284
|
+
this.table_field_resolvers = Object.keys(fields).reduce((resolvers, field) => {
|
|
285
|
+
const r0 = fields[field];
|
|
286
|
+
const r = typeof r0 === "function" ? r0 : r0 === true ? this._get_default_field_resolver(field) : typeof r0 === "string" ? this._get_default_field_resolver(field, r0) : void 0;
|
|
287
|
+
if (r === void 0) {
|
|
288
|
+
throw new Error(
|
|
289
|
+
`Field resolver must be a function, string, or true; found ${typeof r0}.`
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
resolvers[field] = r;
|
|
293
|
+
return resolvers;
|
|
294
|
+
}, {});
|
|
359
295
|
}
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
},
|
|
374
|
-
{}
|
|
375
|
-
) : void 0;
|
|
376
|
-
const { modify, transform } = options;
|
|
377
|
-
return function resolve({ tree, type, query, filters, graph }) {
|
|
378
|
-
if (query.table !== table.table) {
|
|
296
|
+
}
|
|
297
|
+
// Introspection results
|
|
298
|
+
relations;
|
|
299
|
+
virtual_fields;
|
|
300
|
+
modifiers;
|
|
301
|
+
table_field_resolvers;
|
|
302
|
+
/** Modify query to select fields/relations and filter the result set. */
|
|
303
|
+
resolve(query, context) {
|
|
304
|
+
const { modify, transform } = this.options;
|
|
305
|
+
const { graph, tree, type, filters } = context;
|
|
306
|
+
const query_table = this.orm.get_query_table(query);
|
|
307
|
+
const table_table = this.orm.get_table_table(this.table);
|
|
308
|
+
if (query_table !== table_table) {
|
|
379
309
|
throw new Error(
|
|
380
|
-
`Mismatching query type
|
|
310
|
+
`Mismatching query table for type ${type}: expected ${table_table}, received ${query_table}.`
|
|
381
311
|
);
|
|
382
312
|
}
|
|
383
|
-
const allow_all_fields = options.allowAllFields ?? graph.options.allowAllFields ??
|
|
384
|
-
if (!allow_all_fields && !table_field_resolvers) {
|
|
313
|
+
const allow_all_fields = this.options.allowAllFields ?? graph.options.allowAllFields ?? !this.options.fields;
|
|
314
|
+
if (!allow_all_fields && !this.table_field_resolvers) {
|
|
385
315
|
throw new Error(
|
|
386
|
-
`
|
|
316
|
+
`Resolver for type ${type} must either allow all fields or specify options.fields.`
|
|
387
317
|
);
|
|
388
318
|
}
|
|
389
319
|
if (modify) {
|
|
390
|
-
query = modify(query,
|
|
320
|
+
query = modify(query, context);
|
|
391
321
|
}
|
|
392
322
|
for (const subtree of Object.values(tree.fieldsByTypeName[type])) {
|
|
393
323
|
const field = subtree.name;
|
|
394
|
-
const r = table_field_resolvers?.[field] || (allow_all_fields ?
|
|
324
|
+
const r = this.table_field_resolvers?.[field] || (allow_all_fields ? this._get_default_field_resolver(field) : void 0);
|
|
395
325
|
if (!r) {
|
|
396
|
-
throw new Error(`No field resolver defined for field ${type}.${field}
|
|
326
|
+
throw new Error(`No field resolver defined for field ${type}.${field}.`);
|
|
397
327
|
}
|
|
398
328
|
if (r) {
|
|
399
|
-
query = r(query, {
|
|
329
|
+
query = r(query, { ...context, tree: subtree, field });
|
|
400
330
|
}
|
|
401
331
|
}
|
|
402
|
-
const allow_all_filters = options.allowAllFilters ?? graph.options.allowAllFilters;
|
|
332
|
+
const allow_all_filters = this.options.allowAllFilters ?? graph.options.allowAllFilters;
|
|
403
333
|
const effective_filters = allow_all_filters ? true : filters;
|
|
404
334
|
if (effective_filters) {
|
|
405
|
-
query = apply_filters({
|
|
406
|
-
query,
|
|
335
|
+
query = apply_filters(query, {
|
|
407
336
|
filters: effective_filters,
|
|
408
|
-
|
|
409
|
-
|
|
337
|
+
modifiers: this.modifiers,
|
|
338
|
+
context
|
|
410
339
|
});
|
|
411
340
|
}
|
|
412
341
|
if (transform) {
|
|
413
|
-
|
|
414
|
-
query = run_after_query(query, (instance) => {
|
|
342
|
+
query = run_after_query(this.orm, query, (instance) => {
|
|
415
343
|
return transform(instance, context);
|
|
416
344
|
});
|
|
417
345
|
}
|
|
346
|
+
query = this.orm.prevent_select_all(query);
|
|
418
347
|
return query;
|
|
419
|
-
}
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Create field resolver which will modify query to resolve GraphQL field.
|
|
351
|
+
*/
|
|
352
|
+
_get_default_field_resolver(field, tableField) {
|
|
353
|
+
const table_field_lookup = tableField || field;
|
|
354
|
+
if (this.options.modifiers?.[table_field_lookup] || this.virtual_fields.has(table_field_lookup)) {
|
|
355
|
+
return (query) => query;
|
|
356
|
+
} else if (this.relations.has(table_field_lookup)) {
|
|
357
|
+
return defineRelationResolver({ tableField });
|
|
358
|
+
} else {
|
|
359
|
+
return defineFieldResolver({ tableField });
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
// src/orm/orm.ts
|
|
365
|
+
var import_orchid_core = require("orchid-core");
|
|
366
|
+
var orm = {
|
|
367
|
+
// Reflection
|
|
368
|
+
get_table_table(table) {
|
|
369
|
+
return table.table;
|
|
370
|
+
},
|
|
371
|
+
get_table_relations(table) {
|
|
372
|
+
return Object.keys(table.relations);
|
|
373
|
+
},
|
|
374
|
+
get_table_virtual_fields() {
|
|
375
|
+
return [];
|
|
376
|
+
},
|
|
377
|
+
get_table_modifiers() {
|
|
378
|
+
return void 0;
|
|
379
|
+
},
|
|
380
|
+
get_query_table(query) {
|
|
381
|
+
if (!query.table) {
|
|
382
|
+
throw new Error("Query must have table.");
|
|
383
|
+
}
|
|
384
|
+
return query.table;
|
|
385
|
+
},
|
|
386
|
+
// Select
|
|
387
|
+
select_field(query, { field, as }) {
|
|
388
|
+
return query.select({ [as]: field });
|
|
389
|
+
},
|
|
390
|
+
select_relation(query, { relation, as, modify }) {
|
|
391
|
+
return query.select({
|
|
392
|
+
[as]: (q) => modify(q[relation])
|
|
393
|
+
});
|
|
394
|
+
},
|
|
395
|
+
// Find
|
|
396
|
+
where(query, field, op, value) {
|
|
397
|
+
return query.where({ [field]: op ? { [op]: value } : value });
|
|
398
|
+
},
|
|
399
|
+
where_raw(query, expression, bindings) {
|
|
400
|
+
return query.where(query.raw(expression, bindings));
|
|
401
|
+
},
|
|
402
|
+
// Order & Limit
|
|
403
|
+
reset_query_order(query) {
|
|
404
|
+
return query.clear("order");
|
|
405
|
+
},
|
|
406
|
+
add_query_order(query, field, desc) {
|
|
407
|
+
return query.order({ [field]: desc ? "DESC" : "ASC" });
|
|
408
|
+
},
|
|
409
|
+
set_query_limit(query, limit) {
|
|
410
|
+
return query.limit(limit);
|
|
411
|
+
},
|
|
412
|
+
// Pagination helpers
|
|
413
|
+
set_query_page_result(query, get_page) {
|
|
414
|
+
const _query = query.query;
|
|
415
|
+
const { handleResult } = _query;
|
|
416
|
+
_query.handleResult = (q, result, s2) => {
|
|
417
|
+
const data = handleResult(q, result, s2);
|
|
418
|
+
return s2 ? data : get_page(data);
|
|
419
|
+
};
|
|
420
|
+
_query[s] = get_page;
|
|
421
|
+
return query;
|
|
422
|
+
},
|
|
423
|
+
modify_subquery_pagination(subquery, context) {
|
|
424
|
+
context.get_page = subquery.query[s];
|
|
425
|
+
return subquery;
|
|
426
|
+
},
|
|
427
|
+
finish_query_pagination(query, field, context) {
|
|
428
|
+
const { get_page } = context;
|
|
429
|
+
if (!get_page) {
|
|
430
|
+
throw new Error(`Internal error: paginator didn't provide page callback.`);
|
|
431
|
+
}
|
|
432
|
+
(0, import_orchid_core.overrideParserInQuery)(query.query, field, get_page);
|
|
433
|
+
return query;
|
|
434
|
+
},
|
|
435
|
+
// Misc
|
|
436
|
+
run_after_query(query, fn) {
|
|
437
|
+
return query.afterQuery(async (_, result) => fn(result));
|
|
438
|
+
},
|
|
439
|
+
prevent_select_all(query) {
|
|
440
|
+
return query;
|
|
441
|
+
}
|
|
442
|
+
};
|
|
443
|
+
var s = Symbol("subquery_pagination_handler");
|
|
444
|
+
|
|
445
|
+
// src/resolvers/graph.ts
|
|
446
|
+
function createGraphResolver(types, options) {
|
|
447
|
+
return new GraphResolver(orm, types, options);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// src/resolvers/table.ts
|
|
451
|
+
function defineTableResolver(table, options = {}) {
|
|
452
|
+
return new TableResolver(orm, table, options);
|
|
420
453
|
}
|
|
421
454
|
// Annotate the CommonJS export names for ESM import in node:
|
|
422
455
|
0 && (module.exports = {
|