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,130 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const PgConnectionArgFilterForwardRelationsPlugin = (builder) => {
4
+ builder.hook('inflection', (inflection) => ({
5
+ ...inflection,
6
+ filterForwardRelationExistsFieldName(relationFieldName) {
7
+ return `${relationFieldName}Exists`;
8
+ },
9
+ filterSingleRelationFieldName(fieldName) {
10
+ return fieldName;
11
+ },
12
+ }));
13
+ builder.hook('GraphQLInputObjectType:fields', (fields, build, context) => {
14
+ const { describePgEntity, extend, newWithHooks, inflection, graphql: { GraphQLBoolean }, pgOmit: omit, pgSql: sql, pgIntrospectionResultsByKind: introspectionResultsByKind, connectionFilterResolve, connectionFilterRegisterResolver, connectionFilterTypesByTypeName, connectionFilterType, } = build;
15
+ const { fieldWithHooks, scope: { pgIntrospection: table, isPgConnectionFilter }, Self, } = context;
16
+ if (!isPgConnectionFilter || table.kind !== 'class')
17
+ return fields;
18
+ connectionFilterTypesByTypeName[Self.name] = Self;
19
+ const forwardRelationSpecs = introspectionResultsByKind.constraint
20
+ .filter((con) => con.type === 'f')
21
+ .filter((con) => con.classId === table.id)
22
+ .reduce((memo, constraint) => {
23
+ if (omit(constraint, 'read') || omit(constraint, 'filter')) {
24
+ return memo;
25
+ }
26
+ const foreignTable = constraint.foreignClassId
27
+ ? introspectionResultsByKind.classById[constraint.foreignClassId]
28
+ : null;
29
+ if (!foreignTable) {
30
+ throw new Error(`Could not find the foreign table (constraint: ${constraint.name})`);
31
+ }
32
+ if (omit(foreignTable, 'read') || omit(foreignTable, 'filter')) {
33
+ return memo;
34
+ }
35
+ const attributes = introspectionResultsByKind.attribute
36
+ .filter((attr) => attr.classId === table.id)
37
+ .sort((a, b) => a.num - b.num);
38
+ const foreignAttributes = introspectionResultsByKind.attribute
39
+ .filter((attr) => attr.classId === foreignTable.id)
40
+ .sort((a, b) => a.num - b.num);
41
+ const keyAttributes = constraint.keyAttributeNums.map((num) => attributes.filter((attr) => attr.num === num)[0]);
42
+ const foreignKeyAttributes = constraint.foreignKeyAttributeNums.map((num) => foreignAttributes.filter((attr) => attr.num === num)[0]);
43
+ if (keyAttributes.some((attr) => omit(attr, 'read'))) {
44
+ return memo;
45
+ }
46
+ if (foreignKeyAttributes.some((attr) => omit(attr, 'read'))) {
47
+ return memo;
48
+ }
49
+ memo.push({
50
+ table,
51
+ keyAttributes,
52
+ foreignTable,
53
+ foreignKeyAttributes,
54
+ constraint,
55
+ });
56
+ return memo;
57
+ }, []);
58
+ let forwardRelationSpecByFieldName = {};
59
+ const addField = (fieldName, description, type, resolve, spec, hint) => {
60
+ // Field
61
+ fields = extend(fields, {
62
+ [fieldName]: fieldWithHooks(fieldName, {
63
+ description,
64
+ type,
65
+ }, {
66
+ isPgConnectionFilterField: true,
67
+ }),
68
+ }, hint);
69
+ // Spec for use in resolver
70
+ forwardRelationSpecByFieldName = extend(forwardRelationSpecByFieldName, {
71
+ [fieldName]: spec,
72
+ });
73
+ // Resolver
74
+ connectionFilterRegisterResolver(Self.name, fieldName, resolve);
75
+ };
76
+ const resolve = ({ sourceAlias, fieldName, fieldValue, queryBuilder, }) => {
77
+ if (fieldValue == null)
78
+ return null;
79
+ const { foreignTable, foreignKeyAttributes, keyAttributes } = forwardRelationSpecByFieldName[fieldName];
80
+ const foreignTableAlias = sql.identifier(Symbol());
81
+ const sqlIdentifier = sql.identifier(foreignTable.namespace.name, foreignTable.name);
82
+ const sqlKeysMatch = sql.query `(${sql.join(keyAttributes.map((key, i) => {
83
+ return sql.fragment `${sourceAlias}.${sql.identifier(key.name)} = ${foreignTableAlias}.${sql.identifier(foreignKeyAttributes[i].name)}`;
84
+ }), ') and (')})`;
85
+ const foreignTableTypeName = inflection.tableType(foreignTable);
86
+ const foreignTableFilterTypeName = inflection.filterType(foreignTableTypeName);
87
+ const sqlFragment = connectionFilterResolve(fieldValue, foreignTableAlias, foreignTableFilterTypeName, queryBuilder);
88
+ return sqlFragment == null
89
+ ? null
90
+ : sql.query `\
91
+ exists(
92
+ select 1 from ${sqlIdentifier} as ${foreignTableAlias}
93
+ where ${sqlKeysMatch} and
94
+ (${sqlFragment})
95
+ )`;
96
+ };
97
+ const resolveExists = ({ sourceAlias, fieldName, fieldValue, }) => {
98
+ if (fieldValue == null)
99
+ return null;
100
+ const { foreignTable, foreignKeyAttributes, keyAttributes } = forwardRelationSpecByFieldName[fieldName];
101
+ const foreignTableAlias = sql.identifier(Symbol());
102
+ const sqlIdentifier = sql.identifier(foreignTable.namespace.name, foreignTable.name);
103
+ const sqlKeysMatch = sql.query `(${sql.join(keyAttributes.map((key, i) => {
104
+ return sql.fragment `${sourceAlias}.${sql.identifier(key.name)} = ${foreignTableAlias}.${sql.identifier(foreignKeyAttributes[i].name)}`;
105
+ }), ') and (')})`;
106
+ const sqlSelectWhereKeysMatch = sql.query `select 1 from ${sqlIdentifier} as ${foreignTableAlias} where ${sqlKeysMatch}`;
107
+ return fieldValue === true
108
+ ? sql.query `exists(${sqlSelectWhereKeysMatch})`
109
+ : sql.query `not exists(${sqlSelectWhereKeysMatch})`;
110
+ };
111
+ for (const spec of forwardRelationSpecs) {
112
+ const { constraint, foreignTable, keyAttributes } = spec;
113
+ const fieldName = inflection.singleRelationByKeys(keyAttributes, foreignTable, table, constraint);
114
+ const filterFieldName = inflection.filterSingleRelationFieldName(fieldName);
115
+ const foreignTableTypeName = inflection.tableType(foreignTable);
116
+ const foreignTableFilterTypeName = inflection.filterType(foreignTableTypeName);
117
+ const ForeignTableFilterType = connectionFilterType(newWithHooks, foreignTableFilterTypeName, foreignTable, foreignTableTypeName);
118
+ if (!ForeignTableFilterType)
119
+ continue;
120
+ addField(filterFieldName, `Filter by the object’s \`${fieldName}\` relation.`, ForeignTableFilterType, resolve, spec, `Adding connection filter forward relation field from ${describePgEntity(table)} to ${describePgEntity(foreignTable)}`);
121
+ const keyIsNullable = !keyAttributes.every((attr) => attr.isNotNull);
122
+ if (keyIsNullable) {
123
+ const existsFieldName = inflection.filterForwardRelationExistsFieldName(fieldName);
124
+ addField(existsFieldName, `A related \`${fieldName}\` exists.`, GraphQLBoolean, resolveExists, spec, `Adding connection filter forward relation exists field from ${describePgEntity(table)} to ${describePgEntity(foreignTable)}`);
125
+ }
126
+ }
127
+ return fields;
128
+ });
129
+ };
130
+ exports.default = PgConnectionArgFilterForwardRelationsPlugin;
@@ -0,0 +1,3 @@
1
+ import type { Plugin } from 'graphile-build';
2
+ declare const PgConnectionArgFilterLogicalOperatorsPlugin: Plugin;
3
+ export default PgConnectionArgFilterLogicalOperatorsPlugin;
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const PgConnectionArgFilterLogicalOperatorsPlugin = (builder) => {
4
+ builder.hook('GraphQLInputObjectType:fields', (fields, build, context) => {
5
+ const { extend, graphql: { GraphQLList, GraphQLNonNull }, pgSql: sql, connectionFilterTypesByTypeName, connectionFilterResolve, connectionFilterRegisterResolver, } = build;
6
+ const { fieldWithHooks, scope: { isPgConnectionFilter }, Self, } = context;
7
+ if (!isPgConnectionFilter)
8
+ return fields;
9
+ connectionFilterTypesByTypeName[Self.name] = Self;
10
+ if (Object.keys(fields).length === 0) {
11
+ // Skip adding these operators if they would be the only fields
12
+ return fields;
13
+ }
14
+ const logicResolversByFieldName = {
15
+ and: (arr, sourceAlias, queryBuilder) => {
16
+ const sqlFragments = arr
17
+ .map((o) => connectionFilterResolve(o, sourceAlias, Self.name, queryBuilder))
18
+ .filter((x) => x != null);
19
+ return sqlFragments.length === 0
20
+ ? null
21
+ : sql.query `(${sql.join(sqlFragments, ') and (')})`;
22
+ },
23
+ or: (arr, sourceAlias, queryBuilder) => {
24
+ const sqlFragments = arr
25
+ .map((o) => connectionFilterResolve(o, sourceAlias, Self.name, queryBuilder))
26
+ .filter((x) => x != null);
27
+ return sqlFragments.length === 0
28
+ ? null
29
+ : sql.query `(${sql.join(sqlFragments, ') or (')})`;
30
+ },
31
+ not: (obj, sourceAlias, queryBuilder) => {
32
+ const sqlFragment = connectionFilterResolve(obj, sourceAlias, Self.name, queryBuilder);
33
+ return sqlFragment == null ? null : sql.query `not (${sqlFragment})`;
34
+ },
35
+ };
36
+ const logicalOperatorFields = {
37
+ and: fieldWithHooks('and', {
38
+ description: `Checks for all expressions in this list.`,
39
+ type: new GraphQLList(new GraphQLNonNull(Self)),
40
+ }, {
41
+ isPgConnectionFilterOperatorLogical: true,
42
+ }),
43
+ or: fieldWithHooks('or', {
44
+ description: `Checks for any expressions in this list.`,
45
+ type: new GraphQLList(new GraphQLNonNull(Self)),
46
+ }, {
47
+ isPgConnectionFilterOperatorLogical: true,
48
+ }),
49
+ not: fieldWithHooks('not', {
50
+ description: `Negates the expression.`,
51
+ type: Self,
52
+ }, {
53
+ isPgConnectionFilterOperatorLogical: true,
54
+ }),
55
+ };
56
+ const resolve = ({ sourceAlias, fieldName, fieldValue, queryBuilder, }) => {
57
+ if (fieldValue == null)
58
+ return null;
59
+ return logicResolversByFieldName[fieldName](fieldValue, sourceAlias, queryBuilder);
60
+ };
61
+ for (const fieldName of Object.keys(logicResolversByFieldName)) {
62
+ connectionFilterRegisterResolver(Self.name, fieldName, resolve);
63
+ }
64
+ return extend(fields, logicalOperatorFields);
65
+ });
66
+ };
67
+ exports.default = PgConnectionArgFilterLogicalOperatorsPlugin;
@@ -0,0 +1,15 @@
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
+ declare const PgConnectionArgFilterOperatorsPlugin: Plugin;
5
+ export interface OperatorSpec {
6
+ name?: string;
7
+ description: string;
8
+ resolveInput?: (input: unknown) => unknown;
9
+ resolveSql?: any;
10
+ resolveSqlIdentifier?: (sqlIdentifier: SQL, pgType: PgType, pgTypeModifier: number | null) => SQL;
11
+ resolveSqlValue?: (input: unknown, pgType: PgType, pgTypeModifier: number | null, resolveListItemSqlValue?: any) => SQL | null;
12
+ resolveType?: (fieldInputType: GraphQLInputType, rangeElementInputType: GraphQLInputType) => GraphQLType;
13
+ resolve: (sqlIdentifier: SQL, sqlValue: SQL, input: unknown, parentFieldName: string, queryBuilder: QueryBuilder) => SQL | null;
14
+ }
15
+ export default PgConnectionArgFilterOperatorsPlugin;