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 CHANGED
@@ -307,7 +307,7 @@ const graph = r.graph(
307
307
  preview,
308
308
  // Current instance
309
309
  post,
310
- // Query context
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: (q, { args }) => q.where(args).order({ id: "DESC" }),
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, options2) {
164
- const { field } = options2;
153
+ return function resolve(query, context) {
154
+ const { field } = context;
165
155
  if (modify) {
166
- return modify(query, options2);
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({ query, tree, filters });
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({ query, tree, paginator, filters });
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({ tree, type, query, filters, graph: this });
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({ query, tree, filters });
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, filters, modify } = options;
215
+ const { tableField, modify } = options;
236
216
  return defineFieldResolver({
237
- modify: (query, { field, tree, graph }) => {
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
- tree,
243
- query: q[tableField || field],
222
+ q = graph._resolve_page(
223
+ q[tableField || field],
244
224
  paginator,
245
- filters
246
- });
225
+ context
226
+ );
247
227
  get_page = get_query_pagination_handler(q);
248
- return modify ? modify(q, tree) : 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: (query, { field, tree, graph }) => query.select({
262
- [field]: (q) => {
263
- const relation_query = graph._resolve_type({
264
- tree,
265
- query: q[tableField || field],
266
- filters
267
- });
268
- return modify ? modify(relation_query, tree) : relation_query;
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({ tree, type, query, filters, graph }) {
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, tree);
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, { field, tree: subtree, graph });
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
  });
@@ -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({ tree, query, filters, }: {
77
- query: Query;
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
- type FieldResolverFn = (query: Query, options: {
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
- /** GraphQL resolve tree */
93
- tree: ResolveTree;
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: any): any;
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?: QueryTreeModifier;
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, options2) {
127
- const { field } = options2;
116
+ return function resolve(query, context) {
117
+ const { field } = context;
128
118
  if (modify) {
129
- return modify(query, options2);
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({ query, tree, filters });
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({ query, tree, paginator, filters });
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({ tree, type, query, filters, graph: this });
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({ query, tree, filters });
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, filters, modify } = options;
178
+ const { tableField, modify } = options;
199
179
  return defineFieldResolver({
200
- modify: (query, { field, tree, graph }) => {
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
- tree,
206
- query: q[tableField || field],
185
+ q = graph._resolve_page(
186
+ q[tableField || field],
207
187
  paginator,
208
- filters
209
- });
188
+ context
189
+ );
210
190
  get_page = get_query_pagination_handler(q);
211
- return modify ? modify(q, tree) : 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: (query, { field, tree, graph }) => query.select({
225
- [field]: (q) => {
226
- const relation_query = graph._resolve_type({
227
- tree,
228
- query: q[tableField || field],
229
- filters
230
- });
231
- return modify ? modify(relation_query, tree) : relation_query;
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({ tree, type, query, filters, graph }) {
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, tree);
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, { field, tree: subtree, graph });
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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "orchid-graphql",
3
3
  "description": "orchid-orm plugin: resolve GraphQL query with Orchid ORM",
4
- "version": "0.0.3",
4
+ "version": "0.0.4",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "https://github.com/IlyaSemenov/orchid-graphql"
@@ -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 type { GraphResolver } from "./graph"
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
- options: {
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: any): any
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, options) {
36
- const { field } = options
31
+ return function resolve(query, context) {
32
+ const { field } = context
37
33
  if (modify) {
38
- return modify(query, options)
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
@@ -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({ query, tree, filters })
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({ query, tree, paginator, filters })
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({ tree, type, query, filters, graph: this })
64
+ return table_resolver(query, { ...context, graph: this, tree, type })
69
65
  }
70
66
 
71
- _resolve_page({
72
- tree,
73
- query,
74
- paginator,
75
- filters,
76
- }: {
77
- query: Query
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({ query, tree, filters })
81
+ query = this._resolve_type(query, { ...context, tree })
90
82
  return paginator.paginate(query, args)
91
83
  }
92
84
  }
@@ -8,23 +8,23 @@ export function definePageResolver(
8
8
  paginator: Paginator,
9
9
  options: RelationResolverOptions = {}
10
10
  ) {
11
- const { tableField, filters, modify } = options
11
+ const { tableField, modify } = options
12
12
 
13
13
  return defineFieldResolver({
14
- modify: (query, { field, tree, graph }) => {
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
- tree,
21
- query: (q as any)[tableField || field],
20
+ q = graph._resolve_page(
21
+ (q as any)[tableField || field],
22
22
  paginator,
23
- filters,
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, tree) : 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 { QueryTreeModifier } from "./table"
3
+ import { TableResolverFn } from "./table"
4
4
 
5
5
  export interface RelationResolverOptions
6
6
  extends Omit<FieldResolverOptions, "modify"> {
7
7
  filters?: FiltersDef
8
- modify?: QueryTreeModifier
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: (query, { field, tree, graph }) =>
16
- query.select({
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
- tree,
20
- query: (q as any)[tableField || field],
21
- filters,
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
  }
@@ -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, FiltersDef } from "../filters/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
- /** A function that modifies the query to select fields/relations and filter the result set. */
14
- export type TableResolverFn = (args: {
15
- tree: ResolveTree
11
+ export interface TableResolveContext extends GraphResolveContext {
12
+ graph: GraphResolver
16
13
  /** GraphQL type */
17
14
  type: string
18
- query: Query
19
- filters?: FiltersDef
20
- graph: GraphResolver
21
- }) => Query
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?: QueryTreeModifier
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({ tree, type, query, filters, graph }) {
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, tree)
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, { field, tree: subtree, graph })
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
- }