graphile-plugin-connection-filter 2.3.1

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.
Files changed (39) hide show
  1. package/ConnectionArgFilterPlugin.d.ts +3 -0
  2. package/ConnectionArgFilterPlugin.js +18 -0
  3. package/LICENSE +23 -0
  4. package/PgConnectionArgFilterBackwardRelationsPlugin.d.ts +12 -0
  5. package/PgConnectionArgFilterBackwardRelationsPlugin.js +244 -0
  6. package/PgConnectionArgFilterColumnsPlugin.d.ts +3 -0
  7. package/PgConnectionArgFilterColumnsPlugin.js +51 -0
  8. package/PgConnectionArgFilterCompositeTypeColumnsPlugin.d.ts +3 -0
  9. package/PgConnectionArgFilterCompositeTypeColumnsPlugin.js +67 -0
  10. package/PgConnectionArgFilterComputedColumnsPlugin.d.ts +3 -0
  11. package/PgConnectionArgFilterComputedColumnsPlugin.js +114 -0
  12. package/PgConnectionArgFilterForwardRelationsPlugin.d.ts +11 -0
  13. package/PgConnectionArgFilterForwardRelationsPlugin.js +130 -0
  14. package/PgConnectionArgFilterLogicalOperatorsPlugin.d.ts +3 -0
  15. package/PgConnectionArgFilterLogicalOperatorsPlugin.js +67 -0
  16. package/PgConnectionArgFilterOperatorsPlugin.d.ts +15 -0
  17. package/PgConnectionArgFilterOperatorsPlugin.js +551 -0
  18. package/PgConnectionArgFilterPlugin.d.ts +27 -0
  19. package/PgConnectionArgFilterPlugin.js +305 -0
  20. package/PgConnectionArgFilterRecordFunctionsPlugin.d.ts +3 -0
  21. package/PgConnectionArgFilterRecordFunctionsPlugin.js +75 -0
  22. package/README.md +364 -0
  23. package/esm/ConnectionArgFilterPlugin.js +16 -0
  24. package/esm/PgConnectionArgFilterBackwardRelationsPlugin.js +242 -0
  25. package/esm/PgConnectionArgFilterColumnsPlugin.js +49 -0
  26. package/esm/PgConnectionArgFilterCompositeTypeColumnsPlugin.js +65 -0
  27. package/esm/PgConnectionArgFilterComputedColumnsPlugin.js +112 -0
  28. package/esm/PgConnectionArgFilterForwardRelationsPlugin.js +128 -0
  29. package/esm/PgConnectionArgFilterLogicalOperatorsPlugin.js +65 -0
  30. package/esm/PgConnectionArgFilterOperatorsPlugin.js +549 -0
  31. package/esm/PgConnectionArgFilterPlugin.js +303 -0
  32. package/esm/PgConnectionArgFilterRecordFunctionsPlugin.js +73 -0
  33. package/esm/index.js +58 -0
  34. package/esm/types.js +1 -0
  35. package/index.d.ts +6 -0
  36. package/index.js +64 -0
  37. package/package.json +58 -0
  38. package/types.d.ts +25 -0
  39. package/types.js +2 -0
