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