orchid-graphql 0.0.3 → 0.0.4
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 +7 -2
- package/dist/index.cjs +35 -53
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +34 -44
- package/dist/index.js +35 -53
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/resolvers/field.ts +11 -16
- package/src/resolvers/graph.ts +19 -27
- package/src/resolvers/page.ts +8 -8
- package/src/resolvers/relation.ts +12 -11
- package/src/resolvers/table.ts +18 -19
- package/src/utils/query-context.ts +0 -11
package/README.md
CHANGED
|
@@ -307,7 +307,7 @@ const graph = r.graph(
|
|
|
307
307
|
preview,
|
|
308
308
|
// Current instance
|
|
309
309
|
post,
|
|
310
|
-
//
|
|
310
|
+
// Field resolve context: graph, tree, type, field, filters, context
|
|
311
311
|
context
|
|
312
312
|
) {
|
|
313
313
|
if (preview.length < 100) {
|
|
@@ -351,7 +351,12 @@ const graph = r.graph(
|
|
|
351
351
|
),
|
|
352
352
|
},
|
|
353
353
|
// Modify all queries to this table
|
|
354
|
-
modify: (
|
|
354
|
+
modify: (
|
|
355
|
+
// ORM (sub)query
|
|
356
|
+
query,
|
|
357
|
+
// Table resolve context: graph, tree, type, filters, context
|
|
358
|
+
context
|
|
359
|
+
) => query.where(context.tree.args).order({ id: "DESC" }),
|
|
355
360
|
// Allow all fields (`fields` will be used for overrides)
|
|
356
361
|
allowAllFields: true,
|
|
357
362
|
// Allow filters in all relations
|
package/dist/index.cjs
CHANGED
|
@@ -133,16 +133,6 @@ var CursorPaginator = class {
|
|
|
133
133
|
}
|
|
134
134
|
};
|
|
135
135
|
|
|
136
|
-
// src/utils/query-context.ts
|
|
137
|
-
var graphql = Symbol("graphql");
|
|
138
|
-
function set_query_context(query, context) {
|
|
139
|
-
;
|
|
140
|
-
query.query[graphql] = context;
|
|
141
|
-
}
|
|
142
|
-
function get_query_context(query) {
|
|
143
|
-
return query.query[graphql];
|
|
144
|
-
}
|
|
145
|
-
|
|
146
136
|
// src/utils/run-after.ts
|
|
147
137
|
function run_after_query(query, transform) {
|
|
148
138
|
return query.afterQuery(async (_, result) => {
|
|
@@ -160,15 +150,14 @@ function run_after_query(query, transform) {
|
|
|
160
150
|
// src/resolvers/field.ts
|
|
161
151
|
function defineFieldResolver(options = {}) {
|
|
162
152
|
const { tableField, modify, transform } = options;
|
|
163
|
-
return function resolve(query,
|
|
164
|
-
const { field } =
|
|
153
|
+
return function resolve(query, context) {
|
|
154
|
+
const { field } = context;
|
|
165
155
|
if (modify) {
|
|
166
|
-
|
|
156
|
+
query = modify(query, context);
|
|
167
157
|
} else {
|
|
168
158
|
query = query.select({ [field]: tableField || field });
|
|
169
159
|
}
|
|
170
160
|
if (transform) {
|
|
171
|
-
const context = get_query_context(query);
|
|
172
161
|
query = run_after_query(query, async (instance) => {
|
|
173
162
|
instance[field] = await transform(instance[field], instance, context);
|
|
174
163
|
return instance;
|
|
@@ -189,42 +178,33 @@ var GraphResolver = class {
|
|
|
189
178
|
this.options = options;
|
|
190
179
|
}
|
|
191
180
|
resolve(query, { info, context, filters }) {
|
|
192
|
-
set_query_context(query, context);
|
|
193
181
|
const tree = this._get_resolve_tree(info);
|
|
194
|
-
return this._resolve_type(
|
|
182
|
+
return this._resolve_type(query, { tree, filters, context });
|
|
195
183
|
}
|
|
196
184
|
resolvePage(query, paginator, { info, context, filters }) {
|
|
197
|
-
set_query_context(query, context);
|
|
198
185
|
const tree = this._get_resolve_tree(info);
|
|
199
|
-
return this._resolve_page(
|
|
186
|
+
return this._resolve_page(query, paginator, { tree, filters, context });
|
|
200
187
|
}
|
|
201
188
|
_get_resolve_tree(info) {
|
|
202
189
|
return (0, import_graphql_parse_resolve_info.parseResolveInfo)(info);
|
|
203
190
|
}
|
|
204
|
-
_resolve_type({
|
|
205
|
-
tree
|
|
206
|
-
query,
|
|
207
|
-
filters
|
|
208
|
-
}) {
|
|
191
|
+
_resolve_type(query, context) {
|
|
192
|
+
const { tree } = context;
|
|
209
193
|
const type = Object.keys(tree.fieldsByTypeName)[0];
|
|
210
194
|
const table_resolver = this.tables[type];
|
|
211
195
|
if (!table_resolver) {
|
|
212
196
|
throw new Error(`Table resolver not found for type ${type}.`);
|
|
213
197
|
}
|
|
214
|
-
return table_resolver({
|
|
198
|
+
return table_resolver(query, { ...context, graph: this, tree, type });
|
|
215
199
|
}
|
|
216
|
-
_resolve_page({
|
|
217
|
-
tree
|
|
218
|
-
query,
|
|
219
|
-
paginator,
|
|
220
|
-
filters
|
|
221
|
-
}) {
|
|
200
|
+
_resolve_page(query, paginator, context) {
|
|
201
|
+
let { tree } = context;
|
|
222
202
|
const { args } = tree;
|
|
223
203
|
for (const field of paginator.path) {
|
|
224
204
|
const type = Object.keys(tree.fieldsByTypeName)[0];
|
|
225
205
|
tree = tree.fieldsByTypeName[type][field];
|
|
226
206
|
}
|
|
227
|
-
query = this._resolve_type({
|
|
207
|
+
query = this._resolve_type(query, { ...context, tree });
|
|
228
208
|
return paginator.paginate(query, args);
|
|
229
209
|
}
|
|
230
210
|
};
|
|
@@ -232,20 +212,20 @@ var GraphResolver = class {
|
|
|
232
212
|
// src/resolvers/page.ts
|
|
233
213
|
var import_orchid_core = require("orchid-core");
|
|
234
214
|
function definePageResolver(paginator, options = {}) {
|
|
235
|
-
const { tableField,
|
|
215
|
+
const { tableField, modify } = options;
|
|
236
216
|
return defineFieldResolver({
|
|
237
|
-
modify
|
|
217
|
+
modify(query, context) {
|
|
218
|
+
const { field, graph } = context;
|
|
238
219
|
let get_page;
|
|
239
220
|
query = query.select({
|
|
240
221
|
[field]: (q) => {
|
|
241
|
-
q = graph._resolve_page(
|
|
242
|
-
|
|
243
|
-
query: q[tableField || field],
|
|
222
|
+
q = graph._resolve_page(
|
|
223
|
+
q[tableField || field],
|
|
244
224
|
paginator,
|
|
245
|
-
|
|
246
|
-
|
|
225
|
+
context
|
|
226
|
+
);
|
|
247
227
|
get_page = get_query_pagination_handler(q);
|
|
248
|
-
return modify ? modify(q,
|
|
228
|
+
return modify ? modify(q, context) : q;
|
|
249
229
|
}
|
|
250
230
|
});
|
|
251
231
|
(0, import_orchid_core.overrideParserInQuery)(query.query, field, get_page);
|
|
@@ -258,16 +238,18 @@ function definePageResolver(paginator, options = {}) {
|
|
|
258
238
|
function defineRelationResolver(options = {}) {
|
|
259
239
|
const { tableField, filters, modify } = options;
|
|
260
240
|
return defineFieldResolver({
|
|
261
|
-
modify
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
241
|
+
modify(query, context) {
|
|
242
|
+
const { graph, field } = context;
|
|
243
|
+
return query.select({
|
|
244
|
+
[field]: (q) => {
|
|
245
|
+
const relation_query = graph._resolve_type(
|
|
246
|
+
q[tableField || field],
|
|
247
|
+
{ ...context, filters }
|
|
248
|
+
);
|
|
249
|
+
return modify ? modify(relation_query, context) : relation_query;
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
}
|
|
271
253
|
});
|
|
272
254
|
}
|
|
273
255
|
|
|
@@ -374,7 +356,8 @@ function defineTableResolver(table, options = {}) {
|
|
|
374
356
|
{}
|
|
375
357
|
) : void 0;
|
|
376
358
|
const { modify, transform } = options;
|
|
377
|
-
return function resolve(
|
|
359
|
+
return function resolve(query, context) {
|
|
360
|
+
const { graph, tree, type, filters } = context;
|
|
378
361
|
if (query.table !== table.table) {
|
|
379
362
|
throw new Error(
|
|
380
363
|
`Mismatching query type (expected ${table.table}, found ${query.table}).`
|
|
@@ -387,7 +370,7 @@ function defineTableResolver(table, options = {}) {
|
|
|
387
370
|
);
|
|
388
371
|
}
|
|
389
372
|
if (modify) {
|
|
390
|
-
query = modify(query,
|
|
373
|
+
query = modify(query, context);
|
|
391
374
|
}
|
|
392
375
|
for (const subtree of Object.values(tree.fieldsByTypeName[type])) {
|
|
393
376
|
const field = subtree.name;
|
|
@@ -396,7 +379,7 @@ function defineTableResolver(table, options = {}) {
|
|
|
396
379
|
throw new Error(`No field resolver defined for field ${type}.${field}`);
|
|
397
380
|
}
|
|
398
381
|
if (r) {
|
|
399
|
-
query = r(query, {
|
|
382
|
+
query = r(query, { ...context, tree: subtree, field });
|
|
400
383
|
}
|
|
401
384
|
}
|
|
402
385
|
const allow_all_filters = options.allowAllFilters ?? graph.options.allowAllFilters;
|
|
@@ -410,7 +393,6 @@ function defineTableResolver(table, options = {}) {
|
|
|
410
393
|
});
|
|
411
394
|
}
|
|
412
395
|
if (transform) {
|
|
413
|
-
const context = get_query_context(query);
|
|
414
396
|
query = run_after_query(query, (instance) => {
|
|
415
397
|
return transform(instance, context);
|
|
416
398
|
});
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/paginators/base.ts","../src/paginators/cursor.ts","../src/utils/query-context.ts","../src/utils/run-after.ts","../src/resolvers/field.ts","../src/resolvers/graph.ts","../src/resolvers/page.ts","../src/resolvers/relation.ts","../src/utils/is-plain-object.ts","../src/filters/filters.ts","../src/resolvers/table.ts"],"sourcesContent":["export {\n\tdefineCursorPaginator as cursor,\n\tdefineCursorPaginator,\n} from \"./paginators/cursor\"\nexport {\n\tdefineFieldResolver,\n\tdefineFieldResolver as field,\n} from \"./resolvers/field\"\nexport {\n\tcreateGraphResolver,\n\tcreateGraphResolver as graph,\n} from \"./resolvers/graph\"\nexport {\n\tdefinePageResolver,\n\tdefinePageResolver as page,\n} from \"./resolvers/page\"\nexport {\n\tdefineRelationResolver,\n\tdefineRelationResolver as relation,\n} from \"./resolvers/relation\"\nexport {\n\tdefineTableResolver,\n\tdefineTableResolver as table,\n} from \"./resolvers/table\"\n","import { Query } from \"pqb\"\n\nexport interface Paginator {\n\t/** GraphQL path, for example: `[\"nodes\"]` */\n\treadonly path: string[]\n\tpaginate(query: Query, ...args: any[]): Query\n}\n\ntype GetPageFn<P> = (nodes: any[]) => P\n\nconst s = Symbol()\n\n// FIXME use proper orchid methods, set result type\nexport function set_query_page_result<P>(query: Query, get_page: GetPageFn<P>) {\n\tfunction _get_page(data: any) {\n\t\tif (!Array.isArray(data)) {\n\t\t\tthrow new Error(`Paginator called for single result query.`)\n\t\t}\n\t\treturn get_page(data)\n\t}\n\n\t// For root query\n\tconst _query = query.query\n\tconst { handleResult } = _query\n\t_query.handleResult = (q, result, s) => {\n\t\tconst data = handleResult(q, result, s)\n\t\t// Sometimes this is called for subqueries, sometimes not.\n\t\t// TODO: investigate and use always.\n\t\treturn s ? data : _get_page(data)\n\t}\n\n\t// For subqueries\n\t;(_query as any)[s] = _get_page\n\n\treturn query\n}\n\nexport function get_query_pagination_handler(query: Query) {\n\treturn (query.query as any)[s]\n}\n","import { Query } from \"pqb\"\n\nimport { Paginator, set_query_page_result } from \"./base\"\n\nexport function defineCursorPaginator(\n\toptions: Partial<CursorPaginatorOptions> = {}\n) {\n\treturn new CursorPaginator(options)\n}\n\nexport interface CursorPaginatorOptions {\n\tfields: string[]\n\ttake: number\n}\n\nexport interface CursorPaginatorArgs {\n\tcursor?: string\n\ttake?: number\n}\n\nexport interface CursorPaginatorPage<M> {\n\tnodes: M[]\n\tcursor?: string\n}\n\nclass CursorPaginator implements Paginator {\n\treadonly path = [\"nodes\"]\n\treadonly options: CursorPaginatorOptions\n\n\treadonly fields: Array<{\n\t\tname: string\n\t\tdesc: boolean\n\t}>\n\n\tconstructor(options: Partial<CursorPaginatorOptions> = {}) {\n\t\tthis.options = {\n\t\t\tfields: [\"id\"],\n\t\t\ttake: 10,\n\t\t\t...options,\n\t\t}\n\t\tthis.fields = this.options.fields.map((field) => {\n\t\t\tif (field.startsWith(\"-\")) {\n\t\t\t\treturn { name: field.slice(1), desc: true }\n\t\t\t} else {\n\t\t\t\treturn { name: field, desc: false }\n\t\t\t}\n\t\t})\n\t}\n\n\tpaginate(query: Query, args: CursorPaginatorArgs = {}) {\n\t\tconst take = args.take ?? this.options.take\n\t\tconst { cursor } = args\n\n\t\t// Set query order\n\t\tquery = query.clear(\"order\")\n\t\tfor (const field of this.fields) {\n\t\t\t// TODO: prevent potential name clash with aliases like .as(`_${table_ref}_order_key_0`)\n\t\t\tquery = query\n\t\t\t\t.select(field.name)\n\t\t\t\t.order({ [field.name]: field.desc ? \"DESC\" : \"ASC\" })\n\t\t}\n\n\t\tif (cursor) {\n\t\t\tquery = this._set_query_cursor(query, cursor)\n\t\t}\n\t\tquery = query.limit(take + 1)\n\n\t\tquery = set_query_page_result(query, (nodes) => {\n\t\t\tlet cursor: string | undefined\n\t\t\tif (nodes.length > take) {\n\t\t\t\tcursor = this._create_cursor(nodes[take - 1])\n\t\t\t\tnodes = nodes.slice(0, take)\n\t\t\t}\n\t\t\treturn { nodes, cursor }\n\t\t})\n\n\t\treturn query\n\t}\n\n\t_create_cursor(instance: any) {\n\t\treturn JSON.stringify(\n\t\t\tthis.fields.map((field) => {\n\t\t\t\tconst value = instance[field.name]\n\t\t\t\tif (value === undefined) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Unable to create cursor: undefined field ${field.name}`\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\treturn String(value)\n\t\t\t})\n\t\t)\n\t}\n\n\t_set_query_cursor(query: Query, cursor: string) {\n\t\tconst values = JSON.parse(cursor)\n\t\tconst left: string[] = []\n\t\tconst right: string[] = []\n\t\tconst expr_values: Record<string, any> = {}\n\t\t// TODO: refactor\n\t\tfor (let i = 0; i < this.fields.length; ++i) {\n\t\t\tconst field = this.fields[i]\n\t\t\tconst expressions = field.desc ? right : left\n\t\t\tconst placeholders = field.desc ? left : right\n\t\t\texpressions.push(`\"${field.name}\"`)\n\t\t\tconst placeholder = `value${i}`\n\t\t\tplaceholders.push(\"$\" + placeholder)\n\t\t\texpr_values[placeholder] = values[i]\n\t\t}\n\t\treturn query.where(\n\t\t\tquery.raw(`(${left.join(\",\")}) > (${right.join(\",\")})`, expr_values)\n\t\t)\n\t}\n}\n","import { Query } from \"pqb\"\n\nconst graphql = Symbol(\"graphql\")\n\nexport function set_query_context(query: Query, context: any) {\n\t;(query.query as any)[graphql] = context\n}\n\nexport function get_query_context(query: Query) {\n\treturn (query.query as any)[graphql]\n}\n","import { Query } from \"pqb\"\n\nexport function run_after_query(\n\tquery: Query,\n\ttransform: (instance: any) => any\n) {\n\treturn query.afterQuery(async (_, result) => {\n\t\t// FIXME: returning is ignored in orchid (unlike objection). Add some hacks instead.\n\t\tif (Array.isArray(result)) {\n\t\t\tconst result1 = await Promise.all(result.map(transform))\n\t\t\treturn result1.filter(Boolean) // Skip empty objects\n\t\t} else if (result) {\n\t\t\treturn transform(result)\n\t\t} else {\n\t\t\t// Supposedly, single query builder returning NULL\n\t\t\treturn result\n\t\t}\n\t})\n}\n","import { ResolveTree } from \"graphql-parse-resolve-info\"\nimport { Query } from \"pqb\"\n\nimport { get_query_context } from \"../utils/query-context\"\nimport { run_after_query } from \"../utils/run-after\"\nimport type { GraphResolver } from \"./graph\"\n\n/* A function that modifies the query to select a field. */\nexport type FieldResolverFn = (\n\tquery: Query,\n\toptions: {\n\t\t/** GraphQL field */\n\t\tfield: string\n\t\t/** GraphQL resolve tree */\n\t\ttree: ResolveTree\n\t\t/** Current graph resolver */\n\t\tgraph: GraphResolver\n\t}\n) => Query\n\nexport interface FieldResolverOptions {\n\t/** Table field (if different from GraphQL field) */\n\ttableField?: string\n\t/** Custom query modifier (if different than simply selecting a field). */\n\tmodify?: FieldResolverFn\n\t/** Post-process selected value. Return a new value or a promise. */\n\ttransform?(value: any, instance: any, context: any): any\n}\n\nexport function defineFieldResolver(\n\toptions: FieldResolverOptions = {}\n): FieldResolverFn {\n\tconst { tableField, modify, transform } = options\n\n\treturn function resolve(query, options) {\n\t\tconst { field } = options\n\t\tif (modify) {\n\t\t\treturn modify(query, options)\n\t\t} else {\n\t\t\tquery = query.select({ [field]: tableField || field })\n\t\t}\n\t\tif (transform) {\n\t\t\tconst context = get_query_context(query)\n\t\t\tquery = run_after_query(query, async (instance) => {\n\t\t\t\tinstance[field] = await transform(instance[field], instance, context)\n\t\t\t\treturn instance\n\t\t\t})\n\t\t}\n\t\treturn query\n\t}\n}\n","import { GraphQLResolveInfo } from \"graphql\"\nimport { parseResolveInfo, ResolveTree } from \"graphql-parse-resolve-info\"\nimport { Query } from \"pqb\"\n\nimport { FiltersDef } from \"../filters/filters\"\nimport { Paginator } from \"../paginators/base\"\nimport { set_query_context } from \"../utils/query-context\"\nimport type { TableResolverFn, TableResolverOptions } from \"./table\"\n\nexport type GraphResolverOptions = Pick<\n\tTableResolverOptions,\n\t\"allowAllFields\" | \"allowAllFilters\"\n>\n\nexport function createGraphResolver(\n\ttypes: Record<string, TableResolverFn>,\n\toptions?: GraphResolverOptions\n) {\n\treturn new GraphResolver(types, options)\n}\n\nexport interface GraphResolveOptions {\n\tinfo: GraphQLResolveInfo\n\tcontext: any\n\tfilters?: FiltersDef\n}\n\nexport class GraphResolver {\n\tconstructor(\n\t\tpublic readonly tables: Record<string, TableResolverFn>,\n\t\tpublic readonly options: GraphResolverOptions = {}\n\t) {}\n\n\tresolve(query: Query, { info, context, filters }: GraphResolveOptions) {\n\t\tset_query_context(query, context)\n\t\tconst tree = this._get_resolve_tree(info)\n\t\treturn this._resolve_type({ query, tree, filters })\n\t}\n\n\tresolvePage(\n\t\tquery: Query,\n\t\tpaginator: Paginator,\n\t\t{ info, context, filters }: GraphResolveOptions\n\t) {\n\t\tset_query_context(query, context)\n\t\tconst tree = this._get_resolve_tree(info)\n\t\treturn this._resolve_page({ query, tree, paginator, filters })\n\t}\n\n\t_get_resolve_tree(info: GraphQLResolveInfo) {\n\t\treturn parseResolveInfo(info) as ResolveTree\n\t}\n\n\t_resolve_type({\n\t\ttree,\n\t\tquery,\n\t\tfilters,\n\t}: {\n\t\tquery: Query\n\t\ttree: ResolveTree\n\t\tfilters?: FiltersDef\n\t}) {\n\t\tconst type = Object.keys(tree.fieldsByTypeName)[0]\n\t\tconst table_resolver = this.tables[type]\n\t\tif (!table_resolver) {\n\t\t\tthrow new Error(`Table resolver not found for type ${type}.`)\n\t\t}\n\t\treturn table_resolver({ tree, type, query, filters, graph: this })\n\t}\n\n\t_resolve_page({\n\t\ttree,\n\t\tquery,\n\t\tpaginator,\n\t\tfilters,\n\t}: {\n\t\tquery: Query\n\t\ttree: ResolveTree\n\t\tpaginator: Paginator\n\t\tfilters?: FiltersDef\n\t}) {\n\t\tconst { args } = tree\n\t\t// Skip page subtree(s)\n\t\tfor (const field of paginator.path) {\n\t\t\tconst type = Object.keys(tree.fieldsByTypeName)[0]\n\t\t\ttree = tree.fieldsByTypeName[type][field]\n\t\t\t// TODO: raise exception if not found\n\t\t}\n\t\tquery = this._resolve_type({ query, tree, filters })\n\t\treturn paginator.paginate(query, args)\n\t}\n}\n","import { overrideParserInQuery } from \"orchid-core\"\n\nimport { get_query_pagination_handler, Paginator } from \"../paginators/base\"\nimport { defineFieldResolver } from \"./field\"\nimport { RelationResolverOptions } from \"./relation\"\n\nexport function definePageResolver(\n\tpaginator: Paginator,\n\toptions: RelationResolverOptions = {}\n) {\n\tconst { tableField, filters, modify } = options\n\n\treturn defineFieldResolver({\n\t\tmodify: (query, { field, tree, graph }) => {\n\t\t\t// FIXME: refactor, add types\n\t\t\tlet get_page: any\n\t\t\tquery = query.select({\n\t\t\t\t[field]: (q) => {\n\t\t\t\t\tq = graph._resolve_page({\n\t\t\t\t\t\ttree,\n\t\t\t\t\t\tquery: (q as any)[tableField || field],\n\t\t\t\t\t\tpaginator,\n\t\t\t\t\t\tfilters,\n\t\t\t\t\t})\n\t\t\t\t\t// Calling addParserToQuery here doesn't work, save the paginator for later.\n\t\t\t\t\tget_page = get_query_pagination_handler(q)\n\t\t\t\t\treturn modify ? modify(q, tree) : q\n\t\t\t\t},\n\t\t\t})\n\t\t\toverrideParserInQuery(query.query, field, get_page)\n\t\t\treturn query\n\t\t},\n\t})\n}\n","import { FiltersDef } from \"../filters/filters\"\nimport { defineFieldResolver, FieldResolverOptions } from \"./field\"\nimport { QueryTreeModifier } from \"./table\"\n\nexport interface RelationResolverOptions\n\textends Omit<FieldResolverOptions, \"modify\"> {\n\tfilters?: FiltersDef\n\tmodify?: QueryTreeModifier\n}\n\nexport function defineRelationResolver(options: RelationResolverOptions = {}) {\n\tconst { tableField, filters, modify } = options\n\n\treturn defineFieldResolver({\n\t\tmodify: (query, { field, tree, graph }) =>\n\t\t\tquery.select({\n\t\t\t\t[field]: (q) => {\n\t\t\t\t\tconst relation_query = graph._resolve_type({\n\t\t\t\t\t\ttree,\n\t\t\t\t\t\tquery: (q as any)[tableField || field],\n\t\t\t\t\t\tfilters,\n\t\t\t\t\t})\n\t\t\t\t\treturn modify ? modify(relation_query, tree) : relation_query\n\t\t\t\t},\n\t\t\t}),\n\t})\n}\n","export function is_plain_object(obj: any): obj is Record<string, any> {\n\treturn typeof obj === \"object\" && !Array.isArray(obj)\n}\n","import { Query } from \"pqb\"\n\nimport { is_plain_object } from \"../utils/is-plain-object\"\nimport { Modifier } from \"../utils/modifier\"\n\n// TODO: per-field definitions\nexport type FiltersDef = boolean\n\nexport type FilterScalarValue = null | string | number | boolean\nexport type FilterValue = FilterScalarValue | Exclude<FilterScalarValue, null>[]\nexport type Filter = { [property: string]: FilterValue }\n\nfunction is_scalar(value: any) {\n\treturn (\n\t\tvalue === null ||\n\t\ttypeof value === \"string\" ||\n\t\ttypeof value === \"number\" ||\n\t\ttypeof value === \"boolean\"\n\t)\n}\n\nexport function apply_filters({\n\tquery,\n\tfilters,\n\targs,\n\tmodifiers,\n}: {\n\tquery: Query\n\tfilters: FiltersDef\n\targs?: Record<string, any>\n\tmodifiers?: Record<string, Modifier>\n}) {\n\tif (!filters) {\n\t\treturn query\n\t}\n\tconst filter_obj = args?.filter\n\tif (!filter_obj) {\n\t\treturn query\n\t}\n\tif (!is_plain_object(filter_obj)) {\n\t\tthrow new Error(`Invalid filter: ${filter_obj}, must be object.`)\n\t}\n\tfor (const [field, value] of Object.entries(filter_obj)) {\n\t\tif (value === undefined) {\n\t\t\t// Support optional GraphQL arguments in filter\n\t\t\tcontinue\n\t\t}\n\t\tif (modifiers?.[field]) {\n\t\t\tquery = modifiers[field](query, value)\n\t\t} else {\n\t\t\t// Normal filter\n\t\t\tconst [dbfield, op0] = field.split(\"__\")\n\t\t\tconst op = op0?.toLowerCase() || \"exact\"\n\t\t\tif (\n\t\t\t\t// TODO: add the rest of orchid ops (or allow all of them?)\n\t\t\t\t[\n\t\t\t\t\t\"exact\",\n\t\t\t\t\t\"lt\",\n\t\t\t\t\t\"lte\",\n\t\t\t\t\t\"gt\",\n\t\t\t\t\t\"gte\",\n\t\t\t\t\t\"like\",\n\t\t\t\t\t\"ilike\",\n\t\t\t\t\t\"contains\",\n\t\t\t\t].includes(op)\n\t\t\t) {\n\t\t\t\tif (!is_scalar(value)) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Unsupported filter value for ${query.table}.${field}: must be scalar.`\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tif (op === \"exact\") {\n\t\t\t\t\tquery = query.where({ [dbfield]: value })\n\t\t\t\t} else {\n\t\t\t\t\tconst query_op = { like: \"contains\" }[op] || op\n\t\t\t\t\tquery = query.where({ [dbfield]: { [query_op]: value } })\n\t\t\t\t}\n\t\t\t} else if (op === \"in\") {\n\t\t\t\tif (!(Array.isArray(value) && value.every(is_scalar))) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Invalid filter value for ${query.table}.${field}: must be an array of scalars.`\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tquery = query.whereIn(dbfield, value)\n\t\t\t} else {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Invalid filter ${query.table}.${field}: unsupported operator ${op}.`\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t}\n\treturn query\n}\n","import { ResolveTree } from \"graphql-parse-resolve-info\"\nimport { DbTable, TableClass } from \"orchid-orm\"\nimport { Query } from \"pqb\"\n\nimport { apply_filters, FiltersDef } from \"../filters/filters\"\nimport { Modifier } from \"../utils/modifier\"\nimport { get_query_context } from \"../utils/query-context\"\nimport { run_after_query } from \"../utils/run-after\"\nimport { defineFieldResolver, FieldResolverFn } from \"./field\"\nimport type { GraphResolver } from \"./graph\"\nimport { defineRelationResolver } from \"./relation\"\n\n/** A function that modifies the query to select fields/relations and filter the result set. */\nexport type TableResolverFn = (args: {\n\ttree: ResolveTree\n\t/** GraphQL type */\n\ttype: string\n\tquery: Query\n\tfilters?: FiltersDef\n\tgraph: GraphResolver\n}) => Query\n\nexport interface TableResolverOptions {\n\t/** allow to resolve all table fields without explicitly listing them */\n\tallowAllFields?: boolean\n\t/** allow to filter all table fields without explicitly listing them */\n\tallowAllFilters?: boolean\n\tfields?: Record<string, SimpleFieldResolver>\n\tmodifiers?: Record<string, Modifier>\n\tmodify?: QueryTreeModifier\n\ttransform?(instance: any, context: any): void | PromiseLike<void>\n}\n\nexport type SimpleFieldResolver = true | string | FieldResolverFn\n\nexport type QueryTreeModifier = (query: Query, tree: ResolveTree) => Query\n\nexport function defineTableResolver<T extends TableClass>(\n\ttable: DbTable<T>,\n\toptions: TableResolverOptions = {}\n): TableResolverFn {\n\t/**\n\t * Create a field resolver which will modify the query to resolve a GraphQL field.\n\t */\n\tconst get_default_field_resolver = (\n\t\tfield: string,\n\t\ttableField?: string\n\t): FieldResolverFn | undefined => {\n\t\tconst table_field_lookup = tableField || field\n\t\tif (options.modifiers?.[table_field_lookup]) {\n\t\t\t// Keep query as is.\n\t\t\treturn (query) => query\n\t\t} else if ((table.relations as any)?.[table_field_lookup]) {\n\t\t\treturn defineRelationResolver({ tableField })\n\t\t} else {\n\t\t\treturn defineFieldResolver({ tableField })\n\t\t}\n\t}\n\n\t// Pre-create field resolvers\n\tconst { fields } = options\n\tconst table_field_resolvers = fields\n\t\t? Object.keys(fields).reduce<Record<string, FieldResolverFn>>(\n\t\t\t\t(resolvers, field) => {\n\t\t\t\t\tconst r0 = fields[field]\n\t\t\t\t\tconst r: FieldResolverFn | undefined =\n\t\t\t\t\t\ttypeof r0 === \"function\"\n\t\t\t\t\t\t\t? r0\n\t\t\t\t\t\t\t: r0 === true\n\t\t\t\t\t\t\t? get_default_field_resolver(field)\n\t\t\t\t\t\t\t: typeof r0 === \"string\"\n\t\t\t\t\t\t\t? get_default_field_resolver(field, r0)\n\t\t\t\t\t\t\t: undefined\n\t\t\t\t\tif (r === undefined) {\n\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t`Field resolver must be a function, string, or true; found ${r0}`\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t\tresolvers[field] = r\n\t\t\t\t\treturn resolvers\n\t\t\t\t},\n\t\t\t\t{}\n\t\t )\n\t\t: undefined\n\n\tconst { modify, transform } = options\n\n\treturn function resolve({ tree, type, query, filters, graph }) {\n\t\tif (query.table !== table.table) {\n\t\t\tthrow new Error(\n\t\t\t\t`Mismatching query type (expected ${table.table}, found ${query.table}).`\n\t\t\t)\n\t\t}\n\n\t\tconst allow_all_fields =\n\t\t\toptions.allowAllFields ??\n\t\t\tgraph.options.allowAllFields ??\n\t\t\ttable_field_resolvers === undefined\n\n\t\tif (!allow_all_fields && !table_field_resolvers) {\n\t\t\tthrow new Error(\n\t\t\t\t`Table resolver for ${type} must either allow all fields or specify options.fields`\n\t\t\t)\n\t\t}\n\n\t\tif (modify) {\n\t\t\tquery = modify(query, tree)\n\t\t}\n\n\t\tfor (const subtree of Object.values(tree.fieldsByTypeName[type])) {\n\t\t\tconst field = subtree.name\n\t\t\tconst r =\n\t\t\t\ttable_field_resolvers?.[field] ||\n\t\t\t\t(allow_all_fields ? get_default_field_resolver(field) : undefined)\n\t\t\tif (!r) {\n\t\t\t\tthrow new Error(`No field resolver defined for field ${type}.${field}`)\n\t\t\t}\n\t\t\tif (r) {\n\t\t\t\tquery = r(query, { field, tree: subtree, graph })\n\t\t\t}\n\t\t}\n\n\t\tconst allow_all_filters =\n\t\t\toptions.allowAllFilters ?? graph.options.allowAllFilters\n\n\t\tconst effective_filters = allow_all_filters ? true : filters\n\n\t\tif (effective_filters) {\n\t\t\tquery = apply_filters({\n\t\t\t\tquery,\n\t\t\t\tfilters: effective_filters,\n\t\t\t\targs: tree.args,\n\t\t\t\tmodifiers: options.modifiers,\n\t\t\t})\n\t\t}\n\n\t\tif (transform) {\n\t\t\tconst context = get_query_context(query)\n\t\t\tquery = run_after_query(query, (instance) => {\n\t\t\t\treturn transform(instance, context)\n\t\t\t})\n\t\t}\n\n\t\treturn query\n\t}\n}\n"],"mappings":"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","names":["s","cursor","options"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/paginators/base.ts","../src/paginators/cursor.ts","../src/utils/run-after.ts","../src/resolvers/field.ts","../src/resolvers/graph.ts","../src/resolvers/page.ts","../src/resolvers/relation.ts","../src/utils/is-plain-object.ts","../src/filters/filters.ts","../src/resolvers/table.ts"],"sourcesContent":["export {\n\tdefineCursorPaginator as cursor,\n\tdefineCursorPaginator,\n} from \"./paginators/cursor\"\nexport {\n\tdefineFieldResolver,\n\tdefineFieldResolver as field,\n} from \"./resolvers/field\"\nexport {\n\tcreateGraphResolver,\n\tcreateGraphResolver as graph,\n} from \"./resolvers/graph\"\nexport {\n\tdefinePageResolver,\n\tdefinePageResolver as page,\n} from \"./resolvers/page\"\nexport {\n\tdefineRelationResolver,\n\tdefineRelationResolver as relation,\n} from \"./resolvers/relation\"\nexport {\n\tdefineTableResolver,\n\tdefineTableResolver as table,\n} from \"./resolvers/table\"\n","import { Query } from \"pqb\"\n\nexport interface Paginator {\n\t/** GraphQL path, for example: `[\"nodes\"]` */\n\treadonly path: string[]\n\tpaginate(query: Query, ...args: any[]): Query\n}\n\ntype GetPageFn<P> = (nodes: any[]) => P\n\nconst s = Symbol()\n\n// FIXME use proper orchid methods, set result type\nexport function set_query_page_result<P>(query: Query, get_page: GetPageFn<P>) {\n\tfunction _get_page(data: any) {\n\t\tif (!Array.isArray(data)) {\n\t\t\tthrow new Error(`Paginator called for single result query.`)\n\t\t}\n\t\treturn get_page(data)\n\t}\n\n\t// For root query\n\tconst _query = query.query\n\tconst { handleResult } = _query\n\t_query.handleResult = (q, result, s) => {\n\t\tconst data = handleResult(q, result, s)\n\t\t// Sometimes this is called for subqueries, sometimes not.\n\t\t// TODO: investigate and use always.\n\t\treturn s ? data : _get_page(data)\n\t}\n\n\t// For subqueries\n\t;(_query as any)[s] = _get_page\n\n\treturn query\n}\n\nexport function get_query_pagination_handler(query: Query) {\n\treturn (query.query as any)[s]\n}\n","import { Query } from \"pqb\"\n\nimport { Paginator, set_query_page_result } from \"./base\"\n\nexport function defineCursorPaginator(\n\toptions: Partial<CursorPaginatorOptions> = {}\n) {\n\treturn new CursorPaginator(options)\n}\n\nexport interface CursorPaginatorOptions {\n\tfields: string[]\n\ttake: number\n}\n\nexport interface CursorPaginatorArgs {\n\tcursor?: string\n\ttake?: number\n}\n\nexport interface CursorPaginatorPage<M> {\n\tnodes: M[]\n\tcursor?: string\n}\n\nclass CursorPaginator implements Paginator {\n\treadonly path = [\"nodes\"]\n\treadonly options: CursorPaginatorOptions\n\n\treadonly fields: Array<{\n\t\tname: string\n\t\tdesc: boolean\n\t}>\n\n\tconstructor(options: Partial<CursorPaginatorOptions> = {}) {\n\t\tthis.options = {\n\t\t\tfields: [\"id\"],\n\t\t\ttake: 10,\n\t\t\t...options,\n\t\t}\n\t\tthis.fields = this.options.fields.map((field) => {\n\t\t\tif (field.startsWith(\"-\")) {\n\t\t\t\treturn { name: field.slice(1), desc: true }\n\t\t\t} else {\n\t\t\t\treturn { name: field, desc: false }\n\t\t\t}\n\t\t})\n\t}\n\n\tpaginate(query: Query, args: CursorPaginatorArgs = {}) {\n\t\tconst take = args.take ?? this.options.take\n\t\tconst { cursor } = args\n\n\t\t// Set query order\n\t\tquery = query.clear(\"order\")\n\t\tfor (const field of this.fields) {\n\t\t\t// TODO: prevent potential name clash with aliases like .as(`_${table_ref}_order_key_0`)\n\t\t\tquery = query\n\t\t\t\t.select(field.name)\n\t\t\t\t.order({ [field.name]: field.desc ? \"DESC\" : \"ASC\" })\n\t\t}\n\n\t\tif (cursor) {\n\t\t\tquery = this._set_query_cursor(query, cursor)\n\t\t}\n\t\tquery = query.limit(take + 1)\n\n\t\tquery = set_query_page_result(query, (nodes) => {\n\t\t\tlet cursor: string | undefined\n\t\t\tif (nodes.length > take) {\n\t\t\t\tcursor = this._create_cursor(nodes[take - 1])\n\t\t\t\tnodes = nodes.slice(0, take)\n\t\t\t}\n\t\t\treturn { nodes, cursor }\n\t\t})\n\n\t\treturn query\n\t}\n\n\t_create_cursor(instance: any) {\n\t\treturn JSON.stringify(\n\t\t\tthis.fields.map((field) => {\n\t\t\t\tconst value = instance[field.name]\n\t\t\t\tif (value === undefined) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Unable to create cursor: undefined field ${field.name}`\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\treturn String(value)\n\t\t\t})\n\t\t)\n\t}\n\n\t_set_query_cursor(query: Query, cursor: string) {\n\t\tconst values = JSON.parse(cursor)\n\t\tconst left: string[] = []\n\t\tconst right: string[] = []\n\t\tconst expr_values: Record<string, any> = {}\n\t\t// TODO: refactor\n\t\tfor (let i = 0; i < this.fields.length; ++i) {\n\t\t\tconst field = this.fields[i]\n\t\t\tconst expressions = field.desc ? right : left\n\t\t\tconst placeholders = field.desc ? left : right\n\t\t\texpressions.push(`\"${field.name}\"`)\n\t\t\tconst placeholder = `value${i}`\n\t\t\tplaceholders.push(\"$\" + placeholder)\n\t\t\texpr_values[placeholder] = values[i]\n\t\t}\n\t\treturn query.where(\n\t\t\tquery.raw(`(${left.join(\",\")}) > (${right.join(\",\")})`, expr_values)\n\t\t)\n\t}\n}\n","import { Query } from \"pqb\"\n\nexport function run_after_query(\n\tquery: Query,\n\ttransform: (instance: any) => any\n) {\n\treturn query.afterQuery(async (_, result) => {\n\t\t// FIXME: returning is ignored in orchid (unlike objection). Add some hacks instead.\n\t\tif (Array.isArray(result)) {\n\t\t\tconst result1 = await Promise.all(result.map(transform))\n\t\t\treturn result1.filter(Boolean) // Skip empty objects\n\t\t} else if (result) {\n\t\t\treturn transform(result)\n\t\t} else {\n\t\t\t// Supposedly, single query builder returning NULL\n\t\t\treturn result\n\t\t}\n\t})\n}\n","import { Query } from \"pqb\"\n\nimport { run_after_query } from \"../utils/run-after\"\nimport { TableResolveContext } from \"./table\"\n\nexport interface FieldResolveContext extends TableResolveContext {\n\t/** GraphQL field */\n\tfield: string\n}\n\n/* A function that modifies the query to select a field. */\nexport type FieldResolverFn = (\n\tquery: Query,\n\tcontext: FieldResolveContext\n) => Query\n\nexport interface FieldResolverOptions {\n\t/** Table field (if different from GraphQL field) */\n\ttableField?: string\n\t/** Custom query modifier (if different than simply selecting a field). */\n\tmodify?: FieldResolverFn\n\t/** Post-process selected value. Return a new value or a promise. */\n\ttransform?(value: any, instance: any, context: FieldResolveContext): any\n}\n\nexport function defineFieldResolver(\n\toptions: FieldResolverOptions = {}\n): FieldResolverFn {\n\tconst { tableField, modify, transform } = options\n\n\treturn function resolve(query, context) {\n\t\tconst { field } = context\n\t\tif (modify) {\n\t\t\tquery = modify(query, context)\n\t\t} else {\n\t\t\tquery = query.select({ [field]: tableField || field })\n\t\t}\n\t\tif (transform) {\n\t\t\tquery = run_after_query(query, async (instance) => {\n\t\t\t\tinstance[field] = await transform(instance[field], instance, context)\n\t\t\t\treturn instance\n\t\t\t})\n\t\t}\n\t\treturn query\n\t}\n}\n","import { GraphQLResolveInfo } from \"graphql\"\nimport { parseResolveInfo, ResolveTree } from \"graphql-parse-resolve-info\"\nimport { Query } from \"pqb\"\n\nimport { FiltersDef } from \"../filters/filters\"\nimport { Paginator } from \"../paginators/base\"\nimport type { TableResolverFn, TableResolverOptions } from \"./table\"\n\nexport type GraphResolverOptions = Pick<\n\tTableResolverOptions,\n\t\"allowAllFields\" | \"allowAllFilters\"\n>\n\nexport function createGraphResolver(\n\ttypes: Record<string, TableResolverFn>,\n\toptions?: GraphResolverOptions\n) {\n\treturn new GraphResolver(types, options)\n}\n\nexport interface GraphResolveOptions {\n\tinfo: GraphQLResolveInfo\n\tcontext: any\n\tfilters?: FiltersDef\n}\n\nexport interface GraphResolveContext {\n\ttree: ResolveTree\n\tfilters?: FiltersDef\n\tcontext: any\n}\n\nexport class GraphResolver {\n\tconstructor(\n\t\tpublic readonly tables: Record<string, TableResolverFn>,\n\t\tpublic readonly options: GraphResolverOptions = {}\n\t) {}\n\n\tresolve(query: Query, { info, context, filters }: GraphResolveOptions) {\n\t\tconst tree = this._get_resolve_tree(info)\n\t\treturn this._resolve_type(query, { tree, filters, context })\n\t}\n\n\tresolvePage(\n\t\tquery: Query,\n\t\tpaginator: Paginator,\n\t\t{ info, context, filters }: GraphResolveOptions\n\t) {\n\t\tconst tree = this._get_resolve_tree(info)\n\t\treturn this._resolve_page(query, paginator, { tree, filters, context })\n\t}\n\n\t_get_resolve_tree(info: GraphQLResolveInfo) {\n\t\treturn parseResolveInfo(info) as ResolveTree\n\t}\n\n\t_resolve_type(query: Query, context: GraphResolveContext) {\n\t\tconst { tree } = context\n\t\tconst type = Object.keys(tree.fieldsByTypeName)[0]\n\t\tconst table_resolver = this.tables[type]\n\t\tif (!table_resolver) {\n\t\t\tthrow new Error(`Table resolver not found for type ${type}.`)\n\t\t}\n\t\treturn table_resolver(query, { ...context, graph: this, tree, type })\n\t}\n\n\t_resolve_page(\n\t\tquery: Query,\n\t\tpaginator: Paginator,\n\t\tcontext: GraphResolveContext\n\t) {\n\t\tlet { tree } = context\n\t\t// Save pagination args\n\t\tconst { args } = tree\n\t\t// Skip page subtree(s)\n\t\tfor (const field of paginator.path) {\n\t\t\tconst type = Object.keys(tree.fieldsByTypeName)[0]\n\t\t\ttree = tree.fieldsByTypeName[type][field]\n\t\t\t// TODO: raise exception if not found\n\t\t}\n\t\tquery = this._resolve_type(query, { ...context, tree })\n\t\treturn paginator.paginate(query, args)\n\t}\n}\n","import { overrideParserInQuery } from \"orchid-core\"\n\nimport { get_query_pagination_handler, Paginator } from \"../paginators/base\"\nimport { defineFieldResolver } from \"./field\"\nimport { RelationResolverOptions } from \"./relation\"\n\nexport function definePageResolver(\n\tpaginator: Paginator,\n\toptions: RelationResolverOptions = {}\n) {\n\tconst { tableField, modify } = options\n\n\treturn defineFieldResolver({\n\t\tmodify(query, context) {\n\t\t\tconst { field, graph } = context\n\t\t\t// FIXME: refactor, add types\n\t\t\tlet get_page: any\n\t\t\tquery = query.select({\n\t\t\t\t[field]: (q) => {\n\t\t\t\t\tq = graph._resolve_page(\n\t\t\t\t\t\t(q as any)[tableField || field],\n\t\t\t\t\t\tpaginator,\n\t\t\t\t\t\tcontext\n\t\t\t\t\t)\n\t\t\t\t\t// Calling addParserToQuery here doesn't work, save the paginator for later.\n\t\t\t\t\tget_page = get_query_pagination_handler(q)\n\t\t\t\t\treturn modify ? modify(q, context) : q\n\t\t\t\t},\n\t\t\t})\n\t\t\toverrideParserInQuery(query.query, field, get_page)\n\t\t\treturn query\n\t\t},\n\t})\n}\n","import { FiltersDef } from \"../filters/filters\"\nimport { defineFieldResolver, FieldResolverOptions } from \"./field\"\nimport { TableResolverFn } from \"./table\"\n\nexport interface RelationResolverOptions\n\textends Omit<FieldResolverOptions, \"modify\"> {\n\tfilters?: FiltersDef\n\tmodify?: TableResolverFn\n}\n\nexport function defineRelationResolver(options: RelationResolverOptions = {}) {\n\tconst { tableField, filters, modify } = options\n\n\treturn defineFieldResolver({\n\t\tmodify(query, context) {\n\t\t\tconst { graph, field } = context\n\t\t\treturn query.select({\n\t\t\t\t[field]: (q) => {\n\t\t\t\t\tconst relation_query = graph._resolve_type(\n\t\t\t\t\t\t(q as any)[tableField || field],\n\t\t\t\t\t\t{ ...context, filters }\n\t\t\t\t\t)\n\t\t\t\t\treturn modify ? modify(relation_query, context) : relation_query\n\t\t\t\t},\n\t\t\t})\n\t\t},\n\t})\n}\n","export function is_plain_object(obj: any): obj is Record<string, any> {\n\treturn typeof obj === \"object\" && !Array.isArray(obj)\n}\n","import { Query } from \"pqb\"\n\nimport { is_plain_object } from \"../utils/is-plain-object\"\nimport { Modifier } from \"../utils/modifier\"\n\n// TODO: per-field definitions\nexport type FiltersDef = boolean\n\nexport type FilterScalarValue = null | string | number | boolean\nexport type FilterValue = FilterScalarValue | Exclude<FilterScalarValue, null>[]\nexport type Filter = { [property: string]: FilterValue }\n\nfunction is_scalar(value: any) {\n\treturn (\n\t\tvalue === null ||\n\t\ttypeof value === \"string\" ||\n\t\ttypeof value === \"number\" ||\n\t\ttypeof value === \"boolean\"\n\t)\n}\n\nexport function apply_filters({\n\tquery,\n\tfilters,\n\targs,\n\tmodifiers,\n}: {\n\tquery: Query\n\tfilters: FiltersDef\n\targs?: Record<string, any>\n\tmodifiers?: Record<string, Modifier>\n}) {\n\tif (!filters) {\n\t\treturn query\n\t}\n\tconst filter_obj = args?.filter\n\tif (!filter_obj) {\n\t\treturn query\n\t}\n\tif (!is_plain_object(filter_obj)) {\n\t\tthrow new Error(`Invalid filter: ${filter_obj}, must be object.`)\n\t}\n\tfor (const [field, value] of Object.entries(filter_obj)) {\n\t\tif (value === undefined) {\n\t\t\t// Support optional GraphQL arguments in filter\n\t\t\tcontinue\n\t\t}\n\t\tif (modifiers?.[field]) {\n\t\t\tquery = modifiers[field](query, value)\n\t\t} else {\n\t\t\t// Normal filter\n\t\t\tconst [dbfield, op0] = field.split(\"__\")\n\t\t\tconst op = op0?.toLowerCase() || \"exact\"\n\t\t\tif (\n\t\t\t\t// TODO: add the rest of orchid ops (or allow all of them?)\n\t\t\t\t[\n\t\t\t\t\t\"exact\",\n\t\t\t\t\t\"lt\",\n\t\t\t\t\t\"lte\",\n\t\t\t\t\t\"gt\",\n\t\t\t\t\t\"gte\",\n\t\t\t\t\t\"like\",\n\t\t\t\t\t\"ilike\",\n\t\t\t\t\t\"contains\",\n\t\t\t\t].includes(op)\n\t\t\t) {\n\t\t\t\tif (!is_scalar(value)) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Unsupported filter value for ${query.table}.${field}: must be scalar.`\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tif (op === \"exact\") {\n\t\t\t\t\tquery = query.where({ [dbfield]: value })\n\t\t\t\t} else {\n\t\t\t\t\tconst query_op = { like: \"contains\" }[op] || op\n\t\t\t\t\tquery = query.where({ [dbfield]: { [query_op]: value } })\n\t\t\t\t}\n\t\t\t} else if (op === \"in\") {\n\t\t\t\tif (!(Array.isArray(value) && value.every(is_scalar))) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Invalid filter value for ${query.table}.${field}: must be an array of scalars.`\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tquery = query.whereIn(dbfield, value)\n\t\t\t} else {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Invalid filter ${query.table}.${field}: unsupported operator ${op}.`\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t}\n\treturn query\n}\n","import { DbTable, TableClass } from \"orchid-orm\"\nimport { Query } from \"pqb\"\n\nimport { apply_filters } from \"../filters/filters\"\nimport { Modifier } from \"../utils/modifier\"\nimport { run_after_query } from \"../utils/run-after\"\nimport { defineFieldResolver, FieldResolverFn } from \"./field\"\nimport type { GraphResolveContext, GraphResolver } from \"./graph\"\nimport { defineRelationResolver } from \"./relation\"\n\nexport interface TableResolveContext extends GraphResolveContext {\n\tgraph: GraphResolver\n\t/** GraphQL type */\n\ttype: string\n}\n\n/** A function that modifies the query to select fields/relations and filter the result set. */\nexport type TableResolverFn = (\n\tquery: Query,\n\tcontext: TableResolveContext\n) => Query\n\nexport interface TableResolverOptions {\n\t/** allow to resolve all table fields without explicitly listing them */\n\tallowAllFields?: boolean\n\t/** allow to filter all table fields without explicitly listing them */\n\tallowAllFilters?: boolean\n\tfields?: Record<string, SimpleFieldResolver>\n\tmodifiers?: Record<string, Modifier>\n\tmodify?: TableResolverFn\n\ttransform?(instance: any, context: any): void | PromiseLike<void>\n}\n\nexport type SimpleFieldResolver = true | string | FieldResolverFn\n\nexport function defineTableResolver<T extends TableClass>(\n\ttable: DbTable<T>,\n\toptions: TableResolverOptions = {}\n): TableResolverFn {\n\t/**\n\t * Create a field resolver which will modify the query to resolve a GraphQL field.\n\t */\n\tconst get_default_field_resolver = (\n\t\tfield: string,\n\t\ttableField?: string\n\t): FieldResolverFn | undefined => {\n\t\tconst table_field_lookup = tableField || field\n\t\tif (options.modifiers?.[table_field_lookup]) {\n\t\t\t// Keep query as is.\n\t\t\treturn (query) => query\n\t\t} else if ((table.relations as any)?.[table_field_lookup]) {\n\t\t\treturn defineRelationResolver({ tableField })\n\t\t} else {\n\t\t\treturn defineFieldResolver({ tableField })\n\t\t}\n\t}\n\n\t// Pre-create field resolvers\n\tconst { fields } = options\n\tconst table_field_resolvers = fields\n\t\t? Object.keys(fields).reduce<Record<string, FieldResolverFn>>(\n\t\t\t\t(resolvers, field) => {\n\t\t\t\t\tconst r0 = fields[field]\n\t\t\t\t\tconst r: FieldResolverFn | undefined =\n\t\t\t\t\t\ttypeof r0 === \"function\"\n\t\t\t\t\t\t\t? r0\n\t\t\t\t\t\t\t: r0 === true\n\t\t\t\t\t\t\t? get_default_field_resolver(field)\n\t\t\t\t\t\t\t: typeof r0 === \"string\"\n\t\t\t\t\t\t\t? get_default_field_resolver(field, r0)\n\t\t\t\t\t\t\t: undefined\n\t\t\t\t\tif (r === undefined) {\n\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t`Field resolver must be a function, string, or true; found ${r0}`\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t\tresolvers[field] = r\n\t\t\t\t\treturn resolvers\n\t\t\t\t},\n\t\t\t\t{}\n\t\t )\n\t\t: undefined\n\n\tconst { modify, transform } = options\n\n\treturn function resolve(query, context) {\n\t\tconst { graph, tree, type, filters } = context\n\n\t\tif (query.table !== table.table) {\n\t\t\tthrow new Error(\n\t\t\t\t`Mismatching query type (expected ${table.table}, found ${query.table}).`\n\t\t\t)\n\t\t}\n\n\t\tconst allow_all_fields =\n\t\t\toptions.allowAllFields ??\n\t\t\tgraph.options.allowAllFields ??\n\t\t\ttable_field_resolvers === undefined\n\n\t\tif (!allow_all_fields && !table_field_resolvers) {\n\t\t\tthrow new Error(\n\t\t\t\t`Table resolver for ${type} must either allow all fields or specify options.fields`\n\t\t\t)\n\t\t}\n\n\t\tif (modify) {\n\t\t\tquery = modify(query, context)\n\t\t}\n\n\t\tfor (const subtree of Object.values(tree.fieldsByTypeName[type])) {\n\t\t\tconst field = subtree.name\n\t\t\tconst r: FieldResolverFn | undefined =\n\t\t\t\ttable_field_resolvers?.[field] ||\n\t\t\t\t(allow_all_fields ? get_default_field_resolver(field) : undefined)\n\t\t\tif (!r) {\n\t\t\t\tthrow new Error(`No field resolver defined for field ${type}.${field}`)\n\t\t\t}\n\t\t\tif (r) {\n\t\t\t\tquery = r(query, { ...context, tree: subtree, field })\n\t\t\t}\n\t\t}\n\n\t\tconst allow_all_filters =\n\t\t\toptions.allowAllFilters ?? graph.options.allowAllFilters\n\n\t\tconst effective_filters = allow_all_filters ? true : filters\n\n\t\tif (effective_filters) {\n\t\t\tquery = apply_filters({\n\t\t\t\tquery,\n\t\t\t\tfilters: effective_filters,\n\t\t\t\targs: tree.args,\n\t\t\t\tmodifiers: options.modifiers,\n\t\t\t})\n\t\t}\n\n\t\tif (transform) {\n\t\t\tquery = run_after_query(query, (instance) => {\n\t\t\t\treturn transform(instance, context)\n\t\t\t})\n\t\t}\n\n\t\treturn query\n\t}\n}\n"],"mappings":"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","names":["s","cursor"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as pqb from 'pqb';
|
|
2
2
|
import { Query } from 'pqb';
|
|
3
|
-
import { ResolveTree } from 'graphql-parse-resolve-info';
|
|
4
|
-
import { GraphQLResolveInfo } from 'graphql';
|
|
5
3
|
import { TableClass, DbTable } from 'orchid-orm';
|
|
4
|
+
import { GraphQLResolveInfo } from 'graphql';
|
|
5
|
+
import { ResolveTree } from 'graphql-parse-resolve-info';
|
|
6
6
|
|
|
7
7
|
interface Paginator {
|
|
8
8
|
/** GraphQL path, for example: `["nodes"]` */
|
|
@@ -36,29 +36,6 @@ type Modifier = (query: Query, param?: any) => Query;
|
|
|
36
36
|
|
|
37
37
|
type FiltersDef = boolean;
|
|
38
38
|
|
|
39
|
-
/** A function that modifies the query to select fields/relations and filter the result set. */
|
|
40
|
-
type TableResolverFn = (args: {
|
|
41
|
-
tree: ResolveTree;
|
|
42
|
-
/** GraphQL type */
|
|
43
|
-
type: string;
|
|
44
|
-
query: Query;
|
|
45
|
-
filters?: FiltersDef;
|
|
46
|
-
graph: GraphResolver;
|
|
47
|
-
}) => Query;
|
|
48
|
-
interface TableResolverOptions {
|
|
49
|
-
/** allow to resolve all table fields without explicitly listing them */
|
|
50
|
-
allowAllFields?: boolean;
|
|
51
|
-
/** allow to filter all table fields without explicitly listing them */
|
|
52
|
-
allowAllFilters?: boolean;
|
|
53
|
-
fields?: Record<string, SimpleFieldResolver>;
|
|
54
|
-
modifiers?: Record<string, Modifier>;
|
|
55
|
-
modify?: QueryTreeModifier;
|
|
56
|
-
transform?(instance: any, context: any): void | PromiseLike<void>;
|
|
57
|
-
}
|
|
58
|
-
type SimpleFieldResolver = true | string | FieldResolverFn;
|
|
59
|
-
type QueryTreeModifier = (query: Query, tree: ResolveTree) => Query;
|
|
60
|
-
declare function defineTableResolver<T extends TableClass>(table: DbTable<T>, options?: TableResolverOptions): TableResolverFn;
|
|
61
|
-
|
|
62
39
|
type GraphResolverOptions = Pick<TableResolverOptions, "allowAllFields" | "allowAllFilters">;
|
|
63
40
|
declare function createGraphResolver(types: Record<string, TableResolverFn>, options?: GraphResolverOptions): GraphResolver;
|
|
64
41
|
interface GraphResolveOptions {
|
|
@@ -66,6 +43,11 @@ interface GraphResolveOptions {
|
|
|
66
43
|
context: any;
|
|
67
44
|
filters?: FiltersDef;
|
|
68
45
|
}
|
|
46
|
+
interface GraphResolveContext {
|
|
47
|
+
tree: ResolveTree;
|
|
48
|
+
filters?: FiltersDef;
|
|
49
|
+
context: any;
|
|
50
|
+
}
|
|
69
51
|
declare class GraphResolver {
|
|
70
52
|
readonly tables: Record<string, TableResolverFn>;
|
|
71
53
|
readonly options: GraphResolverOptions;
|
|
@@ -73,40 +55,48 @@ declare class GraphResolver {
|
|
|
73
55
|
resolve(query: Query, { info, context, filters }: GraphResolveOptions): Query;
|
|
74
56
|
resolvePage(query: Query, paginator: Paginator, { info, context, filters }: GraphResolveOptions): Query;
|
|
75
57
|
_get_resolve_tree(info: GraphQLResolveInfo): ResolveTree;
|
|
76
|
-
_resolve_type(
|
|
77
|
-
|
|
78
|
-
tree: ResolveTree;
|
|
79
|
-
filters?: FiltersDef;
|
|
80
|
-
}): Query;
|
|
81
|
-
_resolve_page({ tree, query, paginator, filters, }: {
|
|
82
|
-
query: Query;
|
|
83
|
-
tree: ResolveTree;
|
|
84
|
-
paginator: Paginator;
|
|
85
|
-
filters?: FiltersDef;
|
|
86
|
-
}): Query;
|
|
58
|
+
_resolve_type(query: Query, context: GraphResolveContext): Query;
|
|
59
|
+
_resolve_page(query: Query, paginator: Paginator, context: GraphResolveContext): Query;
|
|
87
60
|
}
|
|
88
61
|
|
|
89
|
-
|
|
62
|
+
interface TableResolveContext extends GraphResolveContext {
|
|
63
|
+
graph: GraphResolver;
|
|
64
|
+
/** GraphQL type */
|
|
65
|
+
type: string;
|
|
66
|
+
}
|
|
67
|
+
/** A function that modifies the query to select fields/relations and filter the result set. */
|
|
68
|
+
type TableResolverFn = (query: Query, context: TableResolveContext) => Query;
|
|
69
|
+
interface TableResolverOptions {
|
|
70
|
+
/** allow to resolve all table fields without explicitly listing them */
|
|
71
|
+
allowAllFields?: boolean;
|
|
72
|
+
/** allow to filter all table fields without explicitly listing them */
|
|
73
|
+
allowAllFilters?: boolean;
|
|
74
|
+
fields?: Record<string, SimpleFieldResolver>;
|
|
75
|
+
modifiers?: Record<string, Modifier>;
|
|
76
|
+
modify?: TableResolverFn;
|
|
77
|
+
transform?(instance: any, context: any): void | PromiseLike<void>;
|
|
78
|
+
}
|
|
79
|
+
type SimpleFieldResolver = true | string | FieldResolverFn;
|
|
80
|
+
declare function defineTableResolver<T extends TableClass>(table: DbTable<T>, options?: TableResolverOptions): TableResolverFn;
|
|
81
|
+
|
|
82
|
+
interface FieldResolveContext extends TableResolveContext {
|
|
90
83
|
/** GraphQL field */
|
|
91
84
|
field: string;
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
/** Current graph resolver */
|
|
95
|
-
graph: GraphResolver;
|
|
96
|
-
}) => Query;
|
|
85
|
+
}
|
|
86
|
+
type FieldResolverFn = (query: Query, context: FieldResolveContext) => Query;
|
|
97
87
|
interface FieldResolverOptions {
|
|
98
88
|
/** Table field (if different from GraphQL field) */
|
|
99
89
|
tableField?: string;
|
|
100
90
|
/** Custom query modifier (if different than simply selecting a field). */
|
|
101
91
|
modify?: FieldResolverFn;
|
|
102
92
|
/** Post-process selected value. Return a new value or a promise. */
|
|
103
|
-
transform?(value: any, instance: any, context:
|
|
93
|
+
transform?(value: any, instance: any, context: FieldResolveContext): any;
|
|
104
94
|
}
|
|
105
95
|
declare function defineFieldResolver(options?: FieldResolverOptions): FieldResolverFn;
|
|
106
96
|
|
|
107
97
|
interface RelationResolverOptions extends Omit<FieldResolverOptions, "modify"> {
|
|
108
98
|
filters?: FiltersDef;
|
|
109
|
-
modify?:
|
|
99
|
+
modify?: TableResolverFn;
|
|
110
100
|
}
|
|
111
101
|
declare function defineRelationResolver(options?: RelationResolverOptions): FieldResolverFn;
|
|
112
102
|
|
package/dist/index.js
CHANGED
|
@@ -96,16 +96,6 @@ var CursorPaginator = class {
|
|
|
96
96
|
}
|
|
97
97
|
};
|
|
98
98
|
|
|
99
|
-
// src/utils/query-context.ts
|
|
100
|
-
var graphql = Symbol("graphql");
|
|
101
|
-
function set_query_context(query, context) {
|
|
102
|
-
;
|
|
103
|
-
query.query[graphql] = context;
|
|
104
|
-
}
|
|
105
|
-
function get_query_context(query) {
|
|
106
|
-
return query.query[graphql];
|
|
107
|
-
}
|
|
108
|
-
|
|
109
99
|
// src/utils/run-after.ts
|
|
110
100
|
function run_after_query(query, transform) {
|
|
111
101
|
return query.afterQuery(async (_, result) => {
|
|
@@ -123,15 +113,14 @@ function run_after_query(query, transform) {
|
|
|
123
113
|
// src/resolvers/field.ts
|
|
124
114
|
function defineFieldResolver(options = {}) {
|
|
125
115
|
const { tableField, modify, transform } = options;
|
|
126
|
-
return function resolve(query,
|
|
127
|
-
const { field } =
|
|
116
|
+
return function resolve(query, context) {
|
|
117
|
+
const { field } = context;
|
|
128
118
|
if (modify) {
|
|
129
|
-
|
|
119
|
+
query = modify(query, context);
|
|
130
120
|
} else {
|
|
131
121
|
query = query.select({ [field]: tableField || field });
|
|
132
122
|
}
|
|
133
123
|
if (transform) {
|
|
134
|
-
const context = get_query_context(query);
|
|
135
124
|
query = run_after_query(query, async (instance) => {
|
|
136
125
|
instance[field] = await transform(instance[field], instance, context);
|
|
137
126
|
return instance;
|
|
@@ -152,42 +141,33 @@ var GraphResolver = class {
|
|
|
152
141
|
this.options = options;
|
|
153
142
|
}
|
|
154
143
|
resolve(query, { info, context, filters }) {
|
|
155
|
-
set_query_context(query, context);
|
|
156
144
|
const tree = this._get_resolve_tree(info);
|
|
157
|
-
return this._resolve_type(
|
|
145
|
+
return this._resolve_type(query, { tree, filters, context });
|
|
158
146
|
}
|
|
159
147
|
resolvePage(query, paginator, { info, context, filters }) {
|
|
160
|
-
set_query_context(query, context);
|
|
161
148
|
const tree = this._get_resolve_tree(info);
|
|
162
|
-
return this._resolve_page(
|
|
149
|
+
return this._resolve_page(query, paginator, { tree, filters, context });
|
|
163
150
|
}
|
|
164
151
|
_get_resolve_tree(info) {
|
|
165
152
|
return parseResolveInfo(info);
|
|
166
153
|
}
|
|
167
|
-
_resolve_type({
|
|
168
|
-
tree
|
|
169
|
-
query,
|
|
170
|
-
filters
|
|
171
|
-
}) {
|
|
154
|
+
_resolve_type(query, context) {
|
|
155
|
+
const { tree } = context;
|
|
172
156
|
const type = Object.keys(tree.fieldsByTypeName)[0];
|
|
173
157
|
const table_resolver = this.tables[type];
|
|
174
158
|
if (!table_resolver) {
|
|
175
159
|
throw new Error(`Table resolver not found for type ${type}.`);
|
|
176
160
|
}
|
|
177
|
-
return table_resolver({
|
|
161
|
+
return table_resolver(query, { ...context, graph: this, tree, type });
|
|
178
162
|
}
|
|
179
|
-
_resolve_page({
|
|
180
|
-
tree
|
|
181
|
-
query,
|
|
182
|
-
paginator,
|
|
183
|
-
filters
|
|
184
|
-
}) {
|
|
163
|
+
_resolve_page(query, paginator, context) {
|
|
164
|
+
let { tree } = context;
|
|
185
165
|
const { args } = tree;
|
|
186
166
|
for (const field of paginator.path) {
|
|
187
167
|
const type = Object.keys(tree.fieldsByTypeName)[0];
|
|
188
168
|
tree = tree.fieldsByTypeName[type][field];
|
|
189
169
|
}
|
|
190
|
-
query = this._resolve_type({
|
|
170
|
+
query = this._resolve_type(query, { ...context, tree });
|
|
191
171
|
return paginator.paginate(query, args);
|
|
192
172
|
}
|
|
193
173
|
};
|
|
@@ -195,20 +175,20 @@ var GraphResolver = class {
|
|
|
195
175
|
// src/resolvers/page.ts
|
|
196
176
|
import { overrideParserInQuery } from "orchid-core";
|
|
197
177
|
function definePageResolver(paginator, options = {}) {
|
|
198
|
-
const { tableField,
|
|
178
|
+
const { tableField, modify } = options;
|
|
199
179
|
return defineFieldResolver({
|
|
200
|
-
modify
|
|
180
|
+
modify(query, context) {
|
|
181
|
+
const { field, graph } = context;
|
|
201
182
|
let get_page;
|
|
202
183
|
query = query.select({
|
|
203
184
|
[field]: (q) => {
|
|
204
|
-
q = graph._resolve_page(
|
|
205
|
-
|
|
206
|
-
query: q[tableField || field],
|
|
185
|
+
q = graph._resolve_page(
|
|
186
|
+
q[tableField || field],
|
|
207
187
|
paginator,
|
|
208
|
-
|
|
209
|
-
|
|
188
|
+
context
|
|
189
|
+
);
|
|
210
190
|
get_page = get_query_pagination_handler(q);
|
|
211
|
-
return modify ? modify(q,
|
|
191
|
+
return modify ? modify(q, context) : q;
|
|
212
192
|
}
|
|
213
193
|
});
|
|
214
194
|
overrideParserInQuery(query.query, field, get_page);
|
|
@@ -221,16 +201,18 @@ function definePageResolver(paginator, options = {}) {
|
|
|
221
201
|
function defineRelationResolver(options = {}) {
|
|
222
202
|
const { tableField, filters, modify } = options;
|
|
223
203
|
return defineFieldResolver({
|
|
224
|
-
modify
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
204
|
+
modify(query, context) {
|
|
205
|
+
const { graph, field } = context;
|
|
206
|
+
return query.select({
|
|
207
|
+
[field]: (q) => {
|
|
208
|
+
const relation_query = graph._resolve_type(
|
|
209
|
+
q[tableField || field],
|
|
210
|
+
{ ...context, filters }
|
|
211
|
+
);
|
|
212
|
+
return modify ? modify(relation_query, context) : relation_query;
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
}
|
|
234
216
|
});
|
|
235
217
|
}
|
|
236
218
|
|
|
@@ -337,7 +319,8 @@ function defineTableResolver(table, options = {}) {
|
|
|
337
319
|
{}
|
|
338
320
|
) : void 0;
|
|
339
321
|
const { modify, transform } = options;
|
|
340
|
-
return function resolve(
|
|
322
|
+
return function resolve(query, context) {
|
|
323
|
+
const { graph, tree, type, filters } = context;
|
|
341
324
|
if (query.table !== table.table) {
|
|
342
325
|
throw new Error(
|
|
343
326
|
`Mismatching query type (expected ${table.table}, found ${query.table}).`
|
|
@@ -350,7 +333,7 @@ function defineTableResolver(table, options = {}) {
|
|
|
350
333
|
);
|
|
351
334
|
}
|
|
352
335
|
if (modify) {
|
|
353
|
-
query = modify(query,
|
|
336
|
+
query = modify(query, context);
|
|
354
337
|
}
|
|
355
338
|
for (const subtree of Object.values(tree.fieldsByTypeName[type])) {
|
|
356
339
|
const field = subtree.name;
|
|
@@ -359,7 +342,7 @@ function defineTableResolver(table, options = {}) {
|
|
|
359
342
|
throw new Error(`No field resolver defined for field ${type}.${field}`);
|
|
360
343
|
}
|
|
361
344
|
if (r) {
|
|
362
|
-
query = r(query, {
|
|
345
|
+
query = r(query, { ...context, tree: subtree, field });
|
|
363
346
|
}
|
|
364
347
|
}
|
|
365
348
|
const allow_all_filters = options.allowAllFilters ?? graph.options.allowAllFilters;
|
|
@@ -373,7 +356,6 @@ function defineTableResolver(table, options = {}) {
|
|
|
373
356
|
});
|
|
374
357
|
}
|
|
375
358
|
if (transform) {
|
|
376
|
-
const context = get_query_context(query);
|
|
377
359
|
query = run_after_query(query, (instance) => {
|
|
378
360
|
return transform(instance, context);
|
|
379
361
|
});
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/paginators/base.ts","../src/paginators/cursor.ts","../src/utils/query-context.ts","../src/utils/run-after.ts","../src/resolvers/field.ts","../src/resolvers/graph.ts","../src/resolvers/page.ts","../src/resolvers/relation.ts","../src/utils/is-plain-object.ts","../src/filters/filters.ts","../src/resolvers/table.ts"],"sourcesContent":["import { Query } from \"pqb\"\n\nexport interface Paginator {\n\t/** GraphQL path, for example: `[\"nodes\"]` */\n\treadonly path: string[]\n\tpaginate(query: Query, ...args: any[]): Query\n}\n\ntype GetPageFn<P> = (nodes: any[]) => P\n\nconst s = Symbol()\n\n// FIXME use proper orchid methods, set result type\nexport function set_query_page_result<P>(query: Query, get_page: GetPageFn<P>) {\n\tfunction _get_page(data: any) {\n\t\tif (!Array.isArray(data)) {\n\t\t\tthrow new Error(`Paginator called for single result query.`)\n\t\t}\n\t\treturn get_page(data)\n\t}\n\n\t// For root query\n\tconst _query = query.query\n\tconst { handleResult } = _query\n\t_query.handleResult = (q, result, s) => {\n\t\tconst data = handleResult(q, result, s)\n\t\t// Sometimes this is called for subqueries, sometimes not.\n\t\t// TODO: investigate and use always.\n\t\treturn s ? data : _get_page(data)\n\t}\n\n\t// For subqueries\n\t;(_query as any)[s] = _get_page\n\n\treturn query\n}\n\nexport function get_query_pagination_handler(query: Query) {\n\treturn (query.query as any)[s]\n}\n","import { Query } from \"pqb\"\n\nimport { Paginator, set_query_page_result } from \"./base\"\n\nexport function defineCursorPaginator(\n\toptions: Partial<CursorPaginatorOptions> = {}\n) {\n\treturn new CursorPaginator(options)\n}\n\nexport interface CursorPaginatorOptions {\n\tfields: string[]\n\ttake: number\n}\n\nexport interface CursorPaginatorArgs {\n\tcursor?: string\n\ttake?: number\n}\n\nexport interface CursorPaginatorPage<M> {\n\tnodes: M[]\n\tcursor?: string\n}\n\nclass CursorPaginator implements Paginator {\n\treadonly path = [\"nodes\"]\n\treadonly options: CursorPaginatorOptions\n\n\treadonly fields: Array<{\n\t\tname: string\n\t\tdesc: boolean\n\t}>\n\n\tconstructor(options: Partial<CursorPaginatorOptions> = {}) {\n\t\tthis.options = {\n\t\t\tfields: [\"id\"],\n\t\t\ttake: 10,\n\t\t\t...options,\n\t\t}\n\t\tthis.fields = this.options.fields.map((field) => {\n\t\t\tif (field.startsWith(\"-\")) {\n\t\t\t\treturn { name: field.slice(1), desc: true }\n\t\t\t} else {\n\t\t\t\treturn { name: field, desc: false }\n\t\t\t}\n\t\t})\n\t}\n\n\tpaginate(query: Query, args: CursorPaginatorArgs = {}) {\n\t\tconst take = args.take ?? this.options.take\n\t\tconst { cursor } = args\n\n\t\t// Set query order\n\t\tquery = query.clear(\"order\")\n\t\tfor (const field of this.fields) {\n\t\t\t// TODO: prevent potential name clash with aliases like .as(`_${table_ref}_order_key_0`)\n\t\t\tquery = query\n\t\t\t\t.select(field.name)\n\t\t\t\t.order({ [field.name]: field.desc ? \"DESC\" : \"ASC\" })\n\t\t}\n\n\t\tif (cursor) {\n\t\t\tquery = this._set_query_cursor(query, cursor)\n\t\t}\n\t\tquery = query.limit(take + 1)\n\n\t\tquery = set_query_page_result(query, (nodes) => {\n\t\t\tlet cursor: string | undefined\n\t\t\tif (nodes.length > take) {\n\t\t\t\tcursor = this._create_cursor(nodes[take - 1])\n\t\t\t\tnodes = nodes.slice(0, take)\n\t\t\t}\n\t\t\treturn { nodes, cursor }\n\t\t})\n\n\t\treturn query\n\t}\n\n\t_create_cursor(instance: any) {\n\t\treturn JSON.stringify(\n\t\t\tthis.fields.map((field) => {\n\t\t\t\tconst value = instance[field.name]\n\t\t\t\tif (value === undefined) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Unable to create cursor: undefined field ${field.name}`\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\treturn String(value)\n\t\t\t})\n\t\t)\n\t}\n\n\t_set_query_cursor(query: Query, cursor: string) {\n\t\tconst values = JSON.parse(cursor)\n\t\tconst left: string[] = []\n\t\tconst right: string[] = []\n\t\tconst expr_values: Record<string, any> = {}\n\t\t// TODO: refactor\n\t\tfor (let i = 0; i < this.fields.length; ++i) {\n\t\t\tconst field = this.fields[i]\n\t\t\tconst expressions = field.desc ? right : left\n\t\t\tconst placeholders = field.desc ? left : right\n\t\t\texpressions.push(`\"${field.name}\"`)\n\t\t\tconst placeholder = `value${i}`\n\t\t\tplaceholders.push(\"$\" + placeholder)\n\t\t\texpr_values[placeholder] = values[i]\n\t\t}\n\t\treturn query.where(\n\t\t\tquery.raw(`(${left.join(\",\")}) > (${right.join(\",\")})`, expr_values)\n\t\t)\n\t}\n}\n","import { Query } from \"pqb\"\n\nconst graphql = Symbol(\"graphql\")\n\nexport function set_query_context(query: Query, context: any) {\n\t;(query.query as any)[graphql] = context\n}\n\nexport function get_query_context(query: Query) {\n\treturn (query.query as any)[graphql]\n}\n","import { Query } from \"pqb\"\n\nexport function run_after_query(\n\tquery: Query,\n\ttransform: (instance: any) => any\n) {\n\treturn query.afterQuery(async (_, result) => {\n\t\t// FIXME: returning is ignored in orchid (unlike objection). Add some hacks instead.\n\t\tif (Array.isArray(result)) {\n\t\t\tconst result1 = await Promise.all(result.map(transform))\n\t\t\treturn result1.filter(Boolean) // Skip empty objects\n\t\t} else if (result) {\n\t\t\treturn transform(result)\n\t\t} else {\n\t\t\t// Supposedly, single query builder returning NULL\n\t\t\treturn result\n\t\t}\n\t})\n}\n","import { ResolveTree } from \"graphql-parse-resolve-info\"\nimport { Query } from \"pqb\"\n\nimport { get_query_context } from \"../utils/query-context\"\nimport { run_after_query } from \"../utils/run-after\"\nimport type { GraphResolver } from \"./graph\"\n\n/* A function that modifies the query to select a field. */\nexport type FieldResolverFn = (\n\tquery: Query,\n\toptions: {\n\t\t/** GraphQL field */\n\t\tfield: string\n\t\t/** GraphQL resolve tree */\n\t\ttree: ResolveTree\n\t\t/** Current graph resolver */\n\t\tgraph: GraphResolver\n\t}\n) => Query\n\nexport interface FieldResolverOptions {\n\t/** Table field (if different from GraphQL field) */\n\ttableField?: string\n\t/** Custom query modifier (if different than simply selecting a field). */\n\tmodify?: FieldResolverFn\n\t/** Post-process selected value. Return a new value or a promise. */\n\ttransform?(value: any, instance: any, context: any): any\n}\n\nexport function defineFieldResolver(\n\toptions: FieldResolverOptions = {}\n): FieldResolverFn {\n\tconst { tableField, modify, transform } = options\n\n\treturn function resolve(query, options) {\n\t\tconst { field } = options\n\t\tif (modify) {\n\t\t\treturn modify(query, options)\n\t\t} else {\n\t\t\tquery = query.select({ [field]: tableField || field })\n\t\t}\n\t\tif (transform) {\n\t\t\tconst context = get_query_context(query)\n\t\t\tquery = run_after_query(query, async (instance) => {\n\t\t\t\tinstance[field] = await transform(instance[field], instance, context)\n\t\t\t\treturn instance\n\t\t\t})\n\t\t}\n\t\treturn query\n\t}\n}\n","import { GraphQLResolveInfo } from \"graphql\"\nimport { parseResolveInfo, ResolveTree } from \"graphql-parse-resolve-info\"\nimport { Query } from \"pqb\"\n\nimport { FiltersDef } from \"../filters/filters\"\nimport { Paginator } from \"../paginators/base\"\nimport { set_query_context } from \"../utils/query-context\"\nimport type { TableResolverFn, TableResolverOptions } from \"./table\"\n\nexport type GraphResolverOptions = Pick<\n\tTableResolverOptions,\n\t\"allowAllFields\" | \"allowAllFilters\"\n>\n\nexport function createGraphResolver(\n\ttypes: Record<string, TableResolverFn>,\n\toptions?: GraphResolverOptions\n) {\n\treturn new GraphResolver(types, options)\n}\n\nexport interface GraphResolveOptions {\n\tinfo: GraphQLResolveInfo\n\tcontext: any\n\tfilters?: FiltersDef\n}\n\nexport class GraphResolver {\n\tconstructor(\n\t\tpublic readonly tables: Record<string, TableResolverFn>,\n\t\tpublic readonly options: GraphResolverOptions = {}\n\t) {}\n\n\tresolve(query: Query, { info, context, filters }: GraphResolveOptions) {\n\t\tset_query_context(query, context)\n\t\tconst tree = this._get_resolve_tree(info)\n\t\treturn this._resolve_type({ query, tree, filters })\n\t}\n\n\tresolvePage(\n\t\tquery: Query,\n\t\tpaginator: Paginator,\n\t\t{ info, context, filters }: GraphResolveOptions\n\t) {\n\t\tset_query_context(query, context)\n\t\tconst tree = this._get_resolve_tree(info)\n\t\treturn this._resolve_page({ query, tree, paginator, filters })\n\t}\n\n\t_get_resolve_tree(info: GraphQLResolveInfo) {\n\t\treturn parseResolveInfo(info) as ResolveTree\n\t}\n\n\t_resolve_type({\n\t\ttree,\n\t\tquery,\n\t\tfilters,\n\t}: {\n\t\tquery: Query\n\t\ttree: ResolveTree\n\t\tfilters?: FiltersDef\n\t}) {\n\t\tconst type = Object.keys(tree.fieldsByTypeName)[0]\n\t\tconst table_resolver = this.tables[type]\n\t\tif (!table_resolver) {\n\t\t\tthrow new Error(`Table resolver not found for type ${type}.`)\n\t\t}\n\t\treturn table_resolver({ tree, type, query, filters, graph: this })\n\t}\n\n\t_resolve_page({\n\t\ttree,\n\t\tquery,\n\t\tpaginator,\n\t\tfilters,\n\t}: {\n\t\tquery: Query\n\t\ttree: ResolveTree\n\t\tpaginator: Paginator\n\t\tfilters?: FiltersDef\n\t}) {\n\t\tconst { args } = tree\n\t\t// Skip page subtree(s)\n\t\tfor (const field of paginator.path) {\n\t\t\tconst type = Object.keys(tree.fieldsByTypeName)[0]\n\t\t\ttree = tree.fieldsByTypeName[type][field]\n\t\t\t// TODO: raise exception if not found\n\t\t}\n\t\tquery = this._resolve_type({ query, tree, filters })\n\t\treturn paginator.paginate(query, args)\n\t}\n}\n","import { overrideParserInQuery } from \"orchid-core\"\n\nimport { get_query_pagination_handler, Paginator } from \"../paginators/base\"\nimport { defineFieldResolver } from \"./field\"\nimport { RelationResolverOptions } from \"./relation\"\n\nexport function definePageResolver(\n\tpaginator: Paginator,\n\toptions: RelationResolverOptions = {}\n) {\n\tconst { tableField, filters, modify } = options\n\n\treturn defineFieldResolver({\n\t\tmodify: (query, { field, tree, graph }) => {\n\t\t\t// FIXME: refactor, add types\n\t\t\tlet get_page: any\n\t\t\tquery = query.select({\n\t\t\t\t[field]: (q) => {\n\t\t\t\t\tq = graph._resolve_page({\n\t\t\t\t\t\ttree,\n\t\t\t\t\t\tquery: (q as any)[tableField || field],\n\t\t\t\t\t\tpaginator,\n\t\t\t\t\t\tfilters,\n\t\t\t\t\t})\n\t\t\t\t\t// Calling addParserToQuery here doesn't work, save the paginator for later.\n\t\t\t\t\tget_page = get_query_pagination_handler(q)\n\t\t\t\t\treturn modify ? modify(q, tree) : q\n\t\t\t\t},\n\t\t\t})\n\t\t\toverrideParserInQuery(query.query, field, get_page)\n\t\t\treturn query\n\t\t},\n\t})\n}\n","import { FiltersDef } from \"../filters/filters\"\nimport { defineFieldResolver, FieldResolverOptions } from \"./field\"\nimport { QueryTreeModifier } from \"./table\"\n\nexport interface RelationResolverOptions\n\textends Omit<FieldResolverOptions, \"modify\"> {\n\tfilters?: FiltersDef\n\tmodify?: QueryTreeModifier\n}\n\nexport function defineRelationResolver(options: RelationResolverOptions = {}) {\n\tconst { tableField, filters, modify } = options\n\n\treturn defineFieldResolver({\n\t\tmodify: (query, { field, tree, graph }) =>\n\t\t\tquery.select({\n\t\t\t\t[field]: (q) => {\n\t\t\t\t\tconst relation_query = graph._resolve_type({\n\t\t\t\t\t\ttree,\n\t\t\t\t\t\tquery: (q as any)[tableField || field],\n\t\t\t\t\t\tfilters,\n\t\t\t\t\t})\n\t\t\t\t\treturn modify ? modify(relation_query, tree) : relation_query\n\t\t\t\t},\n\t\t\t}),\n\t})\n}\n","export function is_plain_object(obj: any): obj is Record<string, any> {\n\treturn typeof obj === \"object\" && !Array.isArray(obj)\n}\n","import { Query } from \"pqb\"\n\nimport { is_plain_object } from \"../utils/is-plain-object\"\nimport { Modifier } from \"../utils/modifier\"\n\n// TODO: per-field definitions\nexport type FiltersDef = boolean\n\nexport type FilterScalarValue = null | string | number | boolean\nexport type FilterValue = FilterScalarValue | Exclude<FilterScalarValue, null>[]\nexport type Filter = { [property: string]: FilterValue }\n\nfunction is_scalar(value: any) {\n\treturn (\n\t\tvalue === null ||\n\t\ttypeof value === \"string\" ||\n\t\ttypeof value === \"number\" ||\n\t\ttypeof value === \"boolean\"\n\t)\n}\n\nexport function apply_filters({\n\tquery,\n\tfilters,\n\targs,\n\tmodifiers,\n}: {\n\tquery: Query\n\tfilters: FiltersDef\n\targs?: Record<string, any>\n\tmodifiers?: Record<string, Modifier>\n}) {\n\tif (!filters) {\n\t\treturn query\n\t}\n\tconst filter_obj = args?.filter\n\tif (!filter_obj) {\n\t\treturn query\n\t}\n\tif (!is_plain_object(filter_obj)) {\n\t\tthrow new Error(`Invalid filter: ${filter_obj}, must be object.`)\n\t}\n\tfor (const [field, value] of Object.entries(filter_obj)) {\n\t\tif (value === undefined) {\n\t\t\t// Support optional GraphQL arguments in filter\n\t\t\tcontinue\n\t\t}\n\t\tif (modifiers?.[field]) {\n\t\t\tquery = modifiers[field](query, value)\n\t\t} else {\n\t\t\t// Normal filter\n\t\t\tconst [dbfield, op0] = field.split(\"__\")\n\t\t\tconst op = op0?.toLowerCase() || \"exact\"\n\t\t\tif (\n\t\t\t\t// TODO: add the rest of orchid ops (or allow all of them?)\n\t\t\t\t[\n\t\t\t\t\t\"exact\",\n\t\t\t\t\t\"lt\",\n\t\t\t\t\t\"lte\",\n\t\t\t\t\t\"gt\",\n\t\t\t\t\t\"gte\",\n\t\t\t\t\t\"like\",\n\t\t\t\t\t\"ilike\",\n\t\t\t\t\t\"contains\",\n\t\t\t\t].includes(op)\n\t\t\t) {\n\t\t\t\tif (!is_scalar(value)) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Unsupported filter value for ${query.table}.${field}: must be scalar.`\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tif (op === \"exact\") {\n\t\t\t\t\tquery = query.where({ [dbfield]: value })\n\t\t\t\t} else {\n\t\t\t\t\tconst query_op = { like: \"contains\" }[op] || op\n\t\t\t\t\tquery = query.where({ [dbfield]: { [query_op]: value } })\n\t\t\t\t}\n\t\t\t} else if (op === \"in\") {\n\t\t\t\tif (!(Array.isArray(value) && value.every(is_scalar))) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Invalid filter value for ${query.table}.${field}: must be an array of scalars.`\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tquery = query.whereIn(dbfield, value)\n\t\t\t} else {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Invalid filter ${query.table}.${field}: unsupported operator ${op}.`\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t}\n\treturn query\n}\n","import { ResolveTree } from \"graphql-parse-resolve-info\"\nimport { DbTable, TableClass } from \"orchid-orm\"\nimport { Query } from \"pqb\"\n\nimport { apply_filters, FiltersDef } from \"../filters/filters\"\nimport { Modifier } from \"../utils/modifier\"\nimport { get_query_context } from \"../utils/query-context\"\nimport { run_after_query } from \"../utils/run-after\"\nimport { defineFieldResolver, FieldResolverFn } from \"./field\"\nimport type { GraphResolver } from \"./graph\"\nimport { defineRelationResolver } from \"./relation\"\n\n/** A function that modifies the query to select fields/relations and filter the result set. */\nexport type TableResolverFn = (args: {\n\ttree: ResolveTree\n\t/** GraphQL type */\n\ttype: string\n\tquery: Query\n\tfilters?: FiltersDef\n\tgraph: GraphResolver\n}) => Query\n\nexport interface TableResolverOptions {\n\t/** allow to resolve all table fields without explicitly listing them */\n\tallowAllFields?: boolean\n\t/** allow to filter all table fields without explicitly listing them */\n\tallowAllFilters?: boolean\n\tfields?: Record<string, SimpleFieldResolver>\n\tmodifiers?: Record<string, Modifier>\n\tmodify?: QueryTreeModifier\n\ttransform?(instance: any, context: any): void | PromiseLike<void>\n}\n\nexport type SimpleFieldResolver = true | string | FieldResolverFn\n\nexport type QueryTreeModifier = (query: Query, tree: ResolveTree) => Query\n\nexport function defineTableResolver<T extends TableClass>(\n\ttable: DbTable<T>,\n\toptions: TableResolverOptions = {}\n): TableResolverFn {\n\t/**\n\t * Create a field resolver which will modify the query to resolve a GraphQL field.\n\t */\n\tconst get_default_field_resolver = (\n\t\tfield: string,\n\t\ttableField?: string\n\t): FieldResolverFn | undefined => {\n\t\tconst table_field_lookup = tableField || field\n\t\tif (options.modifiers?.[table_field_lookup]) {\n\t\t\t// Keep query as is.\n\t\t\treturn (query) => query\n\t\t} else if ((table.relations as any)?.[table_field_lookup]) {\n\t\t\treturn defineRelationResolver({ tableField })\n\t\t} else {\n\t\t\treturn defineFieldResolver({ tableField })\n\t\t}\n\t}\n\n\t// Pre-create field resolvers\n\tconst { fields } = options\n\tconst table_field_resolvers = fields\n\t\t? Object.keys(fields).reduce<Record<string, FieldResolverFn>>(\n\t\t\t\t(resolvers, field) => {\n\t\t\t\t\tconst r0 = fields[field]\n\t\t\t\t\tconst r: FieldResolverFn | undefined =\n\t\t\t\t\t\ttypeof r0 === \"function\"\n\t\t\t\t\t\t\t? r0\n\t\t\t\t\t\t\t: r0 === true\n\t\t\t\t\t\t\t? get_default_field_resolver(field)\n\t\t\t\t\t\t\t: typeof r0 === \"string\"\n\t\t\t\t\t\t\t? get_default_field_resolver(field, r0)\n\t\t\t\t\t\t\t: undefined\n\t\t\t\t\tif (r === undefined) {\n\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t`Field resolver must be a function, string, or true; found ${r0}`\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t\tresolvers[field] = r\n\t\t\t\t\treturn resolvers\n\t\t\t\t},\n\t\t\t\t{}\n\t\t )\n\t\t: undefined\n\n\tconst { modify, transform } = options\n\n\treturn function resolve({ tree, type, query, filters, graph }) {\n\t\tif (query.table !== table.table) {\n\t\t\tthrow new Error(\n\t\t\t\t`Mismatching query type (expected ${table.table}, found ${query.table}).`\n\t\t\t)\n\t\t}\n\n\t\tconst allow_all_fields =\n\t\t\toptions.allowAllFields ??\n\t\t\tgraph.options.allowAllFields ??\n\t\t\ttable_field_resolvers === undefined\n\n\t\tif (!allow_all_fields && !table_field_resolvers) {\n\t\t\tthrow new Error(\n\t\t\t\t`Table resolver for ${type} must either allow all fields or specify options.fields`\n\t\t\t)\n\t\t}\n\n\t\tif (modify) {\n\t\t\tquery = modify(query, tree)\n\t\t}\n\n\t\tfor (const subtree of Object.values(tree.fieldsByTypeName[type])) {\n\t\t\tconst field = subtree.name\n\t\t\tconst r =\n\t\t\t\ttable_field_resolvers?.[field] ||\n\t\t\t\t(allow_all_fields ? get_default_field_resolver(field) : undefined)\n\t\t\tif (!r) {\n\t\t\t\tthrow new Error(`No field resolver defined for field ${type}.${field}`)\n\t\t\t}\n\t\t\tif (r) {\n\t\t\t\tquery = r(query, { field, tree: subtree, graph })\n\t\t\t}\n\t\t}\n\n\t\tconst allow_all_filters =\n\t\t\toptions.allowAllFilters ?? graph.options.allowAllFilters\n\n\t\tconst effective_filters = allow_all_filters ? true : filters\n\n\t\tif (effective_filters) {\n\t\t\tquery = apply_filters({\n\t\t\t\tquery,\n\t\t\t\tfilters: effective_filters,\n\t\t\t\targs: tree.args,\n\t\t\t\tmodifiers: options.modifiers,\n\t\t\t})\n\t\t}\n\n\t\tif (transform) {\n\t\t\tconst context = get_query_context(query)\n\t\t\tquery = run_after_query(query, (instance) => {\n\t\t\t\treturn transform(instance, context)\n\t\t\t})\n\t\t}\n\n\t\treturn query\n\t}\n}\n"],"mappings":"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","names":["s","cursor","options"]}
|
|
1
|
+
{"version":3,"sources":["../src/paginators/base.ts","../src/paginators/cursor.ts","../src/utils/run-after.ts","../src/resolvers/field.ts","../src/resolvers/graph.ts","../src/resolvers/page.ts","../src/resolvers/relation.ts","../src/utils/is-plain-object.ts","../src/filters/filters.ts","../src/resolvers/table.ts"],"sourcesContent":["import { Query } from \"pqb\"\n\nexport interface Paginator {\n\t/** GraphQL path, for example: `[\"nodes\"]` */\n\treadonly path: string[]\n\tpaginate(query: Query, ...args: any[]): Query\n}\n\ntype GetPageFn<P> = (nodes: any[]) => P\n\nconst s = Symbol()\n\n// FIXME use proper orchid methods, set result type\nexport function set_query_page_result<P>(query: Query, get_page: GetPageFn<P>) {\n\tfunction _get_page(data: any) {\n\t\tif (!Array.isArray(data)) {\n\t\t\tthrow new Error(`Paginator called for single result query.`)\n\t\t}\n\t\treturn get_page(data)\n\t}\n\n\t// For root query\n\tconst _query = query.query\n\tconst { handleResult } = _query\n\t_query.handleResult = (q, result, s) => {\n\t\tconst data = handleResult(q, result, s)\n\t\t// Sometimes this is called for subqueries, sometimes not.\n\t\t// TODO: investigate and use always.\n\t\treturn s ? data : _get_page(data)\n\t}\n\n\t// For subqueries\n\t;(_query as any)[s] = _get_page\n\n\treturn query\n}\n\nexport function get_query_pagination_handler(query: Query) {\n\treturn (query.query as any)[s]\n}\n","import { Query } from \"pqb\"\n\nimport { Paginator, set_query_page_result } from \"./base\"\n\nexport function defineCursorPaginator(\n\toptions: Partial<CursorPaginatorOptions> = {}\n) {\n\treturn new CursorPaginator(options)\n}\n\nexport interface CursorPaginatorOptions {\n\tfields: string[]\n\ttake: number\n}\n\nexport interface CursorPaginatorArgs {\n\tcursor?: string\n\ttake?: number\n}\n\nexport interface CursorPaginatorPage<M> {\n\tnodes: M[]\n\tcursor?: string\n}\n\nclass CursorPaginator implements Paginator {\n\treadonly path = [\"nodes\"]\n\treadonly options: CursorPaginatorOptions\n\n\treadonly fields: Array<{\n\t\tname: string\n\t\tdesc: boolean\n\t}>\n\n\tconstructor(options: Partial<CursorPaginatorOptions> = {}) {\n\t\tthis.options = {\n\t\t\tfields: [\"id\"],\n\t\t\ttake: 10,\n\t\t\t...options,\n\t\t}\n\t\tthis.fields = this.options.fields.map((field) => {\n\t\t\tif (field.startsWith(\"-\")) {\n\t\t\t\treturn { name: field.slice(1), desc: true }\n\t\t\t} else {\n\t\t\t\treturn { name: field, desc: false }\n\t\t\t}\n\t\t})\n\t}\n\n\tpaginate(query: Query, args: CursorPaginatorArgs = {}) {\n\t\tconst take = args.take ?? this.options.take\n\t\tconst { cursor } = args\n\n\t\t// Set query order\n\t\tquery = query.clear(\"order\")\n\t\tfor (const field of this.fields) {\n\t\t\t// TODO: prevent potential name clash with aliases like .as(`_${table_ref}_order_key_0`)\n\t\t\tquery = query\n\t\t\t\t.select(field.name)\n\t\t\t\t.order({ [field.name]: field.desc ? \"DESC\" : \"ASC\" })\n\t\t}\n\n\t\tif (cursor) {\n\t\t\tquery = this._set_query_cursor(query, cursor)\n\t\t}\n\t\tquery = query.limit(take + 1)\n\n\t\tquery = set_query_page_result(query, (nodes) => {\n\t\t\tlet cursor: string | undefined\n\t\t\tif (nodes.length > take) {\n\t\t\t\tcursor = this._create_cursor(nodes[take - 1])\n\t\t\t\tnodes = nodes.slice(0, take)\n\t\t\t}\n\t\t\treturn { nodes, cursor }\n\t\t})\n\n\t\treturn query\n\t}\n\n\t_create_cursor(instance: any) {\n\t\treturn JSON.stringify(\n\t\t\tthis.fields.map((field) => {\n\t\t\t\tconst value = instance[field.name]\n\t\t\t\tif (value === undefined) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Unable to create cursor: undefined field ${field.name}`\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\treturn String(value)\n\t\t\t})\n\t\t)\n\t}\n\n\t_set_query_cursor(query: Query, cursor: string) {\n\t\tconst values = JSON.parse(cursor)\n\t\tconst left: string[] = []\n\t\tconst right: string[] = []\n\t\tconst expr_values: Record<string, any> = {}\n\t\t// TODO: refactor\n\t\tfor (let i = 0; i < this.fields.length; ++i) {\n\t\t\tconst field = this.fields[i]\n\t\t\tconst expressions = field.desc ? right : left\n\t\t\tconst placeholders = field.desc ? left : right\n\t\t\texpressions.push(`\"${field.name}\"`)\n\t\t\tconst placeholder = `value${i}`\n\t\t\tplaceholders.push(\"$\" + placeholder)\n\t\t\texpr_values[placeholder] = values[i]\n\t\t}\n\t\treturn query.where(\n\t\t\tquery.raw(`(${left.join(\",\")}) > (${right.join(\",\")})`, expr_values)\n\t\t)\n\t}\n}\n","import { Query } from \"pqb\"\n\nexport function run_after_query(\n\tquery: Query,\n\ttransform: (instance: any) => any\n) {\n\treturn query.afterQuery(async (_, result) => {\n\t\t// FIXME: returning is ignored in orchid (unlike objection). Add some hacks instead.\n\t\tif (Array.isArray(result)) {\n\t\t\tconst result1 = await Promise.all(result.map(transform))\n\t\t\treturn result1.filter(Boolean) // Skip empty objects\n\t\t} else if (result) {\n\t\t\treturn transform(result)\n\t\t} else {\n\t\t\t// Supposedly, single query builder returning NULL\n\t\t\treturn result\n\t\t}\n\t})\n}\n","import { Query } from \"pqb\"\n\nimport { run_after_query } from \"../utils/run-after\"\nimport { TableResolveContext } from \"./table\"\n\nexport interface FieldResolveContext extends TableResolveContext {\n\t/** GraphQL field */\n\tfield: string\n}\n\n/* A function that modifies the query to select a field. */\nexport type FieldResolverFn = (\n\tquery: Query,\n\tcontext: FieldResolveContext\n) => Query\n\nexport interface FieldResolverOptions {\n\t/** Table field (if different from GraphQL field) */\n\ttableField?: string\n\t/** Custom query modifier (if different than simply selecting a field). */\n\tmodify?: FieldResolverFn\n\t/** Post-process selected value. Return a new value or a promise. */\n\ttransform?(value: any, instance: any, context: FieldResolveContext): any\n}\n\nexport function defineFieldResolver(\n\toptions: FieldResolverOptions = {}\n): FieldResolverFn {\n\tconst { tableField, modify, transform } = options\n\n\treturn function resolve(query, context) {\n\t\tconst { field } = context\n\t\tif (modify) {\n\t\t\tquery = modify(query, context)\n\t\t} else {\n\t\t\tquery = query.select({ [field]: tableField || field })\n\t\t}\n\t\tif (transform) {\n\t\t\tquery = run_after_query(query, async (instance) => {\n\t\t\t\tinstance[field] = await transform(instance[field], instance, context)\n\t\t\t\treturn instance\n\t\t\t})\n\t\t}\n\t\treturn query\n\t}\n}\n","import { GraphQLResolveInfo } from \"graphql\"\nimport { parseResolveInfo, ResolveTree } from \"graphql-parse-resolve-info\"\nimport { Query } from \"pqb\"\n\nimport { FiltersDef } from \"../filters/filters\"\nimport { Paginator } from \"../paginators/base\"\nimport type { TableResolverFn, TableResolverOptions } from \"./table\"\n\nexport type GraphResolverOptions = Pick<\n\tTableResolverOptions,\n\t\"allowAllFields\" | \"allowAllFilters\"\n>\n\nexport function createGraphResolver(\n\ttypes: Record<string, TableResolverFn>,\n\toptions?: GraphResolverOptions\n) {\n\treturn new GraphResolver(types, options)\n}\n\nexport interface GraphResolveOptions {\n\tinfo: GraphQLResolveInfo\n\tcontext: any\n\tfilters?: FiltersDef\n}\n\nexport interface GraphResolveContext {\n\ttree: ResolveTree\n\tfilters?: FiltersDef\n\tcontext: any\n}\n\nexport class GraphResolver {\n\tconstructor(\n\t\tpublic readonly tables: Record<string, TableResolverFn>,\n\t\tpublic readonly options: GraphResolverOptions = {}\n\t) {}\n\n\tresolve(query: Query, { info, context, filters }: GraphResolveOptions) {\n\t\tconst tree = this._get_resolve_tree(info)\n\t\treturn this._resolve_type(query, { tree, filters, context })\n\t}\n\n\tresolvePage(\n\t\tquery: Query,\n\t\tpaginator: Paginator,\n\t\t{ info, context, filters }: GraphResolveOptions\n\t) {\n\t\tconst tree = this._get_resolve_tree(info)\n\t\treturn this._resolve_page(query, paginator, { tree, filters, context })\n\t}\n\n\t_get_resolve_tree(info: GraphQLResolveInfo) {\n\t\treturn parseResolveInfo(info) as ResolveTree\n\t}\n\n\t_resolve_type(query: Query, context: GraphResolveContext) {\n\t\tconst { tree } = context\n\t\tconst type = Object.keys(tree.fieldsByTypeName)[0]\n\t\tconst table_resolver = this.tables[type]\n\t\tif (!table_resolver) {\n\t\t\tthrow new Error(`Table resolver not found for type ${type}.`)\n\t\t}\n\t\treturn table_resolver(query, { ...context, graph: this, tree, type })\n\t}\n\n\t_resolve_page(\n\t\tquery: Query,\n\t\tpaginator: Paginator,\n\t\tcontext: GraphResolveContext\n\t) {\n\t\tlet { tree } = context\n\t\t// Save pagination args\n\t\tconst { args } = tree\n\t\t// Skip page subtree(s)\n\t\tfor (const field of paginator.path) {\n\t\t\tconst type = Object.keys(tree.fieldsByTypeName)[0]\n\t\t\ttree = tree.fieldsByTypeName[type][field]\n\t\t\t// TODO: raise exception if not found\n\t\t}\n\t\tquery = this._resolve_type(query, { ...context, tree })\n\t\treturn paginator.paginate(query, args)\n\t}\n}\n","import { overrideParserInQuery } from \"orchid-core\"\n\nimport { get_query_pagination_handler, Paginator } from \"../paginators/base\"\nimport { defineFieldResolver } from \"./field\"\nimport { RelationResolverOptions } from \"./relation\"\n\nexport function definePageResolver(\n\tpaginator: Paginator,\n\toptions: RelationResolverOptions = {}\n) {\n\tconst { tableField, modify } = options\n\n\treturn defineFieldResolver({\n\t\tmodify(query, context) {\n\t\t\tconst { field, graph } = context\n\t\t\t// FIXME: refactor, add types\n\t\t\tlet get_page: any\n\t\t\tquery = query.select({\n\t\t\t\t[field]: (q) => {\n\t\t\t\t\tq = graph._resolve_page(\n\t\t\t\t\t\t(q as any)[tableField || field],\n\t\t\t\t\t\tpaginator,\n\t\t\t\t\t\tcontext\n\t\t\t\t\t)\n\t\t\t\t\t// Calling addParserToQuery here doesn't work, save the paginator for later.\n\t\t\t\t\tget_page = get_query_pagination_handler(q)\n\t\t\t\t\treturn modify ? modify(q, context) : q\n\t\t\t\t},\n\t\t\t})\n\t\t\toverrideParserInQuery(query.query, field, get_page)\n\t\t\treturn query\n\t\t},\n\t})\n}\n","import { FiltersDef } from \"../filters/filters\"\nimport { defineFieldResolver, FieldResolverOptions } from \"./field\"\nimport { TableResolverFn } from \"./table\"\n\nexport interface RelationResolverOptions\n\textends Omit<FieldResolverOptions, \"modify\"> {\n\tfilters?: FiltersDef\n\tmodify?: TableResolverFn\n}\n\nexport function defineRelationResolver(options: RelationResolverOptions = {}) {\n\tconst { tableField, filters, modify } = options\n\n\treturn defineFieldResolver({\n\t\tmodify(query, context) {\n\t\t\tconst { graph, field } = context\n\t\t\treturn query.select({\n\t\t\t\t[field]: (q) => {\n\t\t\t\t\tconst relation_query = graph._resolve_type(\n\t\t\t\t\t\t(q as any)[tableField || field],\n\t\t\t\t\t\t{ ...context, filters }\n\t\t\t\t\t)\n\t\t\t\t\treturn modify ? modify(relation_query, context) : relation_query\n\t\t\t\t},\n\t\t\t})\n\t\t},\n\t})\n}\n","export function is_plain_object(obj: any): obj is Record<string, any> {\n\treturn typeof obj === \"object\" && !Array.isArray(obj)\n}\n","import { Query } from \"pqb\"\n\nimport { is_plain_object } from \"../utils/is-plain-object\"\nimport { Modifier } from \"../utils/modifier\"\n\n// TODO: per-field definitions\nexport type FiltersDef = boolean\n\nexport type FilterScalarValue = null | string | number | boolean\nexport type FilterValue = FilterScalarValue | Exclude<FilterScalarValue, null>[]\nexport type Filter = { [property: string]: FilterValue }\n\nfunction is_scalar(value: any) {\n\treturn (\n\t\tvalue === null ||\n\t\ttypeof value === \"string\" ||\n\t\ttypeof value === \"number\" ||\n\t\ttypeof value === \"boolean\"\n\t)\n}\n\nexport function apply_filters({\n\tquery,\n\tfilters,\n\targs,\n\tmodifiers,\n}: {\n\tquery: Query\n\tfilters: FiltersDef\n\targs?: Record<string, any>\n\tmodifiers?: Record<string, Modifier>\n}) {\n\tif (!filters) {\n\t\treturn query\n\t}\n\tconst filter_obj = args?.filter\n\tif (!filter_obj) {\n\t\treturn query\n\t}\n\tif (!is_plain_object(filter_obj)) {\n\t\tthrow new Error(`Invalid filter: ${filter_obj}, must be object.`)\n\t}\n\tfor (const [field, value] of Object.entries(filter_obj)) {\n\t\tif (value === undefined) {\n\t\t\t// Support optional GraphQL arguments in filter\n\t\t\tcontinue\n\t\t}\n\t\tif (modifiers?.[field]) {\n\t\t\tquery = modifiers[field](query, value)\n\t\t} else {\n\t\t\t// Normal filter\n\t\t\tconst [dbfield, op0] = field.split(\"__\")\n\t\t\tconst op = op0?.toLowerCase() || \"exact\"\n\t\t\tif (\n\t\t\t\t// TODO: add the rest of orchid ops (or allow all of them?)\n\t\t\t\t[\n\t\t\t\t\t\"exact\",\n\t\t\t\t\t\"lt\",\n\t\t\t\t\t\"lte\",\n\t\t\t\t\t\"gt\",\n\t\t\t\t\t\"gte\",\n\t\t\t\t\t\"like\",\n\t\t\t\t\t\"ilike\",\n\t\t\t\t\t\"contains\",\n\t\t\t\t].includes(op)\n\t\t\t) {\n\t\t\t\tif (!is_scalar(value)) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Unsupported filter value for ${query.table}.${field}: must be scalar.`\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tif (op === \"exact\") {\n\t\t\t\t\tquery = query.where({ [dbfield]: value })\n\t\t\t\t} else {\n\t\t\t\t\tconst query_op = { like: \"contains\" }[op] || op\n\t\t\t\t\tquery = query.where({ [dbfield]: { [query_op]: value } })\n\t\t\t\t}\n\t\t\t} else if (op === \"in\") {\n\t\t\t\tif (!(Array.isArray(value) && value.every(is_scalar))) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Invalid filter value for ${query.table}.${field}: must be an array of scalars.`\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tquery = query.whereIn(dbfield, value)\n\t\t\t} else {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Invalid filter ${query.table}.${field}: unsupported operator ${op}.`\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t}\n\treturn query\n}\n","import { DbTable, TableClass } from \"orchid-orm\"\nimport { Query } from \"pqb\"\n\nimport { apply_filters } from \"../filters/filters\"\nimport { Modifier } from \"../utils/modifier\"\nimport { run_after_query } from \"../utils/run-after\"\nimport { defineFieldResolver, FieldResolverFn } from \"./field\"\nimport type { GraphResolveContext, GraphResolver } from \"./graph\"\nimport { defineRelationResolver } from \"./relation\"\n\nexport interface TableResolveContext extends GraphResolveContext {\n\tgraph: GraphResolver\n\t/** GraphQL type */\n\ttype: string\n}\n\n/** A function that modifies the query to select fields/relations and filter the result set. */\nexport type TableResolverFn = (\n\tquery: Query,\n\tcontext: TableResolveContext\n) => Query\n\nexport interface TableResolverOptions {\n\t/** allow to resolve all table fields without explicitly listing them */\n\tallowAllFields?: boolean\n\t/** allow to filter all table fields without explicitly listing them */\n\tallowAllFilters?: boolean\n\tfields?: Record<string, SimpleFieldResolver>\n\tmodifiers?: Record<string, Modifier>\n\tmodify?: TableResolverFn\n\ttransform?(instance: any, context: any): void | PromiseLike<void>\n}\n\nexport type SimpleFieldResolver = true | string | FieldResolverFn\n\nexport function defineTableResolver<T extends TableClass>(\n\ttable: DbTable<T>,\n\toptions: TableResolverOptions = {}\n): TableResolverFn {\n\t/**\n\t * Create a field resolver which will modify the query to resolve a GraphQL field.\n\t */\n\tconst get_default_field_resolver = (\n\t\tfield: string,\n\t\ttableField?: string\n\t): FieldResolverFn | undefined => {\n\t\tconst table_field_lookup = tableField || field\n\t\tif (options.modifiers?.[table_field_lookup]) {\n\t\t\t// Keep query as is.\n\t\t\treturn (query) => query\n\t\t} else if ((table.relations as any)?.[table_field_lookup]) {\n\t\t\treturn defineRelationResolver({ tableField })\n\t\t} else {\n\t\t\treturn defineFieldResolver({ tableField })\n\t\t}\n\t}\n\n\t// Pre-create field resolvers\n\tconst { fields } = options\n\tconst table_field_resolvers = fields\n\t\t? Object.keys(fields).reduce<Record<string, FieldResolverFn>>(\n\t\t\t\t(resolvers, field) => {\n\t\t\t\t\tconst r0 = fields[field]\n\t\t\t\t\tconst r: FieldResolverFn | undefined =\n\t\t\t\t\t\ttypeof r0 === \"function\"\n\t\t\t\t\t\t\t? r0\n\t\t\t\t\t\t\t: r0 === true\n\t\t\t\t\t\t\t? get_default_field_resolver(field)\n\t\t\t\t\t\t\t: typeof r0 === \"string\"\n\t\t\t\t\t\t\t? get_default_field_resolver(field, r0)\n\t\t\t\t\t\t\t: undefined\n\t\t\t\t\tif (r === undefined) {\n\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t`Field resolver must be a function, string, or true; found ${r0}`\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t\tresolvers[field] = r\n\t\t\t\t\treturn resolvers\n\t\t\t\t},\n\t\t\t\t{}\n\t\t )\n\t\t: undefined\n\n\tconst { modify, transform } = options\n\n\treturn function resolve(query, context) {\n\t\tconst { graph, tree, type, filters } = context\n\n\t\tif (query.table !== table.table) {\n\t\t\tthrow new Error(\n\t\t\t\t`Mismatching query type (expected ${table.table}, found ${query.table}).`\n\t\t\t)\n\t\t}\n\n\t\tconst allow_all_fields =\n\t\t\toptions.allowAllFields ??\n\t\t\tgraph.options.allowAllFields ??\n\t\t\ttable_field_resolvers === undefined\n\n\t\tif (!allow_all_fields && !table_field_resolvers) {\n\t\t\tthrow new Error(\n\t\t\t\t`Table resolver for ${type} must either allow all fields or specify options.fields`\n\t\t\t)\n\t\t}\n\n\t\tif (modify) {\n\t\t\tquery = modify(query, context)\n\t\t}\n\n\t\tfor (const subtree of Object.values(tree.fieldsByTypeName[type])) {\n\t\t\tconst field = subtree.name\n\t\t\tconst r: FieldResolverFn | undefined =\n\t\t\t\ttable_field_resolvers?.[field] ||\n\t\t\t\t(allow_all_fields ? get_default_field_resolver(field) : undefined)\n\t\t\tif (!r) {\n\t\t\t\tthrow new Error(`No field resolver defined for field ${type}.${field}`)\n\t\t\t}\n\t\t\tif (r) {\n\t\t\t\tquery = r(query, { ...context, tree: subtree, field })\n\t\t\t}\n\t\t}\n\n\t\tconst allow_all_filters =\n\t\t\toptions.allowAllFilters ?? graph.options.allowAllFilters\n\n\t\tconst effective_filters = allow_all_filters ? true : filters\n\n\t\tif (effective_filters) {\n\t\t\tquery = apply_filters({\n\t\t\t\tquery,\n\t\t\t\tfilters: effective_filters,\n\t\t\t\targs: tree.args,\n\t\t\t\tmodifiers: options.modifiers,\n\t\t\t})\n\t\t}\n\n\t\tif (transform) {\n\t\t\tquery = run_after_query(query, (instance) => {\n\t\t\t\treturn transform(instance, context)\n\t\t\t})\n\t\t}\n\n\t\treturn query\n\t}\n}\n"],"mappings":"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","names":["s","cursor"]}
|
package/package.json
CHANGED
package/src/resolvers/field.ts
CHANGED
|
@@ -1,21 +1,17 @@
|
|
|
1
|
-
import { ResolveTree } from "graphql-parse-resolve-info"
|
|
2
1
|
import { Query } from "pqb"
|
|
3
2
|
|
|
4
|
-
import { get_query_context } from "../utils/query-context"
|
|
5
3
|
import { run_after_query } from "../utils/run-after"
|
|
6
|
-
import
|
|
4
|
+
import { TableResolveContext } from "./table"
|
|
5
|
+
|
|
6
|
+
export interface FieldResolveContext extends TableResolveContext {
|
|
7
|
+
/** GraphQL field */
|
|
8
|
+
field: string
|
|
9
|
+
}
|
|
7
10
|
|
|
8
11
|
/* A function that modifies the query to select a field. */
|
|
9
12
|
export type FieldResolverFn = (
|
|
10
13
|
query: Query,
|
|
11
|
-
|
|
12
|
-
/** GraphQL field */
|
|
13
|
-
field: string
|
|
14
|
-
/** GraphQL resolve tree */
|
|
15
|
-
tree: ResolveTree
|
|
16
|
-
/** Current graph resolver */
|
|
17
|
-
graph: GraphResolver
|
|
18
|
-
}
|
|
14
|
+
context: FieldResolveContext
|
|
19
15
|
) => Query
|
|
20
16
|
|
|
21
17
|
export interface FieldResolverOptions {
|
|
@@ -24,7 +20,7 @@ export interface FieldResolverOptions {
|
|
|
24
20
|
/** Custom query modifier (if different than simply selecting a field). */
|
|
25
21
|
modify?: FieldResolverFn
|
|
26
22
|
/** Post-process selected value. Return a new value or a promise. */
|
|
27
|
-
transform?(value: any, instance: any, context:
|
|
23
|
+
transform?(value: any, instance: any, context: FieldResolveContext): any
|
|
28
24
|
}
|
|
29
25
|
|
|
30
26
|
export function defineFieldResolver(
|
|
@@ -32,15 +28,14 @@ export function defineFieldResolver(
|
|
|
32
28
|
): FieldResolverFn {
|
|
33
29
|
const { tableField, modify, transform } = options
|
|
34
30
|
|
|
35
|
-
return function resolve(query,
|
|
36
|
-
const { field } =
|
|
31
|
+
return function resolve(query, context) {
|
|
32
|
+
const { field } = context
|
|
37
33
|
if (modify) {
|
|
38
|
-
|
|
34
|
+
query = modify(query, context)
|
|
39
35
|
} else {
|
|
40
36
|
query = query.select({ [field]: tableField || field })
|
|
41
37
|
}
|
|
42
38
|
if (transform) {
|
|
43
|
-
const context = get_query_context(query)
|
|
44
39
|
query = run_after_query(query, async (instance) => {
|
|
45
40
|
instance[field] = await transform(instance[field], instance, context)
|
|
46
41
|
return instance
|
package/src/resolvers/graph.ts
CHANGED
|
@@ -4,7 +4,6 @@ import { Query } from "pqb"
|
|
|
4
4
|
|
|
5
5
|
import { FiltersDef } from "../filters/filters"
|
|
6
6
|
import { Paginator } from "../paginators/base"
|
|
7
|
-
import { set_query_context } from "../utils/query-context"
|
|
8
7
|
import type { TableResolverFn, TableResolverOptions } from "./table"
|
|
9
8
|
|
|
10
9
|
export type GraphResolverOptions = Pick<
|
|
@@ -25,6 +24,12 @@ export interface GraphResolveOptions {
|
|
|
25
24
|
filters?: FiltersDef
|
|
26
25
|
}
|
|
27
26
|
|
|
27
|
+
export interface GraphResolveContext {
|
|
28
|
+
tree: ResolveTree
|
|
29
|
+
filters?: FiltersDef
|
|
30
|
+
context: any
|
|
31
|
+
}
|
|
32
|
+
|
|
28
33
|
export class GraphResolver {
|
|
29
34
|
constructor(
|
|
30
35
|
public readonly tables: Record<string, TableResolverFn>,
|
|
@@ -32,9 +37,8 @@ export class GraphResolver {
|
|
|
32
37
|
) {}
|
|
33
38
|
|
|
34
39
|
resolve(query: Query, { info, context, filters }: GraphResolveOptions) {
|
|
35
|
-
set_query_context(query, context)
|
|
36
40
|
const tree = this._get_resolve_tree(info)
|
|
37
|
-
return this._resolve_type(
|
|
41
|
+
return this._resolve_type(query, { tree, filters, context })
|
|
38
42
|
}
|
|
39
43
|
|
|
40
44
|
resolvePage(
|
|
@@ -42,43 +46,31 @@ export class GraphResolver {
|
|
|
42
46
|
paginator: Paginator,
|
|
43
47
|
{ info, context, filters }: GraphResolveOptions
|
|
44
48
|
) {
|
|
45
|
-
set_query_context(query, context)
|
|
46
49
|
const tree = this._get_resolve_tree(info)
|
|
47
|
-
return this._resolve_page(
|
|
50
|
+
return this._resolve_page(query, paginator, { tree, filters, context })
|
|
48
51
|
}
|
|
49
52
|
|
|
50
53
|
_get_resolve_tree(info: GraphQLResolveInfo) {
|
|
51
54
|
return parseResolveInfo(info) as ResolveTree
|
|
52
55
|
}
|
|
53
56
|
|
|
54
|
-
_resolve_type({
|
|
55
|
-
tree
|
|
56
|
-
query,
|
|
57
|
-
filters,
|
|
58
|
-
}: {
|
|
59
|
-
query: Query
|
|
60
|
-
tree: ResolveTree
|
|
61
|
-
filters?: FiltersDef
|
|
62
|
-
}) {
|
|
57
|
+
_resolve_type(query: Query, context: GraphResolveContext) {
|
|
58
|
+
const { tree } = context
|
|
63
59
|
const type = Object.keys(tree.fieldsByTypeName)[0]
|
|
64
60
|
const table_resolver = this.tables[type]
|
|
65
61
|
if (!table_resolver) {
|
|
66
62
|
throw new Error(`Table resolver not found for type ${type}.`)
|
|
67
63
|
}
|
|
68
|
-
return table_resolver({
|
|
64
|
+
return table_resolver(query, { ...context, graph: this, tree, type })
|
|
69
65
|
}
|
|
70
66
|
|
|
71
|
-
_resolve_page(
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
tree: ResolveTree
|
|
79
|
-
paginator: Paginator
|
|
80
|
-
filters?: FiltersDef
|
|
81
|
-
}) {
|
|
67
|
+
_resolve_page(
|
|
68
|
+
query: Query,
|
|
69
|
+
paginator: Paginator,
|
|
70
|
+
context: GraphResolveContext
|
|
71
|
+
) {
|
|
72
|
+
let { tree } = context
|
|
73
|
+
// Save pagination args
|
|
82
74
|
const { args } = tree
|
|
83
75
|
// Skip page subtree(s)
|
|
84
76
|
for (const field of paginator.path) {
|
|
@@ -86,7 +78,7 @@ export class GraphResolver {
|
|
|
86
78
|
tree = tree.fieldsByTypeName[type][field]
|
|
87
79
|
// TODO: raise exception if not found
|
|
88
80
|
}
|
|
89
|
-
query = this._resolve_type({
|
|
81
|
+
query = this._resolve_type(query, { ...context, tree })
|
|
90
82
|
return paginator.paginate(query, args)
|
|
91
83
|
}
|
|
92
84
|
}
|
package/src/resolvers/page.ts
CHANGED
|
@@ -8,23 +8,23 @@ export function definePageResolver(
|
|
|
8
8
|
paginator: Paginator,
|
|
9
9
|
options: RelationResolverOptions = {}
|
|
10
10
|
) {
|
|
11
|
-
const { tableField,
|
|
11
|
+
const { tableField, modify } = options
|
|
12
12
|
|
|
13
13
|
return defineFieldResolver({
|
|
14
|
-
modify
|
|
14
|
+
modify(query, context) {
|
|
15
|
+
const { field, graph } = context
|
|
15
16
|
// FIXME: refactor, add types
|
|
16
17
|
let get_page: any
|
|
17
18
|
query = query.select({
|
|
18
19
|
[field]: (q) => {
|
|
19
|
-
q = graph._resolve_page(
|
|
20
|
-
|
|
21
|
-
query: (q as any)[tableField || field],
|
|
20
|
+
q = graph._resolve_page(
|
|
21
|
+
(q as any)[tableField || field],
|
|
22
22
|
paginator,
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
context
|
|
24
|
+
)
|
|
25
25
|
// Calling addParserToQuery here doesn't work, save the paginator for later.
|
|
26
26
|
get_page = get_query_pagination_handler(q)
|
|
27
|
-
return modify ? modify(q,
|
|
27
|
+
return modify ? modify(q, context) : q
|
|
28
28
|
},
|
|
29
29
|
})
|
|
30
30
|
overrideParserInQuery(query.query, field, get_page)
|
|
@@ -1,27 +1,28 @@
|
|
|
1
1
|
import { FiltersDef } from "../filters/filters"
|
|
2
2
|
import { defineFieldResolver, FieldResolverOptions } from "./field"
|
|
3
|
-
import {
|
|
3
|
+
import { TableResolverFn } from "./table"
|
|
4
4
|
|
|
5
5
|
export interface RelationResolverOptions
|
|
6
6
|
extends Omit<FieldResolverOptions, "modify"> {
|
|
7
7
|
filters?: FiltersDef
|
|
8
|
-
modify?:
|
|
8
|
+
modify?: TableResolverFn
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
export function defineRelationResolver(options: RelationResolverOptions = {}) {
|
|
12
12
|
const { tableField, filters, modify } = options
|
|
13
13
|
|
|
14
14
|
return defineFieldResolver({
|
|
15
|
-
modify
|
|
16
|
-
|
|
15
|
+
modify(query, context) {
|
|
16
|
+
const { graph, field } = context
|
|
17
|
+
return query.select({
|
|
17
18
|
[field]: (q) => {
|
|
18
|
-
const relation_query = graph._resolve_type(
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
return modify ? modify(relation_query, tree) : relation_query
|
|
19
|
+
const relation_query = graph._resolve_type(
|
|
20
|
+
(q as any)[tableField || field],
|
|
21
|
+
{ ...context, filters }
|
|
22
|
+
)
|
|
23
|
+
return modify ? modify(relation_query, context) : relation_query
|
|
24
24
|
},
|
|
25
|
-
})
|
|
25
|
+
})
|
|
26
|
+
},
|
|
26
27
|
})
|
|
27
28
|
}
|
package/src/resolvers/table.ts
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
import { ResolveTree } from "graphql-parse-resolve-info"
|
|
2
1
|
import { DbTable, TableClass } from "orchid-orm"
|
|
3
2
|
import { Query } from "pqb"
|
|
4
3
|
|
|
5
|
-
import { apply_filters
|
|
4
|
+
import { apply_filters } from "../filters/filters"
|
|
6
5
|
import { Modifier } from "../utils/modifier"
|
|
7
|
-
import { get_query_context } from "../utils/query-context"
|
|
8
6
|
import { run_after_query } from "../utils/run-after"
|
|
9
7
|
import { defineFieldResolver, FieldResolverFn } from "./field"
|
|
10
|
-
import type { GraphResolver } from "./graph"
|
|
8
|
+
import type { GraphResolveContext, GraphResolver } from "./graph"
|
|
11
9
|
import { defineRelationResolver } from "./relation"
|
|
12
10
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
tree: ResolveTree
|
|
11
|
+
export interface TableResolveContext extends GraphResolveContext {
|
|
12
|
+
graph: GraphResolver
|
|
16
13
|
/** GraphQL type */
|
|
17
14
|
type: string
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** A function that modifies the query to select fields/relations and filter the result set. */
|
|
18
|
+
export type TableResolverFn = (
|
|
19
|
+
query: Query,
|
|
20
|
+
context: TableResolveContext
|
|
21
|
+
) => Query
|
|
22
22
|
|
|
23
23
|
export interface TableResolverOptions {
|
|
24
24
|
/** allow to resolve all table fields without explicitly listing them */
|
|
@@ -27,14 +27,12 @@ export interface TableResolverOptions {
|
|
|
27
27
|
allowAllFilters?: boolean
|
|
28
28
|
fields?: Record<string, SimpleFieldResolver>
|
|
29
29
|
modifiers?: Record<string, Modifier>
|
|
30
|
-
modify?:
|
|
30
|
+
modify?: TableResolverFn
|
|
31
31
|
transform?(instance: any, context: any): void | PromiseLike<void>
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
export type SimpleFieldResolver = true | string | FieldResolverFn
|
|
35
35
|
|
|
36
|
-
export type QueryTreeModifier = (query: Query, tree: ResolveTree) => Query
|
|
37
|
-
|
|
38
36
|
export function defineTableResolver<T extends TableClass>(
|
|
39
37
|
table: DbTable<T>,
|
|
40
38
|
options: TableResolverOptions = {}
|
|
@@ -85,7 +83,9 @@ export function defineTableResolver<T extends TableClass>(
|
|
|
85
83
|
|
|
86
84
|
const { modify, transform } = options
|
|
87
85
|
|
|
88
|
-
return function resolve(
|
|
86
|
+
return function resolve(query, context) {
|
|
87
|
+
const { graph, tree, type, filters } = context
|
|
88
|
+
|
|
89
89
|
if (query.table !== table.table) {
|
|
90
90
|
throw new Error(
|
|
91
91
|
`Mismatching query type (expected ${table.table}, found ${query.table}).`
|
|
@@ -104,19 +104,19 @@ export function defineTableResolver<T extends TableClass>(
|
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
if (modify) {
|
|
107
|
-
query = modify(query,
|
|
107
|
+
query = modify(query, context)
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
for (const subtree of Object.values(tree.fieldsByTypeName[type])) {
|
|
111
111
|
const field = subtree.name
|
|
112
|
-
const r =
|
|
112
|
+
const r: FieldResolverFn | undefined =
|
|
113
113
|
table_field_resolvers?.[field] ||
|
|
114
114
|
(allow_all_fields ? get_default_field_resolver(field) : undefined)
|
|
115
115
|
if (!r) {
|
|
116
116
|
throw new Error(`No field resolver defined for field ${type}.${field}`)
|
|
117
117
|
}
|
|
118
118
|
if (r) {
|
|
119
|
-
query = r(query, {
|
|
119
|
+
query = r(query, { ...context, tree: subtree, field })
|
|
120
120
|
}
|
|
121
121
|
}
|
|
122
122
|
|
|
@@ -135,7 +135,6 @@ export function defineTableResolver<T extends TableClass>(
|
|
|
135
135
|
}
|
|
136
136
|
|
|
137
137
|
if (transform) {
|
|
138
|
-
const context = get_query_context(query)
|
|
139
138
|
query = run_after_query(query, (instance) => {
|
|
140
139
|
return transform(instance, context)
|
|
141
140
|
})
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { Query } from "pqb"
|
|
2
|
-
|
|
3
|
-
const graphql = Symbol("graphql")
|
|
4
|
-
|
|
5
|
-
export function set_query_context(query: Query, context: any) {
|
|
6
|
-
;(query.query as any)[graphql] = context
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export function get_query_context(query: Query) {
|
|
10
|
-
return (query.query as any)[graphql]
|
|
11
|
-
}
|