@@ -0,0 +1,551 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const PgConnectionArgFilterOperatorsPlugin = (builder, rawOptions) => {
4
+ const { connectionFilterAllowedOperators, connectionFilterOperatorNames } = rawOptions;
5
+ builder.hook('build', (build) => {
6
+ const { graphql: { getNamedType, GraphQLBoolean, GraphQLString, GraphQLNonNull, GraphQLList, }, pgGetGqlTypeByTypeIdAndModifier, pgIntrospectionResultsByKind: introspectionResultsByKind, pgSql: sql, gql2pg, escapeLikeWildcards, } = build;
7
+ const resolveListType = (fieldInputType) => new GraphQLList(new GraphQLNonNull(fieldInputType));
8
+ const resolveListSqlValue = (input, pgType, pgTypeModifier, resolveListItemSqlValue) => input.length === 0
9
+ ? sql.query `(select null::${sql.identifier(pgType.namespaceName)}.${sql.identifier(pgType.name)} limit 0)`
10
+ : sql.query `(${sql.join(input.map((i) => resolveListItemSqlValue
11
+ ? resolveListItemSqlValue(i, pgType, pgTypeModifier)
12
+ : gql2pg(i, pgType, pgTypeModifier)), ',')})`;
13
+ const standardOperators = {
14
+ isNull: {
15
+ description: 'Is null (if `true` is specified) or is not null (if `false` is specified).',
16
+ resolveType: () => GraphQLBoolean,
17
+ resolveSqlValue: () => null, // do not parse
18
+ resolve: (i, _v, input) => sql.query `${i} ${input ? sql.query `IS NULL` : sql.query `IS NOT NULL`}`,
19
+ },
20
+ equalTo: {
21
+ description: 'Equal to the specified value.',
22
+ resolve: (i, v) => sql.query `${i} = ${v}`,
23
+ },
24
+ notEqualTo: {
25
+ description: 'Not equal to the specified value.',
26
+ resolve: (i, v) => sql.query `${i} <> ${v}`,
27
+ },
28
+ distinctFrom: {
29
+ description: 'Not equal to the specified value, treating null like an ordinary value.',
30
+ resolve: (i, v) => sql.query `${i} IS DISTINCT FROM ${v}`,
31
+ },
32
+ notDistinctFrom: {
33
+ description: 'Equal to the specified value, treating null like an ordinary value.',
34
+ resolve: (i, v) => sql.query `${i} IS NOT DISTINCT FROM ${v}`,
35
+ },
36
+ in: {
37
+ description: 'Included in the specified list.',
38
+ resolveType: resolveListType,
39
+ resolveSqlValue: resolveListSqlValue,
40
+ resolve: (i, v) => sql.query `${i} IN ${v}`,
41
+ },
42
+ notIn: {
43
+ description: 'Not included in the specified list.',
44
+ resolveType: resolveListType,
45
+ resolveSqlValue: resolveListSqlValue,
46
+ resolve: (i, v) => sql.query `${i} NOT IN ${v}`,
47
+ },
48
+ };
49
+ const sortOperators = {
50
+ lessThan: {
51
+ description: 'Less than the specified value.',
52
+ resolve: (i, v) => sql.query `${i} < ${v}`,
53
+ },
54
+ lessThanOrEqualTo: {
55
+ description: 'Less than or equal to the specified value.',
56
+ resolve: (i, v) => sql.query `${i} <= ${v}`,
57
+ },
58
+ greaterThan: {
59
+ description: 'Greater than the specified value.',
60
+ resolve: (i, v) => sql.query `${i} > ${v}`,
61
+ },
62
+ greaterThanOrEqualTo: {
63
+ description: 'Greater than or equal to the specified value.',
64
+ resolve: (i, v) => sql.query `${i} >= ${v}`,
65
+ },
66
+ };
67
+ const patternMatchingOperators = {
68
+ includes: {
69
+ description: 'Contains the specified string (case-sensitive).',
70
+ resolveInput: (input) => `%${escapeLikeWildcards(input)}%`,
71
+ resolve: (i, v) => sql.query `${i} LIKE ${v}`,
72
+ },
73
+ notIncludes: {
74
+ description: 'Does not contain the specified string (case-sensitive).',
75
+ resolveInput: (input) => `%${escapeLikeWildcards(input)}%`,
76
+ resolve: (i, v) => sql.query `${i} NOT LIKE ${v}`,
77
+ },
78
+ includesInsensitive: {
79
+ description: 'Contains the specified string (case-insensitive).',
80
+ resolveInput: (input) => `%${escapeLikeWildcards(input)}%`,
81
+ resolveSqlIdentifier: (i) => i, // avoid casting citext to text
82
+ resolve: (i, v) => sql.query `${i} ILIKE ${v}`,
83
+ },
84
+ notIncludesInsensitive: {
85
+ description: 'Does not contain the specified string (case-insensitive).',
86
+ resolveInput: (input) => `%${escapeLikeWildcards(input)}%`,
87
+ resolveSqlIdentifier: (i) => i, // avoid casting citext to text
88
+ resolve: (i, v) => sql.query `${i} NOT ILIKE ${v}`,
89
+ },
90
+ startsWith: {
91
+ description: 'Starts with the specified string (case-sensitive).',
92
+ resolveInput: (input) => `${escapeLikeWildcards(input)}%`,
93
+ resolve: (i, v) => sql.query `${i} LIKE ${v}`,
94
+ },
95
+ notStartsWith: {
96
+ description: 'Does not start with the specified string (case-sensitive).',
97
+ resolveInput: (input) => `${escapeLikeWildcards(input)}%`,
98
+ resolve: (i, v) => sql.query `${i} NOT LIKE ${v}`,
99
+ },
100
+ startsWithInsensitive: {
101
+ description: 'Starts with the specified string (case-insensitive).',
102
+ resolveInput: (input) => `${escapeLikeWildcards(input)}%`,
103
+ resolveSqlIdentifier: (i) => i, // avoid casting citext to text
104
+ resolve: (i, v) => sql.query `${i} ILIKE ${v}`,
105
+ },
106
+ notStartsWithInsensitive: {
107
+ description: 'Does not start with the specified string (case-insensitive).',
108
+ resolveInput: (input) => `${escapeLikeWildcards(input)}%`,
109
+ resolveSqlIdentifier: (i) => i, // avoid casting citext to text
110
+ resolve: (i, v) => sql.query `${i} NOT ILIKE ${v}`,
111
+ },
112
+ endsWith: {
113
+ description: 'Ends with the specified string (case-sensitive).',
114
+ resolveInput: (input) => `%${escapeLikeWildcards(input)}`,
115
+ resolve: (i, v) => sql.query `${i} LIKE ${v}`,
116
+ },
117
+ notEndsWith: {
118
+ description: 'Does not end with the specified string (case-sensitive).',
119
+ resolveInput: (input) => `%${escapeLikeWildcards(input)}`,
120
+ resolve: (i, v) => sql.query `${i} NOT LIKE ${v}`,
121
+ },
122
+ endsWithInsensitive: {
123
+ description: 'Ends with the specified string (case-insensitive).',
124
+ resolveInput: (input) => `%${escapeLikeWildcards(input)}`,
125
+ resolveSqlIdentifier: (i) => i, // avoid casting citext to text
126
+ resolve: (i, v) => sql.query `${i} ILIKE ${v}`,
127
+ },
128
+ notEndsWithInsensitive: {
129
+ description: 'Does not end with the specified string (case-insensitive).',
130
+ resolveInput: (input) => `%${escapeLikeWildcards(input)}`,
131
+ resolveSqlIdentifier: (i) => i, // avoid casting citext to text
132
+ resolve: (i, v) => sql.query `${i} NOT ILIKE ${v}`,
133
+ },
134
+ like: {
135
+ description: 'Matches the specified pattern (case-sensitive). An underscore (_) matches any single character; a percent sign (%) matches any sequence of zero or more characters.',
136
+ resolve: (i, v) => sql.query `${i} LIKE ${v}`,
137
+ },
138
+ notLike: {
139
+ description: 'Does not match the specified pattern (case-sensitive). An underscore (_) matches any single character; a percent sign (%) matches any sequence of zero or more characters.',
140
+ resolve: (i, v) => sql.query `${i} NOT LIKE ${v}`,
141
+ },
142
+ likeInsensitive: {
143
+ description: 'Matches the specified pattern (case-insensitive). An underscore (_) matches any single character; a percent sign (%) matches any sequence of zero or more characters.',
144
+ resolveSqlIdentifier: (i) => i, // avoid casting citext to text
145
+ resolve: (i, v) => sql.query `${i} ILIKE ${v}`,
146
+ },
147
+ notLikeInsensitive: {
148
+ description: 'Does not match the specified pattern (case-insensitive). An underscore (_) matches any single character; a percent sign (%) matches any sequence of zero or more characters.',
149
+ resolveSqlIdentifier: (i) => i, // avoid casting citext to text
150
+ resolve: (i, v) => sql.query `${i} NOT ILIKE ${v}`,
151
+ },
152
+ };
153
+ const hstoreOperators = {
154
+ contains: {
155
+ description: 'Contains the specified KeyValueHash.',
156
+ resolve: (i, v) => sql.query `${i} @> ${v}`,
157
+ },
158
+ containsKey: {
159
+ description: 'Contains the specified key.',
160
+ resolveType: () => GraphQLString,
161
+ resolveSqlValue: (input) => sql.query `${sql.value(input)}::text`,
162
+ resolve: (i, v) => sql.query `${i} ? ${v}`,
163
+ },
164
+ containsAllKeys: {
165
+ name: 'containsAllKeys',
166
+ description: 'Contains all of the specified keys.',
167
+ resolveType: () => new GraphQLList(new GraphQLNonNull(GraphQLString)),
168
+ resolveSqlValue: (input) => sql.value(input),
169
+ resolve: (i, v) => sql.query `${i} ?& ${v}`,
170
+ },
171
+ containsAnyKeys: {
172
+ name: 'containsAnyKeys',
173
+ description: 'Contains any of the specified keys.',
174
+ resolveType: () => new GraphQLList(new GraphQLNonNull(GraphQLString)),
175
+ resolveSqlValue: (input) => sql.value(input),
176
+ resolve: (i, v) => sql.query `${i} ?| ${v}`,
177
+ },
178
+ containedBy: {
179
+ description: 'Contained by the specified KeyValueHash.',
180
+ resolve: (i, v) => sql.query `${i} <@ ${v}`,
181
+ },
182
+ };
183
+ const jsonbOperators = {
184
+ contains: {
185
+ description: 'Contains the specified JSON.',
186
+ resolve: (i, v) => sql.query `${i} @> ${v}`,
187
+ },
188
+ containsKey: {
189
+ description: 'Contains the specified key.',
190
+ resolveType: () => GraphQLString,
191
+ resolveSqlValue: (input) => sql.query `${sql.value(input)}::text`,
192
+ resolve: (i, v) => sql.query `${i} ? ${v}`,
193
+ },
194
+ containsAllKeys: {
195
+ name: 'containsAllKeys',
196
+ description: 'Contains all of the specified keys.',
197
+ resolveType: () => new GraphQLList(new GraphQLNonNull(GraphQLString)),
198
+ resolveSqlValue: (input) => sql.value(input),
199
+ resolve: (i, v) => sql.query `${i} ?& ${v}`,
200
+ },
201
+ containsAnyKeys: {
202
+ name: 'containsAnyKeys',
203
+ description: 'Contains any of the specified keys.',
204
+ resolveType: () => new GraphQLList(new GraphQLNonNull(GraphQLString)),
205
+ resolveSqlValue: (input) => sql.value(input),
206
+ resolve: (i, v) => sql.query `${i} ?| ${v}`,
207
+ },
208
+ containedBy: {
209
+ description: 'Contained by the specified JSON.',
210
+ resolve: (i, v) => sql.query `${i} <@ ${v}`,
211
+ },
212
+ };
213
+ const inetOperators = {
214
+ contains: {
215
+ description: 'Contains the specified internet address.',
216
+ resolve: (i, v) => sql.query `${i} >> ${v}`,
217
+ },
218
+ containsOrEqualTo: {
219
+ description: 'Contains or equal to the specified internet address.',
220
+ resolve: (i, v) => sql.query `${i} >>= ${v}`,
221
+ },
222
+ containedBy: {
223
+ description: 'Contained by the specified internet address.',
224
+ resolve: (i, v) => sql.query `${i} << ${v}`,
225
+ },
226
+ containedByOrEqualTo: {
227
+ description: 'Contained by or equal to the specified internet address.',
228
+ resolve: (i, v) => sql.query `${i} <<= ${v}`,
229
+ },
230
+ containsOrContainedBy: {
231
+ description: 'Contains or contained by the specified internet address.',
232
+ resolve: (i, v) => sql.query `${i} && ${v}`,
233
+ },
234
+ };
235
+ const gqlTypeNameFromPgTypeName = (pgTypeName) => {
236
+ const pgType = introspectionResultsByKind.type.find((t) => t.name === pgTypeName);
237
+ if (!pgType) {
238
+ return null;
239
+ }
240
+ const gqlTypeName = pgGetGqlTypeByTypeIdAndModifier(pgType.id, null).name;
241
+ if (gqlTypeName === 'String') {
242
+ // PostGraphile v4 handles all unknown types as Strings, so we can't trust
243
+ // that the String operators are appropriate. Just return null so that the
244
+ // fallback type name defined below is used.
245
+ return null;
246
+ }
247
+ return gqlTypeName;
248
+ };
249
+ const _BigFloat = gqlTypeNameFromPgTypeName('numeric') || 'BigFloat';
250
+ const _BigInt = gqlTypeNameFromPgTypeName('int8') || 'BigInt';
251
+ const _BitString = gqlTypeNameFromPgTypeName('varbit') || 'BitString';
252
+ const _Boolean = gqlTypeNameFromPgTypeName('bool') || 'Boolean';
253
+ const _CidrAddress = gqlTypeNameFromPgTypeName('cidr') || 'CidrAddress';
254
+ const _Date = gqlTypeNameFromPgTypeName('date') || 'Date';
255
+ const _Datetime = gqlTypeNameFromPgTypeName('timestamp') || 'Datetime';
256
+ const _Float = gqlTypeNameFromPgTypeName('float4') || 'Float';
257
+ const _Int = gqlTypeNameFromPgTypeName('int2') || 'Int';
258
+ const _InternetAddress = gqlTypeNameFromPgTypeName('inet') || 'InternetAddress';
259
+ const _Interval = gqlTypeNameFromPgTypeName('interval') || 'Interval';
260
+ const _JSON = gqlTypeNameFromPgTypeName('jsonb') || 'JSON';
261
+ const _KeyValueHash = gqlTypeNameFromPgTypeName('hstore') || 'KeyValueHash';
262
+ const _MacAddress = gqlTypeNameFromPgTypeName('macaddr') || 'MacAddress';
263
+ const _MacAddress8 = gqlTypeNameFromPgTypeName('macaddr8') || 'MacAddress8';
264
+ const _String = gqlTypeNameFromPgTypeName('text') || 'String';
265
+ const _Time = gqlTypeNameFromPgTypeName('time') || 'Time';
266
+ const _UUID = gqlTypeNameFromPgTypeName('uuid') || 'UUID';
267
+ const connectionFilterScalarOperators = {
268
+ [_BigFloat]: { ...standardOperators, ...sortOperators },
269
+ [_BigInt]: { ...standardOperators, ...sortOperators },
270
+ [_BitString]: { ...standardOperators, ...sortOperators },
271
+ [_Boolean]: { ...standardOperators, ...sortOperators },
272
+ [_CidrAddress]: {
273
+ ...standardOperators,
274
+ ...sortOperators,
275
+ ...inetOperators,
276
+ },
277
+ [_Date]: { ...standardOperators, ...sortOperators },
278
+ [_Datetime]: { ...standardOperators, ...sortOperators },
279
+ [_Float]: { ...standardOperators, ...sortOperators },
280
+ [_Int]: { ...standardOperators, ...sortOperators },
281
+ [_InternetAddress]: {
282
+ ...standardOperators,
283
+ ...sortOperators,
284
+ ...inetOperators,
285
+ },
286
+ [_Interval]: { ...standardOperators, ...sortOperators },
287
+ [_JSON]: {
288
+ ...standardOperators,
289
+ ...sortOperators,
290
+ ...jsonbOperators,
291
+ },
292
+ [_KeyValueHash]: {
293
+ ...standardOperators,
294
+ ...hstoreOperators,
295
+ },
296
+ [_MacAddress]: {
297
+ ...standardOperators,
298
+ ...sortOperators,
299
+ },
300
+ [_MacAddress8]: {
301
+ ...standardOperators,
302
+ ...sortOperators,
303
+ },
304
+ [_String]: {
305
+ ...standardOperators,
306
+ ...sortOperators,
307
+ ...patternMatchingOperators,
308
+ },
309
+ [_Time]: { ...standardOperators, ...sortOperators },
310
+ [_UUID]: { ...standardOperators, ...sortOperators },
311
+ };
312
+ /**
313
+ * This block adds the following operators:
314
+ * - distinctFromInsensitive
315
+ * - equalToInsensitive
316
+ * - greaterThanInsensitive
317
+ * - greaterThanOrEqualToInsensitive
318
+ * - inInsensitive
319
+ * - lessThanInsensitive
320
+ * - lessThanOrEqualToInsensitive
321
+ * - notDistinctFromInsensitive
322
+ * - notEqualToInsensitive
323
+ * - notInInsensitive
324
+ *
325
+ * The compiled SQL depends on the underlying PostgreSQL column type.
326
+ * Using case-insensitive operators with `text`/`varchar`/`char` columns
327
+ * will result in calling `lower()` on the operands. Using case-sensitive
328
+ * operators with `citext` columns will result in casting the operands to `text`.
329
+ *
330
+ * For example, here is how the `equalTo`/`equalToInsensitive` operators compile to SQL:
331
+ * | GraphQL operator | PostgreSQL column type | Compiled SQL |
332
+ * | ------------------ | ----------------------- | -------------------------- |
333
+ * | equalTo | `text`/`varchar`/`char` | `<col> = $1` |
334
+ * | equalTo | `citext` | `<col>::text = $1::text` |
335
+ * | equalToInsensitive | `text`/`varchar`/`char` | `lower(<col>) = lower($1)` |
336
+ * | equalToInsensitive | `citext` | `<col> = $1` |
337
+ */
338
+ for (const [name, spec] of [
339
+ ...Object.entries(standardOperators),
340
+ ...Object.entries(sortOperators),
341
+ ]) {
342
+ if (name == 'isNull')
343
+ continue;
344
+ const description = `${spec.description.substring(0, spec.description.length - 1)} (case-insensitive).`;
345
+ const resolveSqlIdentifier = (sourceAlias, pgType) => pgType.name === 'citext'
346
+ ? sourceAlias // already case-insensitive, so no need to call `lower()`
347
+ : sql.query `lower(${sourceAlias})`;
348
+ const resolveSimpleSqlValue = (input, pgType, pgTypeModifier) => pgType.name === 'citext'
349
+ ? gql2pg(input, pgType, pgTypeModifier) // already case-insensitive, so no need to call `lower()`
350
+ : sql.query `lower(${gql2pg(input, pgType, pgTypeModifier)})`;
351
+ const resolveSqlValue = (input, pgType, pgTypeModifier) => name === 'in' || name === 'notIn'
352
+ ? resolveListSqlValue(input, pgType, pgTypeModifier, resolveSimpleSqlValue)
353
+ : resolveSimpleSqlValue(input, pgType, pgTypeModifier);
354
+ connectionFilterScalarOperators[_String][`${name}Insensitive`] = {
355
+ ...spec,
356
+ description,
357
+ resolveSqlIdentifier,
358
+ resolveSqlValue,
359
+ };
360
+ }
361
+ const connectionFilterEnumOperators = {
362
+ ...standardOperators,
363
+ ...sortOperators,
364
+ };
365
+ const connectionFilterRangeOperators = {
366
+ ...standardOperators,
367
+ ...sortOperators,
368
+ contains: {
369
+ description: 'Contains the specified range.',
370
+ resolve: (i, v) => sql.query `${i} @> ${v}`,
371
+ },
372
+ containsElement: {
373
+ description: 'Contains the specified value.',
374
+ resolveType: (_fieldInputType, rangeElementInputType) => rangeElementInputType,
375
+ resolveSqlValue: (input, pgType, pgTypeModifier) => {
376
+ const rangeSubType = introspectionResultsByKind.typeById[pgType.rangeSubTypeId];
377
+ return sql.query `${gql2pg(input, pgType.rangeSubTypeId, pgTypeModifier)}::${sql.identifier(rangeSubType.namespaceName, rangeSubType.name)}`;
378
+ },
379
+ resolve: (i, v) => sql.query `${i} @> ${v}`,
380
+ },
381
+ containedBy: {
382
+ description: 'Contained by the specified range.',
383
+ resolve: (i, v) => sql.query `${i} <@ ${v}`,
384
+ },
385
+ overlaps: {
386
+ description: 'Overlaps the specified range.',
387
+ resolve: (i, v) => sql.query `${i} && ${v}`,
388
+ },
389
+ strictlyLeftOf: {
390
+ description: 'Strictly left of the specified range.',
391
+ resolve: (i, v) => sql.query `${i} << ${v}`,
392
+ },
393
+ strictlyRightOf: {
394
+ description: 'Strictly right of the specified range.',
395
+ resolve: (i, v) => sql.query `${i} >> ${v}`,
396
+ },
397
+ notExtendsRightOf: {
398
+ description: 'Does not extend right of the specified range.',
399
+ resolve: (i, v) => sql.query `${i} &< ${v}`,
400
+ },
401
+ notExtendsLeftOf: {
402
+ description: 'Does not extend left of the specified range.',
403
+ resolve: (i, v) => sql.query `${i} &> ${v}`,
404
+ },
405
+ adjacentTo: {
406
+ description: 'Adjacent to the specified range.',
407
+ resolve: (i, v) => sql.query `${i} -|- ${v}`,
408
+ },
409
+ };
410
+ const resolveArrayItemType = (fieldInputType) => getNamedType(fieldInputType);
411
+ const resolveArrayItemSqlValue = (input, pgType, pgTypeModifier) => gql2pg(input, pgType.arrayItemType, pgTypeModifier);
412
+ const connectionFilterArrayOperators = {
413
+ isNull: standardOperators.isNull,
414
+ equalTo: standardOperators.equalTo,
415
+ notEqualTo: standardOperators.notEqualTo,
416
+ distinctFrom: standardOperators.distinctFrom,
417
+ notDistinctFrom: standardOperators.notDistinctFrom,
418
+ ...sortOperators,
419
+ contains: {
420
+ description: 'Contains the specified list of values.',
421
+ resolve: (i, v) => sql.query `${i} @> ${v}`,
422
+ },
423
+ containedBy: {
424
+ description: 'Contained by the specified list of values.',
425
+ resolve: (i, v) => sql.query `${i} <@ ${v}`,
426
+ },
427
+ overlaps: {
428
+ description: 'Overlaps the specified list of values.',
429
+ resolve: (i, v) => sql.query `${i} && ${v}`,
430
+ },
431
+ anyEqualTo: {
432
+ description: 'Any array item is equal to the specified value.',
433
+ resolveType: resolveArrayItemType,
434
+ resolveSqlValue: resolveArrayItemSqlValue,
435
+ resolve: (i, v) => sql.query `${v} = ANY (${i})`,
436
+ },
437
+ anyNotEqualTo: {
438
+ description: 'Any array item is not equal to the specified value.',
439
+ resolveType: resolveArrayItemType,
440
+ resolveSqlValue: resolveArrayItemSqlValue,
441
+ resolve: (i, v) => sql.query `${v} <> ANY (${i})`,
442
+ },
443
+ anyLessThan: {
444
+ description: 'Any array item is less than the specified value.',
445
+ resolveType: resolveArrayItemType,
446
+ resolveSqlValue: resolveArrayItemSqlValue,
447
+ resolve: (i, v) => sql.query `${v} > ANY (${i})`,
448
+ },
449
+ anyLessThanOrEqualTo: {
450
+ description: 'Any array item is less than or equal to the specified value.',
451
+ resolveType: resolveArrayItemType,
452
+ resolveSqlValue: resolveArrayItemSqlValue,
453
+ resolve: (i, v) => sql.query `${v} >= ANY (${i})`,
454
+ },
455
+ anyGreaterThan: {
456
+ description: 'Any array item is greater than the specified value.',
457
+ resolveType: resolveArrayItemType,
458
+ resolveSqlValue: resolveArrayItemSqlValue,
459
+ resolve: (i, v) => sql.query `${v} < ANY (${i})`,
460
+ },
461
+ anyGreaterThanOrEqualTo: {
462
+ description: 'Any array item is greater than or equal to the specified value.',
463
+ resolveType: resolveArrayItemType,
464
+ resolveSqlValue: resolveArrayItemSqlValue,
465
+ resolve: (i, v) => sql.query `${v} <= ANY (${i})`,
466
+ },
467
+ };
468
+ return build.extend(build, {
469
+ connectionFilterArrayOperators,
470
+ connectionFilterEnumOperators,
471
+ connectionFilterRangeOperators,
472
+ connectionFilterScalarOperators,
473
+ });
474
+ });
475
+ builder.hook('GraphQLInputObjectType:fields', (fields, build, context) => {
476
+ const { extend, gql2pg, pgIntrospectionResultsByKind: introspectionResultsByKind, pgSql: sql, connectionFilterRegisterResolver, connectionFilterTypesByTypeName, connectionFilterArrayOperators, connectionFilterEnumOperators, connectionFilterRangeOperators, connectionFilterScalarOperators, } = build;
477
+ const { scope: { isPgConnectionFilterOperators, pgConnectionFilterOperatorsCategory, fieldType, fieldInputType, rangeElementInputType, domainBaseType, }, fieldWithHooks, Self, } = context;
478
+ if (!isPgConnectionFilterOperators ||
479
+ !pgConnectionFilterOperatorsCategory ||
480
+ !fieldType ||
481
+ !fieldInputType) {
482
+ return fields;
483
+ }
484
+ connectionFilterTypesByTypeName[Self.name] = Self;
485
+ const operatorSpecsByCategory = {
486
+ Array: connectionFilterArrayOperators,
487
+ Range: connectionFilterRangeOperators,
488
+ Enum: connectionFilterEnumOperators,
489
+ Domain: domainBaseType
490
+ ? connectionFilterScalarOperators[domainBaseType.name]
491
+ : {},
492
+ Scalar: connectionFilterScalarOperators[fieldType.name],
493
+ };
494
+ const operatorSpecs = operatorSpecsByCategory[pgConnectionFilterOperatorsCategory];
495
+ if (!operatorSpecs) {
496
+ return fields;
497
+ }
498
+ const operatorSpecByFieldName = {};
499
+ const operatorFields = Object.entries(operatorSpecs).reduce((memo, [name, spec]) => {
500
+ const { description, resolveType } = spec;
501
+ if (connectionFilterAllowedOperators &&
502
+ !connectionFilterAllowedOperators.includes(name)) {
503
+ return memo;
504
+ }
505
+ const type = resolveType
506
+ ? resolveType(fieldInputType, rangeElementInputType)
507
+ : fieldInputType;
508
+ const operatorName = (connectionFilterOperatorNames &&
509
+ connectionFilterOperatorNames[name]) ||
510
+ name;
511
+ operatorSpecByFieldName[operatorName] = spec;
512
+ memo[operatorName] = fieldWithHooks(operatorName, {
513
+ description,
514
+ type,
515
+ }, {
516
+ isPgConnectionFilterOperator: true,
517
+ });
518
+ return memo;
519
+ }, {});
520
+ const textPgType = introspectionResultsByKind.type.find((t) => t.name === 'text');
521
+ const textArrayPgType = introspectionResultsByKind.type.find((t) => t.name === '_text');
522
+ const resolve = ({ sourceAlias, fieldName, fieldValue, queryBuilder, pgType, pgTypeModifier, parentFieldName, }) => {
523
+ if (fieldValue == null)
524
+ return null;
525
+ const operatorSpec = operatorSpecByFieldName[fieldName];
526
+ const { resolveInput, resolveSqlIdentifier, resolveSqlValue } = operatorSpec;
527
+ const sqlIdentifier = resolveSqlIdentifier
528
+ ? resolveSqlIdentifier(sourceAlias, pgType, pgTypeModifier)
529
+ : pgType.name === 'citext'
530
+ ? sql.query `${sourceAlias}::text` // cast column to text for case-sensitive matching
531
+ : pgType.name === '_citext'
532
+ ? sql.query `${sourceAlias}::text[]` // cast column to text[] for case-sensitive matching
533
+ : sourceAlias;
534
+ const input = fieldValue;
535
+ const resolvedInput = resolveInput ? resolveInput(input) : input;
536
+ const sqlValue = resolveSqlValue
537
+ ? resolveSqlValue(input, pgType, pgTypeModifier)
538
+ : pgType.name === 'citext'
539
+ ? gql2pg(resolvedInput, textPgType, null) // cast input to text
540
+ : pgType.name === '_citext'
541
+ ? gql2pg(resolvedInput, textArrayPgType, null) // cast input to text[]
542
+ : gql2pg(resolvedInput, pgType, pgTypeModifier);
543
+ return operatorSpec.resolve(sqlIdentifier, sqlValue, input, parentFieldName, queryBuilder);
544
+ };
545
+ for (const fieldName of Object.keys(operatorFields)) {
546
+ connectionFilterRegisterResolver(Self.name, fieldName, resolve);
547
+ }
548
+ return extend(fields, operatorFields);
549
+ });
550
+ };
551
+ exports.default = PgConnectionArgFilterOperatorsPlugin;
@@ -0,0 +1,27 @@
1
+ import type { Plugin } from 'graphile-build';
2
+ import type { PgType, QueryBuilder, SQL } from 'graphile-build-pg';
3
+ import type { GraphQLInputType, GraphQLType } from 'graphql';
4
+ import { BackwardRelationSpec } from './PgConnectionArgFilterBackwardRelationsPlugin';
5
+ declare const PgConnectionArgFilterPlugin: Plugin;
6
+ export interface ConnectionFilterResolver {
7
+ (input: {
8
+ sourceAlias: SQL;
9
+ fieldName: string;
10
+ fieldValue?: unknown;
11
+ queryBuilder: QueryBuilder;
12
+ pgType: PgType;
13
+ pgTypeModifier: number | null;
14
+ parentFieldName: string;
15
+ parentFieldInfo?: {
16
+ backwardRelationSpec?: BackwardRelationSpec;
17
+ };
18
+ }): SQL | null;
19
+ }
20
+ export interface AddConnectionFilterOperator {
21
+ (typeNames: string | string[], operatorName: string, description: string | null, resolveType: (fieldInputType: GraphQLInputType, rangeElementInputType: GraphQLInputType) => GraphQLType, resolve: (sqlIdentifier: SQL, sqlValue: SQL, input: unknown, parentFieldName: string, queryBuilder: QueryBuilder) => SQL | null, options?: {
22
+ resolveInput?: (input: unknown) => unknown;
23
+ resolveSqlIdentifier?: (sqlIdentifier: SQL, pgType: PgType, pgTypeModifier: number | null) => SQL;
24
+ resolveSqlValue?: (input: unknown, pgType: PgType, pgTypeModifier: number | null, resolveListItemSqlValue?: any) => SQL | null;
25
+ }): void;
26
+ }
27
+ export default PgConnectionArgFilterPlugin;