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,49 @@
1
+ const PgConnectionArgFilterColumnsPlugin = (builder) => {
2
+ builder.hook('GraphQLInputObjectType:fields', (fields, build, context) => {
3
+ const { extend, newWithHooks, pgSql: sql, pgIntrospectionResultsByKind: introspectionResultsByKind, pgColumnFilter, pgOmit: omit, inflection, connectionFilterOperatorsType, connectionFilterRegisterResolver, connectionFilterResolve, connectionFilterTypesByTypeName, } = build;
4
+ const { fieldWithHooks, scope: { pgIntrospection: table, isPgConnectionFilter }, Self, } = context;
5
+ if (!isPgConnectionFilter || table.kind !== 'class')
6
+ return fields;
7
+ connectionFilterTypesByTypeName[Self.name] = Self;
8
+ const attrByFieldName = introspectionResultsByKind.attribute
9
+ .filter((attr) => attr.classId === table.id)
10
+ .filter((attr) => pgColumnFilter(attr, build, context))
11
+ .filter((attr) => !omit(attr, 'filter'))
12
+ .reduce((memo, attr) => {
13
+ const fieldName = inflection.column(attr);
14
+ memo[fieldName] = attr;
15
+ return memo;
16
+ }, {});
17
+ const operatorsTypeNameByFieldName = {};
18
+ const attrFields = Object.entries(attrByFieldName).reduce((memo, [fieldName, attr]) => {
19
+ const OperatorsType = connectionFilterOperatorsType(newWithHooks, attr.typeId, attr.typeModifier);
20
+ if (!OperatorsType) {
21
+ return memo;
22
+ }
23
+ operatorsTypeNameByFieldName[fieldName] = OperatorsType.name;
24
+ return extend(memo, {
25
+ [fieldName]: fieldWithHooks(fieldName, {
26
+ description: `Filter by the object’s \`${fieldName}\` field.`,
27
+ type: OperatorsType,
28
+ }, {
29
+ isPgConnectionFilterField: true,
30
+ }),
31
+ });
32
+ }, {});
33
+ const resolve = ({ sourceAlias, fieldName, fieldValue, queryBuilder, }) => {
34
+ if (fieldValue == null)
35
+ return null;
36
+ const attr = attrByFieldName[fieldName];
37
+ const sqlIdentifier = sql.query `${sourceAlias}.${sql.identifier(attr.name)}`;
38
+ const pgType = attr.type;
39
+ const pgTypeModifier = attr.typeModifier;
40
+ const filterTypeName = operatorsTypeNameByFieldName[fieldName];
41
+ return connectionFilterResolve(fieldValue, sqlIdentifier, filterTypeName, queryBuilder, pgType, pgTypeModifier, fieldName);
42
+ };
43
+ for (const fieldName of Object.keys(attrFields)) {
44
+ connectionFilterRegisterResolver(Self.name, fieldName, resolve);
45
+ }
46
+ return extend(fields, attrFields);
47
+ });
48
+ };
49
+ export default PgConnectionArgFilterColumnsPlugin;
@@ -0,0 +1,65 @@
1
+ const PgConnectionArgFilterCompositeTypeColumnsPlugin = (builder, rawOptions) => {
2
+ const { connectionFilterAllowedFieldTypes } = rawOptions;
3
+ builder.hook('GraphQLInputObjectType:fields', (fields, build, context) => {
4
+ const { extend, newWithHooks, pgSql: sql, pgIntrospectionResultsByKind: introspectionResultsByKind, pgGetGqlTypeByTypeIdAndModifier, pgColumnFilter, pgOmit: omit, inflection, connectionFilterRegisterResolver, connectionFilterResolve, connectionFilterType, connectionFilterTypesByTypeName, } = build;
5
+ const { fieldWithHooks, scope: { pgIntrospection: table, isPgConnectionFilter }, Self, } = context;
6
+ if (!isPgConnectionFilter || table.kind !== 'class')
7
+ return fields;
8
+ connectionFilterTypesByTypeName[Self.name] = Self;
9
+ const attrByFieldName = introspectionResultsByKind.attribute
10
+ .filter((attr) => attr.classId === table.id)
11
+ .filter((attr) => pgColumnFilter(attr, build, context))
12
+ .filter((attr) => !omit(attr, 'filter'))
13
+ .filter((attr) => attr.type &&
14
+ attr.type.type === 'c' &&
15
+ attr.type.class &&
16
+ !attr.type.class.isSelectable) // keep only the composite type columns
17
+ .reduce((memo, attr) => {
18
+ const fieldName = inflection.column(attr);
19
+ memo[fieldName] = attr;
20
+ return memo;
21
+ }, {});
22
+ const filterTypeNameByFieldName = {};
23
+ const attrFields = Object.entries(attrByFieldName).reduce((memo, [fieldName, attr]) => {
24
+ const NodeType = pgGetGqlTypeByTypeIdAndModifier(attr.typeId, attr.typeModifier);
25
+ if (!NodeType) {
26
+ return memo;
27
+ }
28
+ const nodeTypeName = NodeType.name;
29
+ // Respect `connectionFilterAllowedFieldTypes` config option
30
+ if (connectionFilterAllowedFieldTypes &&
31
+ !connectionFilterAllowedFieldTypes.includes(nodeTypeName)) {
32
+ return memo;
33
+ }
34
+ const filterTypeName = inflection.filterType(nodeTypeName);
35
+ const CompositeFilterType = connectionFilterType(newWithHooks, filterTypeName, attr.type.class, nodeTypeName);
36
+ if (!CompositeFilterType) {
37
+ return memo;
38
+ }
39
+ filterTypeNameByFieldName[fieldName] = filterTypeName;
40
+ return extend(memo, {
41
+ [fieldName]: fieldWithHooks(fieldName, {
42
+ description: `Filter by the object’s \`${fieldName}\` field.`,
43
+ type: CompositeFilterType,
44
+ }, {
45
+ isPgConnectionFilterField: true,
46
+ }),
47
+ });
48
+ }, {});
49
+ const resolve = ({ sourceAlias, fieldName, fieldValue, queryBuilder, }) => {
50
+ if (fieldValue == null)
51
+ return null;
52
+ const attr = attrByFieldName[fieldName];
53
+ const sqlIdentifier = sql.query `(${sourceAlias}.${sql.identifier(attr.name)})`; // parentheses are required to avoid confusing the parser
54
+ const pgType = attr.type;
55
+ const pgTypeModifier = attr.typeModifier;
56
+ const filterTypeName = filterTypeNameByFieldName[fieldName];
57
+ return connectionFilterResolve(fieldValue, sqlIdentifier, filterTypeName, queryBuilder, pgType, pgTypeModifier, fieldName);
58
+ };
59
+ for (const fieldName of Object.keys(attrFields)) {
60
+ connectionFilterRegisterResolver(Self.name, fieldName, resolve);
61
+ }
62
+ return extend(fields, attrFields);
63
+ });
64
+ };
65
+ export default PgConnectionArgFilterCompositeTypeColumnsPlugin;
@@ -0,0 +1,112 @@
1
+ const PgConnectionArgFilterComputedColumnsPlugin = (builder, rawOptions) => {
2
+ const { connectionFilterComputedColumns } = rawOptions;
3
+ builder.hook('GraphQLInputObjectType:fields', (fields, build, context) => {
4
+ const { extend, newWithHooks, pgIntrospectionResultsByKind: introspectionResultsByKind, pgOmit: omit, pgSql: sql, inflection, connectionFilterOperatorsType, connectionFilterRegisterResolver, connectionFilterResolve, connectionFilterTypesByTypeName, } = build;
5
+ const { scope: { isPgConnectionFilter, pgIntrospection: table }, fieldWithHooks, Self, } = context;
6
+ if (!isPgConnectionFilter || !table || table.kind !== 'class') {
7
+ return fields;
8
+ }
9
+ connectionFilterTypesByTypeName[Self.name] = Self;
10
+ const procByFieldName = introspectionResultsByKind.procedure.reduce((memo, proc) => {
11
+ // Must be marked @filterable OR enabled via plugin option
12
+ if (!(proc.tags.filterable || connectionFilterComputedColumns))
13
+ return memo;
14
+ // Must not be omitted
15
+ if (omit(proc, 'execute'))
16
+ return memo;
17
+ if (omit(proc, 'filter'))
18
+ return memo;
19
+ // Must be a computed column
20
+ const computedColumnDetails = getComputedColumnDetails(build, table, proc);
21
+ if (!computedColumnDetails)
22
+ return memo;
23
+ const { pseudoColumnName } = computedColumnDetails;
24
+ // Must have only one required argument
25
+ const inputArgsCount = proc.argTypeIds.filter((_typeId, idx) => proc.argModes.length === 0 || // all args are `in`
26
+ proc.argModes[idx] === 'i' || // this arg is `in`
27
+ proc.argModes[idx] === 'b' // this arg is `inout`
28
+ ).length;
29
+ const nonOptionalArgumentsCount = inputArgsCount - proc.argDefaultsNum;
30
+ if (nonOptionalArgumentsCount > 1) {
31
+ return memo;
32
+ }
33
+ // Must return a scalar or an array
34
+ if (proc.returnsSet)
35
+ return memo;
36
+ const returnType = introspectionResultsByKind.typeById[proc.returnTypeId];
37
+ const returnTypeTable = introspectionResultsByKind.classById[returnType.classId];
38
+ if (returnTypeTable)
39
+ return memo;
40
+ const isRecordLike = returnType.id === '2249';
41
+ if (isRecordLike)
42
+ return memo;
43
+ const isVoid = String(returnType.id) === '2278';
44
+ if (isVoid)
45
+ return memo;
46
+ // Looks good
47
+ const fieldName = inflection.computedColumn(pseudoColumnName, proc, table);
48
+ memo = build.extend(memo, { [fieldName]: proc });
49
+ return memo;
50
+ }, {});
51
+ const operatorsTypeNameByFieldName = {};
52
+ const procFields = Object.entries(procByFieldName).reduce((memo, [fieldName, proc]) => {
53
+ const OperatorsType = connectionFilterOperatorsType(newWithHooks, proc.returnTypeId, null);
54
+ if (!OperatorsType) {
55
+ return memo;
56
+ }
57
+ operatorsTypeNameByFieldName[fieldName] = OperatorsType.name;
58
+ return extend(memo, {
59
+ [fieldName]: fieldWithHooks(fieldName, {
60
+ description: `Filter by the object’s \`${fieldName}\` field.`,
61
+ type: OperatorsType,
62
+ }, {
63
+ isPgConnectionFilterField: true,
64
+ }),
65
+ });
66
+ }, {});
67
+ const resolve = ({ sourceAlias, fieldName, fieldValue, queryBuilder, }) => {
68
+ if (fieldValue == null)
69
+ return null;
70
+ const proc = procByFieldName[fieldName];
71
+ const sqlIdentifier = sql.query `${sql.identifier(proc.namespace.name)}.${sql.identifier(proc.name)}(${sourceAlias})`;
72
+ const pgType = introspectionResultsByKind.typeById[proc.returnTypeId];
73
+ const pgTypeModifier = null;
74
+ const filterTypeName = operatorsTypeNameByFieldName[fieldName];
75
+ return connectionFilterResolve(fieldValue, sqlIdentifier, filterTypeName, queryBuilder, pgType, pgTypeModifier, fieldName);
76
+ };
77
+ for (const fieldName of Object.keys(procFields)) {
78
+ connectionFilterRegisterResolver(Self.name, fieldName, resolve);
79
+ }
80
+ return extend(fields, procFields);
81
+ });
82
+ function getComputedColumnDetails(build, table, proc) {
83
+ if (!proc.isStable)
84
+ return null;
85
+ if (proc.namespaceId !== table.namespaceId)
86
+ return null;
87
+ if (!proc.name.startsWith(`${table.name}_`))
88
+ return null;
89
+ if (proc.argTypeIds.length < 1)
90
+ return null;
91
+ if (proc.argTypeIds[0] !== table.type.id)
92
+ return null;
93
+ const argTypes = proc.argTypeIds.reduce((prev, typeId, idx) => {
94
+ if (proc.argModes.length === 0 || // all args are `in`
95
+ proc.argModes[idx] === 'i' || // this arg is `in`
96
+ proc.argModes[idx] === 'b' // this arg is `inout`
97
+ ) {
98
+ prev.push(build.pgIntrospectionResultsByKind.typeById[typeId]);
99
+ }
100
+ return prev;
101
+ }, []);
102
+ if (argTypes
103
+ .slice(1)
104
+ .some((type) => type.type === 'c' && type.class && type.class.isSelectable)) {
105
+ // Accepts two input tables? Skip.
106
+ return null;
107
+ }
108
+ const pseudoColumnName = proc.name.substr(table.name.length + 1);
109
+ return { argTypes, pseudoColumnName };
110
+ }
111
+ };
112
+ export default PgConnectionArgFilterComputedColumnsPlugin;
@@ -0,0 +1,128 @@
1
+ const PgConnectionArgFilterForwardRelationsPlugin = (builder) => {
2
+ builder.hook('inflection', (inflection) => ({
3
+ ...inflection,
4
+ filterForwardRelationExistsFieldName(relationFieldName) {
5
+ return `${relationFieldName}Exists`;
6
+ },
7
+ filterSingleRelationFieldName(fieldName) {
8
+ return fieldName;
9
+ },
10
+ }));
11
+ builder.hook('GraphQLInputObjectType:fields', (fields, build, context) => {
12
+ const { describePgEntity, extend, newWithHooks, inflection, graphql: { GraphQLBoolean }, pgOmit: omit, pgSql: sql, pgIntrospectionResultsByKind: introspectionResultsByKind, connectionFilterResolve, connectionFilterRegisterResolver, connectionFilterTypesByTypeName, connectionFilterType, } = build;
13
+ const { fieldWithHooks, scope: { pgIntrospection: table, isPgConnectionFilter }, Self, } = context;
14
+ if (!isPgConnectionFilter || table.kind !== 'class')
15
+ return fields;
16
+ connectionFilterTypesByTypeName[Self.name] = Self;
17
+ const forwardRelationSpecs = introspectionResultsByKind.constraint
18
+ .filter((con) => con.type === 'f')
19
+ .filter((con) => con.classId === table.id)
20
+ .reduce((memo, constraint) => {
21
+ if (omit(constraint, 'read') || omit(constraint, 'filter')) {
22
+ return memo;
23
+ }
24
+ const foreignTable = constraint.foreignClassId
25
+ ? introspectionResultsByKind.classById[constraint.foreignClassId]
26
+ : null;
27
+ if (!foreignTable) {
28
+ throw new Error(`Could not find the foreign table (constraint: ${constraint.name})`);
29
+ }
30
+ if (omit(foreignTable, 'read') || omit(foreignTable, 'filter')) {
31
+ return memo;
32
+ }
33
+ const attributes = introspectionResultsByKind.attribute
34
+ .filter((attr) => attr.classId === table.id)
35
+ .sort((a, b) => a.num - b.num);
36
+ const foreignAttributes = introspectionResultsByKind.attribute
37
+ .filter((attr) => attr.classId === foreignTable.id)
38
+ .sort((a, b) => a.num - b.num);
39
+ const keyAttributes = constraint.keyAttributeNums.map((num) => attributes.filter((attr) => attr.num === num)[0]);
40
+ const foreignKeyAttributes = constraint.foreignKeyAttributeNums.map((num) => foreignAttributes.filter((attr) => attr.num === num)[0]);
41
+ if (keyAttributes.some((attr) => omit(attr, 'read'))) {
42
+ return memo;
43
+ }
44
+ if (foreignKeyAttributes.some((attr) => omit(attr, 'read'))) {
45
+ return memo;
46
+ }
47
+ memo.push({
48
+ table,
49
+ keyAttributes,
50
+ foreignTable,
51
+ foreignKeyAttributes,
52
+ constraint,
53
+ });
54
+ return memo;
55
+ }, []);
56
+ let forwardRelationSpecByFieldName = {};
57
+ const addField = (fieldName, description, type, resolve, spec, hint) => {
58
+ // Field
59
+ fields = extend(fields, {
60
+ [fieldName]: fieldWithHooks(fieldName, {
61
+ description,
62
+ type,
63
+ }, {
64
+ isPgConnectionFilterField: true,
65
+ }),
66
+ }, hint);
67
+ // Spec for use in resolver
68
+ forwardRelationSpecByFieldName = extend(forwardRelationSpecByFieldName, {
69
+ [fieldName]: spec,
70
+ });
71
+ // Resolver
72
+ connectionFilterRegisterResolver(Self.name, fieldName, resolve);
73
+ };
74
+ const resolve = ({ sourceAlias, fieldName, fieldValue, queryBuilder, }) => {
75
+ if (fieldValue == null)
76
+ return null;
77
+ const { foreignTable, foreignKeyAttributes, keyAttributes } = forwardRelationSpecByFieldName[fieldName];
78
+ const foreignTableAlias = sql.identifier(Symbol());
79
+ const sqlIdentifier = sql.identifier(foreignTable.namespace.name, foreignTable.name);
80
+ const sqlKeysMatch = sql.query `(${sql.join(keyAttributes.map((key, i) => {
81
+ return sql.fragment `${sourceAlias}.${sql.identifier(key.name)} = ${foreignTableAlias}.${sql.identifier(foreignKeyAttributes[i].name)}`;
82
+ }), ') and (')})`;
83
+ const foreignTableTypeName = inflection.tableType(foreignTable);
84
+ const foreignTableFilterTypeName = inflection.filterType(foreignTableTypeName);
85
+ const sqlFragment = connectionFilterResolve(fieldValue, foreignTableAlias, foreignTableFilterTypeName, queryBuilder);
86
+ return sqlFragment == null
87
+ ? null
88
+ : sql.query `\
89
+ exists(
90
+ select 1 from ${sqlIdentifier} as ${foreignTableAlias}
91
+ where ${sqlKeysMatch} and
92
+ (${sqlFragment})
93
+ )`;
94
+ };
95
+ const resolveExists = ({ sourceAlias, fieldName, fieldValue, }) => {
96
+ if (fieldValue == null)
97
+ return null;
98
+ const { foreignTable, foreignKeyAttributes, keyAttributes } = forwardRelationSpecByFieldName[fieldName];
99
+ const foreignTableAlias = sql.identifier(Symbol());
100
+ const sqlIdentifier = sql.identifier(foreignTable.namespace.name, foreignTable.name);
101
+ const sqlKeysMatch = sql.query `(${sql.join(keyAttributes.map((key, i) => {
102
+ return sql.fragment `${sourceAlias}.${sql.identifier(key.name)} = ${foreignTableAlias}.${sql.identifier(foreignKeyAttributes[i].name)}`;
103
+ }), ') and (')})`;
104
+ const sqlSelectWhereKeysMatch = sql.query `select 1 from ${sqlIdentifier} as ${foreignTableAlias} where ${sqlKeysMatch}`;
105
+ return fieldValue === true
106
+ ? sql.query `exists(${sqlSelectWhereKeysMatch})`
107
+ : sql.query `not exists(${sqlSelectWhereKeysMatch})`;
108
+ };
109
+ for (const spec of forwardRelationSpecs) {
110
+ const { constraint, foreignTable, keyAttributes } = spec;
111
+ const fieldName = inflection.singleRelationByKeys(keyAttributes, foreignTable, table, constraint);
112
+ const filterFieldName = inflection.filterSingleRelationFieldName(fieldName);
113
+ const foreignTableTypeName = inflection.tableType(foreignTable);
114
+ const foreignTableFilterTypeName = inflection.filterType(foreignTableTypeName);
115
+ const ForeignTableFilterType = connectionFilterType(newWithHooks, foreignTableFilterTypeName, foreignTable, foreignTableTypeName);
116
+ if (!ForeignTableFilterType)
117
+ continue;
118
+ addField(filterFieldName, `Filter by the object’s \`${fieldName}\` relation.`, ForeignTableFilterType, resolve, spec, `Adding connection filter forward relation field from ${describePgEntity(table)} to ${describePgEntity(foreignTable)}`);
119
+ const keyIsNullable = !keyAttributes.every((attr) => attr.isNotNull);
120
+ if (keyIsNullable) {
121
+ const existsFieldName = inflection.filterForwardRelationExistsFieldName(fieldName);
122
+ addField(existsFieldName, `A related \`${fieldName}\` exists.`, GraphQLBoolean, resolveExists, spec, `Adding connection filter forward relation exists field from ${describePgEntity(table)} to ${describePgEntity(foreignTable)}`);
123
+ }
124
+ }
125
+ return fields;
126
+ });
127
+ };
128
+ export default PgConnectionArgFilterForwardRelationsPlugin;
@@ -0,0 +1,65 @@
1
+ const PgConnectionArgFilterLogicalOperatorsPlugin = (builder) => {
2
+ builder.hook('GraphQLInputObjectType:fields', (fields, build, context) => {
3
+ const { extend, graphql: { GraphQLList, GraphQLNonNull }, pgSql: sql, connectionFilterTypesByTypeName, connectionFilterResolve, connectionFilterRegisterResolver, } = build;
4
+ const { fieldWithHooks, scope: { isPgConnectionFilter }, Self, } = context;
5
+ if (!isPgConnectionFilter)
6
+ return fields;
7
+ connectionFilterTypesByTypeName[Self.name] = Self;
8
+ if (Object.keys(fields).length === 0) {
9
+ // Skip adding these operators if they would be the only fields
10
+ return fields;
11
+ }
12
+ const logicResolversByFieldName = {
13
+ and: (arr, sourceAlias, queryBuilder) => {
14
+ const sqlFragments = arr
15
+ .map((o) => connectionFilterResolve(o, sourceAlias, Self.name, queryBuilder))
16
+ .filter((x) => x != null);
17
+ return sqlFragments.length === 0
18
+ ? null
19
+ : sql.query `(${sql.join(sqlFragments, ') and (')})`;
20
+ },
21
+ or: (arr, sourceAlias, queryBuilder) => {
22
+ const sqlFragments = arr
23
+ .map((o) => connectionFilterResolve(o, sourceAlias, Self.name, queryBuilder))
24
+ .filter((x) => x != null);
25
+ return sqlFragments.length === 0
26
+ ? null
27
+ : sql.query `(${sql.join(sqlFragments, ') or (')})`;
28
+ },
29
+ not: (obj, sourceAlias, queryBuilder) => {
30
+ const sqlFragment = connectionFilterResolve(obj, sourceAlias, Self.name, queryBuilder);
31
+ return sqlFragment == null ? null : sql.query `not (${sqlFragment})`;
32
+ },
33
+ };
34
+ const logicalOperatorFields = {
35
+ and: fieldWithHooks('and', {
36
+ description: `Checks for all expressions in this list.`,
37
+ type: new GraphQLList(new GraphQLNonNull(Self)),
38
+ }, {
39
+ isPgConnectionFilterOperatorLogical: true,
40
+ }),
41
+ or: fieldWithHooks('or', {
42
+ description: `Checks for any expressions in this list.`,
43
+ type: new GraphQLList(new GraphQLNonNull(Self)),
44
+ }, {
45
+ isPgConnectionFilterOperatorLogical: true,
46
+ }),
47
+ not: fieldWithHooks('not', {
48
+ description: `Negates the expression.`,
49
+ type: Self,
50
+ }, {
51
+ isPgConnectionFilterOperatorLogical: true,
52
+ }),
53
+ };
54
+ const resolve = ({ sourceAlias, fieldName, fieldValue, queryBuilder, }) => {
55
+ if (fieldValue == null)
56
+ return null;
57
+ return logicResolversByFieldName[fieldName](fieldValue, sourceAlias, queryBuilder);
58
+ };
59
+ for (const fieldName of Object.keys(logicResolversByFieldName)) {
60
+ connectionFilterRegisterResolver(Self.name, fieldName, resolve);
61
+ }
62
+ return extend(fields, logicalOperatorFields);
63
+ });
64
+ };
65
+ export default PgConnectionArgFilterLogicalOperatorsPlugin;