@constructive-io/graphql-query 2.4.3

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/custom-ast.js ADDED
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ // @ts-nocheck
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ exports.getCustomAst = getCustomAst;
38
+ exports.geometryAst = geometryAst;
39
+ exports.intervalAst = intervalAst;
40
+ exports.isIntervalType = isIntervalType;
41
+ const t = __importStar(require("gql-ast"));
42
+ function getCustomAst(fieldDefn) {
43
+ const { pgType } = fieldDefn.type;
44
+ if (pgType === 'geometry') {
45
+ return geometryAst(fieldDefn.name);
46
+ }
47
+ if (pgType === 'interval') {
48
+ return intervalAst(fieldDefn.name);
49
+ }
50
+ return t.field({
51
+ name: fieldDefn.name
52
+ });
53
+ }
54
+ function geometryAst(name) {
55
+ return t.field({
56
+ name,
57
+ selectionSet: t.selectionSet({
58
+ selections: toFieldArray(['geojson'])
59
+ })
60
+ });
61
+ }
62
+ function intervalAst(name) {
63
+ return t.field({
64
+ name,
65
+ selectionSet: t.selectionSet({
66
+ selections: toFieldArray([
67
+ 'days',
68
+ 'hours',
69
+ 'minutes',
70
+ 'months',
71
+ 'seconds',
72
+ 'years'
73
+ ])
74
+ })
75
+ });
76
+ }
77
+ function toFieldArray(strArr) {
78
+ return strArr.map((fieldName) => t.field({ name: fieldName }));
79
+ }
80
+ function isIntervalType(obj) {
81
+ return [
82
+ 'days',
83
+ 'hours',
84
+ 'minutes',
85
+ 'months',
86
+ 'seconds',
87
+ 'years'
88
+ ].every((key) => obj.hasOwnProperty(key));
89
+ }
package/esm/ast.js ADDED
@@ -0,0 +1,585 @@
1
+ // @ts-nocheck
2
+ import * as t from 'gql-ast';
3
+ import inflection from 'inflection';
4
+ import plz from 'pluralize';
5
+ import { getCustomAst, isIntervalType } from './custom-ast';
6
+ const isObject = val => val !== null && typeof val === 'object';
7
+ const NON_MUTABLE_PROPS = ['createdAt', 'createdBy', 'updatedAt', 'updatedBy'];
8
+ const objectToArray = (obj) => Object.keys(obj).map((k) => ({ name: k, ...obj[k] }));
9
+ const createGqlMutation = ({ operationName, mutationName, selectArgs, selections, variableDefinitions, modelName, useModel = true }) => {
10
+ const opSel = !modelName
11
+ ? [
12
+ t.field({
13
+ name: operationName,
14
+ args: selectArgs,
15
+ selectionSet: t.selectionSet({ selections })
16
+ })
17
+ ]
18
+ : [
19
+ t.field({
20
+ name: operationName,
21
+ args: selectArgs,
22
+ selectionSet: t.selectionSet({
23
+ selections: useModel
24
+ ? [
25
+ t.field({
26
+ name: modelName,
27
+ selectionSet: t.selectionSet({ selections })
28
+ })
29
+ ]
30
+ : selections
31
+ })
32
+ })
33
+ ];
34
+ return t.document({
35
+ definitions: [
36
+ t.operationDefinition({
37
+ operation: 'mutation',
38
+ name: mutationName,
39
+ variableDefinitions,
40
+ selectionSet: t.selectionSet({ selections: opSel })
41
+ })
42
+ ]
43
+ });
44
+ };
45
+ export const getAll = ({ queryName, operationName, query, selection }) => {
46
+ const selections = getSelections(selection);
47
+ const opSel = [
48
+ t.field({
49
+ name: operationName,
50
+ selectionSet: t.objectValue({
51
+ fields: [
52
+ t.field({
53
+ name: 'totalCount'
54
+ }),
55
+ t.field({
56
+ name: 'nodes',
57
+ selectionSet: t.selectionSet({ selections })
58
+ })
59
+ ]
60
+ })
61
+ })
62
+ ];
63
+ const ast = t.document({
64
+ definitions: [
65
+ t.operationDefinition({
66
+ operation: 'query',
67
+ name: queryName,
68
+ selectionSet: t.selectionSet({ selections: opSel })
69
+ })
70
+ ]
71
+ });
72
+ return ast;
73
+ };
74
+ export const getMany = ({ builder, // we can use props here to enable pagination, etc
75
+ queryName, operationName, query, selection }) => {
76
+ const Singular = query.model;
77
+ const Plural = operationName.charAt(0).toUpperCase() + operationName.slice(1);
78
+ const Condition = `${Singular}Condition`;
79
+ const Filter = `${Singular}Filter`;
80
+ const OrderBy = `${Plural}OrderBy`;
81
+ const selections = getSelections(selection);
82
+ const ast = t.document({
83
+ definitions: [
84
+ t.operationDefinition({
85
+ operation: 'query',
86
+ name: queryName,
87
+ variableDefinitions: [
88
+ t.variableDefinition({
89
+ variable: t.variable({
90
+ name: 'first'
91
+ }),
92
+ type: t.namedType({
93
+ type: 'Int'
94
+ })
95
+ }),
96
+ t.variableDefinition({
97
+ variable: t.variable({
98
+ name: 'last'
99
+ }),
100
+ type: t.namedType({
101
+ type: 'Int'
102
+ })
103
+ }),
104
+ t.variableDefinition({
105
+ variable: t.variable({
106
+ name: 'after'
107
+ }),
108
+ type: t.namedType({
109
+ type: 'Cursor'
110
+ })
111
+ }),
112
+ t.variableDefinition({
113
+ variable: t.variable({
114
+ name: 'before'
115
+ }),
116
+ type: t.namedType({
117
+ type: 'Cursor'
118
+ })
119
+ }),
120
+ t.variableDefinition({
121
+ variable: t.variable({
122
+ name: 'offset'
123
+ }),
124
+ type: t.namedType({
125
+ type: 'Int'
126
+ })
127
+ }),
128
+ t.variableDefinition({
129
+ variable: t.variable({
130
+ name: 'condition'
131
+ }),
132
+ type: t.namedType({
133
+ type: Condition
134
+ })
135
+ }),
136
+ t.variableDefinition({
137
+ variable: t.variable({
138
+ name: 'filter'
139
+ }),
140
+ type: t.namedType({
141
+ type: Filter
142
+ })
143
+ }),
144
+ t.variableDefinition({
145
+ variable: t.variable({
146
+ name: 'orderBy'
147
+ }),
148
+ type: t.listType({
149
+ type: t.nonNullType({ type: t.namedType({ type: OrderBy }) })
150
+ })
151
+ })
152
+ ],
153
+ selectionSet: t.selectionSet({
154
+ selections: [
155
+ t.field({
156
+ name: operationName,
157
+ args: [
158
+ t.argument({
159
+ name: 'first',
160
+ value: t.variable({
161
+ name: 'first'
162
+ })
163
+ }),
164
+ t.argument({
165
+ name: 'last',
166
+ value: t.variable({
167
+ name: 'last'
168
+ })
169
+ }),
170
+ t.argument({
171
+ name: 'offset',
172
+ value: t.variable({
173
+ name: 'offset'
174
+ })
175
+ }),
176
+ t.argument({
177
+ name: 'after',
178
+ value: t.variable({
179
+ name: 'after'
180
+ })
181
+ }),
182
+ t.argument({
183
+ name: 'before',
184
+ value: t.variable({
185
+ name: 'before'
186
+ })
187
+ }),
188
+ t.argument({
189
+ name: 'condition',
190
+ value: t.variable({
191
+ name: 'condition'
192
+ })
193
+ }),
194
+ t.argument({
195
+ name: 'filter',
196
+ value: t.variable({
197
+ name: 'filter'
198
+ })
199
+ }),
200
+ t.argument({
201
+ name: 'orderBy',
202
+ value: t.variable({
203
+ name: 'orderBy'
204
+ })
205
+ })
206
+ ],
207
+ selectionSet: t.objectValue({
208
+ fields: [
209
+ t.field({
210
+ name: 'totalCount'
211
+ }),
212
+ t.field({
213
+ name: 'pageInfo',
214
+ selectionSet: t.selectionSet({
215
+ selections: [
216
+ t.field({ name: 'hasNextPage' }),
217
+ t.field({ name: 'hasPreviousPage' }),
218
+ t.field({ name: 'endCursor' }),
219
+ t.field({ name: 'startCursor' })
220
+ ]
221
+ })
222
+ }),
223
+ builder._edges
224
+ ? t.field({
225
+ name: 'edges',
226
+ selectionSet: t.selectionSet({
227
+ selections: [
228
+ t.field({ name: 'cursor' }),
229
+ t.field({
230
+ name: 'node',
231
+ selectionSet: t.selectionSet({ selections })
232
+ })
233
+ ]
234
+ })
235
+ })
236
+ : t.field({
237
+ name: 'nodes',
238
+ selectionSet: t.selectionSet({
239
+ selections
240
+ })
241
+ })
242
+ ]
243
+ })
244
+ })
245
+ ]
246
+ })
247
+ })
248
+ ]
249
+ });
250
+ return ast;
251
+ };
252
+ export const getOne = ({ builder, // we can use props here to enable pagination, etc
253
+ queryName, operationName, query, selection }) => {
254
+ const variableDefinitions = Object.keys(query.properties)
255
+ .map((key) => ({ name: key, ...query.properties[key] }))
256
+ .filter((field) => field.isNotNull)
257
+ .map((field) => {
258
+ const { name: fieldName, type: fieldType, isNotNull, isArray, isArrayNotNull } = field;
259
+ let type = t.namedType({ type: fieldType });
260
+ if (isNotNull)
261
+ type = t.nonNullType({ type });
262
+ if (isArray) {
263
+ type = t.listType({ type });
264
+ if (isArrayNotNull)
265
+ type = t.nonNullType({ type });
266
+ }
267
+ return t.variableDefinition({
268
+ variable: t.variable({ name: fieldName }),
269
+ type
270
+ });
271
+ });
272
+ const props = objectToArray(query.properties);
273
+ const selectArgs = props
274
+ .filter((field) => field.isNotNull)
275
+ .map((field) => {
276
+ return t.argument({
277
+ name: field.name,
278
+ value: t.variable({ name: field.name })
279
+ });
280
+ });
281
+ const selections = getSelections(selection);
282
+ const opSel = [
283
+ t.field({
284
+ name: operationName,
285
+ args: selectArgs,
286
+ selectionSet: t.selectionSet({ selections })
287
+ })
288
+ ];
289
+ const ast = t.document({
290
+ definitions: [
291
+ t.operationDefinition({
292
+ operation: 'query',
293
+ name: queryName,
294
+ variableDefinitions,
295
+ selectionSet: t.selectionSet({ selections: opSel })
296
+ })
297
+ ]
298
+ });
299
+ return ast;
300
+ };
301
+ export const createOne = ({ mutationName, operationName, mutation, selection }) => {
302
+ if (!mutation.properties?.input?.properties) {
303
+ console.log('no input field for mutation for' + mutationName);
304
+ return;
305
+ }
306
+ const modelName = inflection.camelize([plz.singular(mutation.model)].join('_'), true);
307
+ const allAttrs = objectToArray(mutation.properties.input.properties[modelName].properties);
308
+ const attrs = allAttrs.filter((field) => !NON_MUTABLE_PROPS.includes(field.name));
309
+ const variableDefinitions = getCreateVariablesAst(attrs);
310
+ const selectArgs = [
311
+ t.argument({
312
+ name: 'input',
313
+ value: t.objectValue({
314
+ fields: [
315
+ t.objectField({
316
+ name: modelName,
317
+ value: t.objectValue({
318
+ fields: attrs.map((field) => t.objectField({
319
+ name: field.name,
320
+ value: t.variable({
321
+ name: field.name
322
+ })
323
+ }))
324
+ })
325
+ })
326
+ ]
327
+ })
328
+ })
329
+ ];
330
+ const selections = selection
331
+ ? getSelections(selection)
332
+ : allAttrs.map((field) => t.field({ name: field.name }));
333
+ const ast = createGqlMutation({
334
+ operationName,
335
+ mutationName,
336
+ selectArgs,
337
+ selections,
338
+ variableDefinitions,
339
+ modelName
340
+ });
341
+ return ast;
342
+ };
343
+ export const patchOne = ({ mutationName, operationName, mutation, selection }) => {
344
+ if (!mutation.properties?.input?.properties) {
345
+ console.log('no input field for mutation for' + mutationName);
346
+ return;
347
+ }
348
+ const modelName = inflection.camelize([plz.singular(mutation.model)].join('_'), true);
349
+ const allAttrs = objectToArray(mutation.properties.input.properties['patch']?.properties || {});
350
+ const patchAttrs = allAttrs.filter((prop) => !NON_MUTABLE_PROPS.includes(prop.name));
351
+ const patchByAttrs = objectToArray(mutation.properties.input.properties).filter((n) => n.name !== 'patch');
352
+ const patchers = patchByAttrs.map((p) => p.name);
353
+ const variableDefinitions = getUpdateVariablesAst(patchAttrs, patchers);
354
+ const selectArgs = [
355
+ t.argument({
356
+ name: 'input',
357
+ value: t.objectValue({
358
+ fields: [
359
+ ...patchByAttrs.map((field) => t.objectField({
360
+ name: field.name,
361
+ value: t.variable({ name: field.name })
362
+ })),
363
+ t.objectField({
364
+ name: 'patch',
365
+ value: t.objectValue({
366
+ fields: patchAttrs
367
+ .filter((field) => !patchers.includes(field.name))
368
+ .map((field) => t.objectField({
369
+ name: field.name,
370
+ value: t.variable({
371
+ name: field.name
372
+ })
373
+ }))
374
+ })
375
+ })
376
+ ]
377
+ })
378
+ })
379
+ ];
380
+ const selections = selection
381
+ ? getSelections(selection)
382
+ : allAttrs.map((field) => t.field({ name: field.name }));
383
+ const ast = createGqlMutation({
384
+ operationName,
385
+ mutationName,
386
+ selectArgs,
387
+ selections,
388
+ variableDefinitions,
389
+ modelName
390
+ });
391
+ return ast;
392
+ };
393
+ export const deleteOne = ({ mutationName, operationName, mutation }) => {
394
+ if (!mutation.properties?.input?.properties) {
395
+ console.log('no input field for mutation for' + mutationName);
396
+ return;
397
+ }
398
+ const modelName = inflection.camelize([plz.singular(mutation.model)].join('_'), true);
399
+ const deleteAttrs = objectToArray(mutation.properties.input.properties);
400
+ const variableDefinitions = deleteAttrs.map((field) => {
401
+ const { name: fieldName, type: fieldType, isNotNull, isArray, isArrayNotNull } = field;
402
+ let type = t.namedType({ type: fieldType });
403
+ if (isNotNull)
404
+ type = t.nonNullType({ type });
405
+ if (isArray) {
406
+ type = t.listType({ type });
407
+ // no need to check isArrayNotNull since we need this field for deletion
408
+ type = t.nonNullType({ type });
409
+ }
410
+ return t.variableDefinition({
411
+ variable: t.variable({ name: fieldName }),
412
+ type
413
+ });
414
+ });
415
+ const selectArgs = [
416
+ t.argument({
417
+ name: 'input',
418
+ value: t.objectValue({
419
+ fields: deleteAttrs.map((f) => t.objectField({
420
+ name: f.name,
421
+ value: t.variable({ name: f.name })
422
+ }))
423
+ })
424
+ })
425
+ ];
426
+ // so we can support column select grants plugin
427
+ const selections = [t.field({ name: 'clientMutationId' })];
428
+ const ast = createGqlMutation({
429
+ operationName,
430
+ mutationName,
431
+ selectArgs,
432
+ selections,
433
+ useModel: false,
434
+ variableDefinitions,
435
+ modelName
436
+ });
437
+ return ast;
438
+ };
439
+ export function getSelections(selection = []) {
440
+ const selectionAst = (field) => {
441
+ return typeof field === 'string'
442
+ ? t.field({
443
+ name: field
444
+ })
445
+ : getCustomAst(field.fieldDefn);
446
+ };
447
+ return selection
448
+ .map((selectionDefn) => {
449
+ if (selectionDefn.isObject) {
450
+ const { name, selection, variables = {}, isBelongTo } = selectionDefn;
451
+ return t.field({
452
+ name,
453
+ args: Object.entries(variables).reduce((args, variable) => {
454
+ const [argName, argValue] = variable;
455
+ const argAst = t.argument({
456
+ name: argName,
457
+ value: getValueAst(argValue)
458
+ });
459
+ args = argAst ? [...args, argAst] : args;
460
+ return args;
461
+ }, []),
462
+ selectionSet: isBelongTo
463
+ ? t.selectionSet({
464
+ selections: selection.map((field) => selectionAst(field))
465
+ })
466
+ : t.objectValue({
467
+ fields: [
468
+ t.field({
469
+ name: 'totalCount'
470
+ }),
471
+ t.field({
472
+ name: 'nodes',
473
+ selectionSet: t.selectionSet({
474
+ selections: selection.map((field) => selectionAst(field))
475
+ })
476
+ })
477
+ ]
478
+ })
479
+ });
480
+ }
481
+ else {
482
+ const { fieldDefn } = selectionDefn;
483
+ // Field is not found in model meta, do nothing
484
+ if (!fieldDefn)
485
+ return null;
486
+ return getCustomAst(fieldDefn);
487
+ }
488
+ })
489
+ .filter(Boolean);
490
+ }
491
+ /**
492
+ * Get argument AST from a value
493
+ * @param {*} value
494
+ * @returns {Object} AST for the argument
495
+ */
496
+ function getValueAst(value) {
497
+ if (value == null) {
498
+ return t.nullValue();
499
+ }
500
+ if (typeof value === 'number') {
501
+ return t.intValue({ value });
502
+ }
503
+ if (typeof value === 'string') {
504
+ return t.stringValue({ value });
505
+ }
506
+ if (typeof value === 'boolean') {
507
+ return t.booleanValue({ value });
508
+ }
509
+ if (Array.isArray(value)) {
510
+ return t.listValue({ values: value.map((v) => getValueAst(v)) });
511
+ }
512
+ if (isObject(value)) {
513
+ return t.objectValue({
514
+ fields: Object.entries(value).reduce((fields, entry) => {
515
+ const [objKey, objValue] = entry;
516
+ fields = [
517
+ ...fields,
518
+ t.objectField({
519
+ name: objKey,
520
+ value: getValueAst(objValue)
521
+ })
522
+ ];
523
+ return fields;
524
+ }, [])
525
+ });
526
+ }
527
+ }
528
+ const CustomInputTypes = {
529
+ interval: 'IntervalInput'
530
+ };
531
+ /**
532
+ * Get mutation variables AST from attributes array
533
+ * @param {Array} attrs
534
+ * @returns {Object} AST for the variables
535
+ */
536
+ function getCreateVariablesAst(attrs) {
537
+ return attrs.map((field) => {
538
+ const { name: fieldName, type: fieldType, isNotNull, isArray, isArrayNotNull, properties } = field;
539
+ let type;
540
+ if (properties == null) {
541
+ type = t.namedType({ type: fieldType });
542
+ }
543
+ else if (isIntervalType(properties)) {
544
+ type = t.namedType({ type: CustomInputTypes.interval });
545
+ }
546
+ if (isNotNull)
547
+ type = t.nonNullType({ type });
548
+ if (isArray) {
549
+ type = t.listType({ type });
550
+ if (isArrayNotNull)
551
+ type = t.nonNullType({ type });
552
+ }
553
+ return t.variableDefinition({
554
+ variable: t.variable({ name: fieldName }),
555
+ type
556
+ });
557
+ });
558
+ }
559
+ /**
560
+ * Get mutation variables AST from attributes array
561
+ * @param {Array} attrs
562
+ * @returns {Object} AST for the variables
563
+ */
564
+ function getUpdateVariablesAst(attrs, patchers) {
565
+ return attrs.map((field) => {
566
+ const { name: fieldName, type: fieldType, isNotNull, isArray, properties } = field;
567
+ let type;
568
+ if (properties == null) {
569
+ type = t.namedType({ type: fieldType });
570
+ }
571
+ else if (isIntervalType(properties)) {
572
+ type = t.namedType({ type: CustomInputTypes.interval });
573
+ }
574
+ if (isNotNull)
575
+ type = t.nonNullType({ type });
576
+ if (isArray)
577
+ type = t.listType({ type });
578
+ if (patchers.includes(field.name))
579
+ type = t.nonNullType({ type });
580
+ return t.variableDefinition({
581
+ variable: t.variable({ name: fieldName }),
582
+ type
583
+ });
584
+ });
585
+ }