Skip to content

Commit f4194a7

Browse files
committed
Refactor to allow operator name reuse across types
1 parent d8723fe commit f4194a7

File tree

2 files changed

+112
-47
lines changed

2 files changed

+112
-47
lines changed

src/ConnectionArgFilterPlugin.js

Lines changed: 89 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -7,51 +7,103 @@ module.exports = function ConnectionArgFilterPlugin(
77
} = {}
88
) {
99
builder.hook("build", build => {
10-
const connectionFilterOperators = {};
11-
return build.extend(build, {
12-
connectionFilterOperators,
13-
addConnectionFilterOperator(
14-
defaultName,
15-
description,
16-
resolveType,
17-
resolveWhereClause,
18-
options = {}
10+
const connectionFilterOperatorsByFieldType = {};
11+
const connectionFilterOperatorsGlobal = {};
12+
13+
const addConnectionFilterOperator = (
14+
defaultName,
15+
description,
16+
resolveType,
17+
resolveWhereClause,
18+
options = {}
19+
) => {
20+
if (!defaultName) {
21+
throw new Error(
22+
`Missing argument 'defaultName' in call to 'addConnectionFilterOperator'`
23+
);
24+
}
25+
if (!resolveType) {
26+
throw new Error(
27+
`Missing argument 'resolveType' for filter operator '${defaultName}'`
28+
);
29+
}
30+
if (!resolveWhereClause) {
31+
throw new Error(
32+
`Missing argument 'resolveWhereClause' for filter operator '${defaultName}'`
33+
);
34+
}
35+
36+
// If `connectionFilterOperatorNames` is specified, override the operator name
37+
const operatorName =
38+
connectionFilterOperatorNames[defaultName] || defaultName;
39+
40+
// If `connectionFilterAllowedOperators` is specified and this operator isn't included, skip it
41+
if (
42+
connectionFilterAllowedOperators &&
43+
!connectionFilterAllowedOperators.includes(operatorName)
1944
) {
20-
if (!defaultName) {
21-
throw new Error("No filter operator defaultName specified");
22-
}
23-
const name = connectionFilterOperatorNames[defaultName] || defaultName;
24-
if (connectionFilterOperators[name]) {
25-
throw new Error(
26-
"There is already a filter operator with the name '" + name + "'"
27-
);
28-
}
29-
if (!resolveType) {
30-
throw new Error(
31-
"No filter operator type specified for '" + name + "'"
32-
);
33-
}
34-
if (!resolveWhereClause) {
35-
throw new Error(
36-
"No filter operator where clause resolver specified for '" +
37-
name +
38-
"'"
39-
);
40-
}
41-
if (
42-
!connectionFilterAllowedOperators ||
43-
connectionFilterAllowedOperators.includes(defaultName)
44-
) {
45-
connectionFilterOperators[name] = {
46-
name,
45+
return;
46+
}
47+
48+
if (connectionFilterOperatorsGlobal[operatorName]) {
49+
throw new Error(
50+
`Failed to register filter operator '${operatorName}' because this operator is already registered globally`
51+
);
52+
}
53+
54+
if (options.allowedFieldTypes) {
55+
// Operator has an `allowedFieldTypes` whitelist
56+
for (const fieldTypeName of options.allowedFieldTypes) {
57+
// Ensure operator name hasn't already been registered for this field type
58+
if (
59+
connectionFilterOperatorsByFieldType[fieldTypeName] &&
60+
connectionFilterOperatorsByFieldType[fieldTypeName][operatorName]
61+
) {
62+
throw new Error(
63+
`Failed to register filter operator '${operatorName}' because this operator is already registered for type '${fieldTypeName}'`
64+
);
65+
}
66+
67+
// Register operator for this field type
68+
if (!connectionFilterOperatorsByFieldType[fieldTypeName]) {
69+
connectionFilterOperatorsByFieldType[fieldTypeName] = {};
70+
}
71+
connectionFilterOperatorsByFieldType[fieldTypeName][operatorName] = {
4772
description,
4873
resolveType,
4974
resolveWhereClause,
5075
options,
5176
};
5277
}
53-
},
78+
} else {
79+
// Operator does not have an `allowedFieldTypes` whitelist
80+
81+
// Ensure operator name hasn't been registered for any field type
82+
Object.entries(connectionFilterOperatorsByFieldType).forEach(
83+
([fieldTypeName, operators]) => {
84+
if (Object.keys(operators).includes(operatorName)) {
85+
throw new Error(
86+
`Failed to register global filter operator '${operatorName}' because this operator is already registered for type '${fieldTypeName}'`
87+
);
88+
}
89+
}
90+
);
91+
92+
// Register operator globally
93+
connectionFilterOperatorsGlobal[operatorName] = {
94+
description,
95+
resolveType,
96+
resolveWhereClause,
97+
options,
98+
};
99+
}
100+
};
101+
102+
return build.extend(build, {
103+
addConnectionFilterOperator,
54104
connectionFilterAllowedFieldTypes,
105+
connectionFilterOperatorsByFieldType,
106+
connectionFilterOperatorsGlobal,
55107
});
56108
});
57109

src/PgConnectionArgFilterPlugin.js

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -216,11 +216,14 @@ module.exports = function PgConnectionArgFilterPlugin(
216216
GraphQLList,
217217
GraphQLScalarType,
218218
GraphQLEnumType,
219+
GraphQLString,
219220
},
220221
inflection,
222+
pgGetGqlInputTypeByTypeIdAndModifier,
221223
pgSql: sql,
222224
connectionFilterAllowedFieldTypes,
223-
connectionFilterOperators,
225+
connectionFilterOperatorsByFieldType,
226+
connectionFilterOperatorsGlobal,
224227
} = build;
225228

226229
const getOrCreateFieldFilterTypeFromFieldType = (
@@ -246,19 +249,19 @@ module.exports = function PgConnectionArgFilterPlugin(
246249
} fields. All fields are combined with a logical ‘and.’`,
247250
fields: context => {
248251
const { fieldWithHooks } = context;
249-
return Object.keys(connectionFilterOperators).reduce(
250-
(memo, operatorName) => {
251-
const operator = connectionFilterOperators[operatorName];
252-
const allowedFieldTypes = operator.options.allowedFieldTypes;
253-
const fieldTypeIsAllowed =
254-
!allowedFieldTypes ||
255-
allowedFieldTypes.includes(namedTypeName);
252+
const operators = Object.assign(
253+
{},
254+
connectionFilterOperatorsGlobal,
255+
connectionFilterOperatorsByFieldType[namedTypeName]
256+
);
257+
return Object.entries(operators).reduce(
258+
(memo, [operatorName, operator]) => {
256259
const allowedListTypes = operator.options
257260
.allowedListTypes || ["NonList"];
258261
const listTypeIsAllowed = isListType
259262
? allowedListTypes.includes("List")
260263
: allowedListTypes.includes("NonList");
261-
if (fieldTypeIsAllowed && listTypeIsAllowed) {
264+
if (listTypeIsAllowed) {
262265
memo[operatorName] = fieldWithHooks(operatorName, {
263266
description: operator.description,
264267
type: operator.resolveType(fieldType),
@@ -355,7 +358,17 @@ module.exports = function PgConnectionArgFilterPlugin(
355358
pgType,
356359
pgTypeModifier
357360
) => {
358-
const operator = connectionFilterOperators[operatorName];
361+
const fieldType = getNamedType(
362+
pgGetGqlInputTypeByTypeIdAndModifier(pgType.id, pgTypeModifier) ||
363+
GraphQLString
364+
);
365+
const operator =
366+
connectionFilterOperatorsGlobal[operatorName] ||
367+
(connectionFilterOperatorsByFieldType[fieldType.name] &&
368+
connectionFilterOperatorsByFieldType[fieldType.name][operatorName]);
369+
if (!operator) {
370+
throw new Error(`Unable to resolve operator '${operatorName}'`);
371+
}
359372
const inputResolver = operator.options.inputResolver;
360373
const val = operator.options.resolveWithRawInput
361374
? input

0 commit comments

Comments
 (0)