Skip to content

Commit 6189b44

Browse files
author
mattia rossi
committed
Add graphile-export methods to allow correct export of a schema using the plugin
1 parent ce40268 commit 6189b44

7 files changed

Lines changed: 303 additions & 128 deletions

.eslintrc.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@ module.exports = {
66
es6: true,
77
"jest/globals": true,
88
},
9-
plugins: ["@typescript-eslint", "jest"],
9+
plugins: ["@typescript-eslint", "jest", "graphile-export"],
1010
extends: [
1111
"eslint:recommended",
1212
"plugin:@typescript-eslint/recommended",
13+
"plugin:graphile-export/recommended",
1314
"plugin:jest/recommended",
1415
"prettier",
1516
],

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
"url": "https://github.com/graphile-contrib/postgraphile-plugin-connection-filter/issues"
2626
},
2727
"dependencies": {
28+
"eslint-plugin-graphile-export": "^0.0.2-beta.3",
29+
"graphile-export": "^0.0.2-beta.4",
2830
"tslib": "^2.5.0"
2931
},
3032
"devDependencies": {

src/PgConnectionArgFilterForwardRelationsPlugin.ts

Lines changed: 44 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -112,24 +112,28 @@ export const PgConnectionArgFilterForwardRelationsPlugin: GraphileConfig.Plugin
112112
() => ({
113113
description: `Filter by the object’s \`${fieldName}\` relation.`,
114114
type: ForeignTableFilterType,
115-
applyPlan($where: PgConditionStep<any>, fieldArgs) {
116-
assertAllowed(fieldArgs, "object");
117-
const $subQuery = $where.existsPlan({
118-
tableExpression: foreignTableExpression,
119-
alias: foreignTable.name,
120-
});
121-
localAttributes.forEach((localAttribute, i) => {
122-
const remoteAttribute = remoteAttributes[i];
123-
$subQuery.where(
124-
sql`${$where.alias}.${sql.identifier(
125-
localAttribute as string
126-
)} = ${$subQuery.alias}.${sql.identifier(
127-
remoteAttribute as string
128-
)}`
129-
);
130-
});
131-
fieldArgs.apply($subQuery);
132-
},
115+
applyPlan: build.EXPORTABLE(
116+
() =>
117+
function ($where: PgConditionStep<any>, fieldArgs) {
118+
//assertAllowed(fieldArgs, "object");
119+
const $subQuery = $where.existsPlan({
120+
tableExpression: foreignTableExpression,
121+
alias: foreignTable.name,
122+
});
123+
localAttributes.forEach((localAttribute, i) => {
124+
const remoteAttribute = remoteAttributes[i];
125+
$subQuery.where(
126+
sql`${$where.alias}.${sql.identifier(
127+
localAttribute as string
128+
)} = ${$subQuery.alias}.${sql.identifier(
129+
remoteAttribute as string
130+
)}`
131+
);
132+
});
133+
fieldArgs.apply($subQuery);
134+
},
135+
[]
136+
),
133137
})
134138
),
135139
},
@@ -154,24 +158,28 @@ export const PgConnectionArgFilterForwardRelationsPlugin: GraphileConfig.Plugin
154158
() => ({
155159
description: `A related \`${fieldName}\` exists.`,
156160
type: GraphQLBoolean,
157-
applyPlan($where: PgConditionStep<any>, fieldArgs) {
158-
assertAllowed(fieldArgs, "scalar");
159-
const $subQuery = $where.existsPlan({
160-
tableExpression: foreignTableExpression,
161-
alias: foreignTable.name,
162-
$equals: fieldArgs.get(),
163-
});
164-
localAttributes.forEach((localAttribute, i) => {
165-
const remoteAttribute = remoteAttributes[i];
166-
$subQuery.where(
167-
sql`${$where.alias}.${sql.identifier(
168-
localAttribute as string
169-
)} = ${$subQuery.alias}.${sql.identifier(
170-
remoteAttribute as string
171-
)}`
172-
);
173-
});
174-
},
161+
applyPlan: build.EXPORTABLE(
162+
() =>
163+
function ($where: PgConditionStep<any>, fieldArgs) {
164+
//assertAllowed(fieldArgs, "scalar");
165+
const $subQuery = $where.existsPlan({
166+
tableExpression: foreignTableExpression,
167+
alias: foreignTable.name,
168+
$equals: fieldArgs.get(),
169+
});
170+
localAttributes.forEach((localAttribute, i) => {
171+
const remoteAttribute = remoteAttributes[i];
172+
$subQuery.where(
173+
sql`${$where.alias}.${sql.identifier(
174+
localAttribute as string
175+
)} = ${$subQuery.alias}.${sql.identifier(
176+
remoteAttribute as string
177+
)}`
178+
);
179+
});
180+
},
181+
[]
182+
),
175183
})
176184
),
177185
},

src/PgConnectionArgFilterLogicalOperatorsPlugin.ts

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,17 @@ export const PgConnectionArgFilterLogicalOperatorsPlugin: GraphileConfig.Plugin
3939
{
4040
description: `Checks for all expressions in this list.`,
4141
type: new GraphQLList(new GraphQLNonNull(Self)),
42-
applyPlan($where: PgConditionStep<any>, fieldArgs) {
43-
assertAllowed(fieldArgs, "list");
44-
const $and = $where.andPlan();
45-
// No need for this more correct form, easier to read if it's flatter.
46-
// fieldArgs.apply(() => $and.andPlan());
47-
fieldArgs.apply($and);
48-
},
42+
applyPlan: build.EXPORTABLE(
43+
() =>
44+
function ($where: PgConditionStep<any>, fieldArgs) {
45+
assertAllowed(fieldArgs, "list");
46+
const $and = $where.andPlan();
47+
// No need for this more correct form, easier to read if it's flatter.
48+
// fieldArgs.apply(() => $and.andPlan());
49+
fieldArgs.apply($and);
50+
},
51+
[]
52+
),
4953
}
5054
),
5155
or: fieldWithHooks(
@@ -56,12 +60,16 @@ export const PgConnectionArgFilterLogicalOperatorsPlugin: GraphileConfig.Plugin
5660
{
5761
description: `Checks for any expressions in this list.`,
5862
type: new GraphQLList(new GraphQLNonNull(Self)),
59-
applyPlan($where: PgConditionStep<any>, fieldArgs) {
60-
assertAllowed(fieldArgs, "list");
61-
const $or = $where.orPlan();
62-
// Every entry is added to the `$or`, but the entries themselves should use an `and`.
63-
fieldArgs.apply(() => $or.andPlan());
64-
},
63+
applyPlan: build.EXPORTABLE(
64+
() =>
65+
function ($where: PgConditionStep<any>, fieldArgs) {
66+
assertAllowed(fieldArgs, "list");
67+
const $or = $where.orPlan();
68+
// Every entry is added to the `$or`, but the entries themselves should use an `and`.
69+
fieldArgs.apply(() => $or.andPlan());
70+
},
71+
[]
72+
),
6573
}
6674
),
6775
not: fieldWithHooks(
@@ -72,12 +80,16 @@ export const PgConnectionArgFilterLogicalOperatorsPlugin: GraphileConfig.Plugin
7280
{
7381
description: `Negates the expression.`,
7482
type: Self,
75-
applyPlan($where: PgConditionStep<any>, fieldArgs) {
76-
assertAllowed(fieldArgs, "object");
77-
const $not = $where.notPlan();
78-
const $and = $not.andPlan();
79-
fieldArgs.apply($and);
80-
},
83+
applyPlan: build.EXPORTABLE(
84+
() =>
85+
function ($where: PgConditionStep<any>, fieldArgs) {
86+
assertAllowed(fieldArgs, "object");
87+
const $not = $where.notPlan();
88+
const $and = $not.andPlan();
89+
fieldArgs.apply($and);
90+
},
91+
[]
92+
),
8193
}
8294
),
8395
};

src/PgConnectionArgFilterPlugin.ts

Lines changed: 30 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import type {
77
} from "graphql";
88
import { OperatorsCategory } from "./interfaces";
99

10-
const { version } = require("../package.json");
10+
const { version } = require("../package.json"); // eslint-disable-line
1111

1212
type AnyCodec = PgCodec<any, any, any, any, any, any, any>;
1313

@@ -444,33 +444,37 @@ export const PgConnectionArgFilterPlugin: GraphileConfig.Plugin = {
444444
return args;
445445
}
446446

447-
const assertAllowed = (fieldArgs: FieldArgs) => {
448-
const $raw = fieldArgs.getRaw();
449-
if (
450-
!connectionFilterAllowEmptyObjectInput &&
451-
"evalIsEmpty" in $raw &&
452-
$raw.evalIsEmpty()
453-
) {
454-
throw Object.assign(
455-
new Error(
456-
"Empty objects are forbidden in filter argument input."
457-
),
458-
{
459-
//TODO: mark this error as safe
447+
const assertAllowed = build.EXPORTABLE(
448+
() =>
449+
function (fieldArgs: FieldArgs) {
450+
const $raw = fieldArgs.getRaw();
451+
if (
452+
!connectionFilterAllowEmptyObjectInput &&
453+
"evalIsEmpty" in $raw &&
454+
$raw.evalIsEmpty()
455+
) {
456+
throw Object.assign(
457+
new Error(
458+
"Empty objects are forbidden in filter argument input."
459+
),
460+
{
461+
//TODO: mark this error as safe
462+
}
463+
);
460464
}
461-
);
462-
}
463-
if (!connectionFilterAllowNullInput && $raw.evalIs(null)) {
464-
throw Object.assign(
465-
new Error(
466-
"Null literals are forbidden in filter argument input."
467-
),
468-
{
469-
//TODO: mark this error as safe
465+
if (!connectionFilterAllowNullInput && $raw.evalIs(null)) {
466+
throw Object.assign(
467+
new Error(
468+
"Null literals are forbidden in filter argument input."
469+
),
470+
{
471+
//TODO: mark this error as safe
472+
}
473+
);
470474
}
471-
);
472-
}
473-
};
475+
},
476+
[]
477+
);
474478

475479
const attributeCodec =
476480
resource?.parameters && !resource?.codec.attributes

src/utils.ts

Lines changed: 47 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import type {
77
import type { FieldArgs } from "grafast";
88
import type { GraphileBuild } from "graphile-build";
99
import type {} from "graphile-build-pg";
10+
import { EXPORTABLE } from "graphile-export";
1011

1112
export function isComputedScalarAttributeResource(
1213
s: PgResource<any, any, any, any, any>
@@ -61,55 +62,56 @@ export function makeAssertAllowed(options: GraphileBuild.SchemaOptions) {
6162
connectionFilterAllowNullInput,
6263
connectionFilterAllowEmptyObjectInput,
6364
} = options;
64-
const assertAllowed = (
65-
fieldArgs: FieldArgs,
66-
mode: "list" | "object" | "scalar"
67-
) => {
68-
const $raw = fieldArgs.getRaw();
69-
if (
70-
mode === "object" &&
71-
!connectionFilterAllowEmptyObjectInput &&
72-
"evalIsEmpty" in $raw &&
73-
$raw.evalIsEmpty()
74-
) {
75-
throw Object.assign(
76-
new Error("Empty objects are forbidden in filter argument input."),
77-
{
78-
//TODO: mark this error as safe
65+
const assertAllowed = EXPORTABLE(
66+
(connectionFilterAllowEmptyObjectInput, connectionFilterAllowNullInput) =>
67+
function (fieldArgs: FieldArgs, mode: "list" | "object" | "scalar") {
68+
const $raw = fieldArgs.getRaw();
69+
if (
70+
mode === "object" &&
71+
!connectionFilterAllowEmptyObjectInput &&
72+
"evalIsEmpty" in $raw &&
73+
$raw.evalIsEmpty()
74+
) {
75+
throw Object.assign(
76+
new Error("Empty objects are forbidden in filter argument input."),
77+
{
78+
//TODO: mark this error as safe
79+
}
80+
);
7981
}
80-
);
81-
}
82-
if (
83-
mode === "list" &&
84-
!connectionFilterAllowEmptyObjectInput &&
85-
"evalLength" in $raw
86-
) {
87-
const l = $raw.evalLength();
88-
if (l != null) {
89-
for (let i = 0; i < l; i++) {
90-
const $entry = $raw.at(i);
91-
if ("evalIsEmpty" in $entry && $entry.evalIsEmpty()) {
92-
throw Object.assign(
93-
new Error(
94-
"Empty objects are forbidden in filter argument input."
95-
),
96-
{
97-
//TODO: mark this error as safe
82+
if (
83+
mode === "list" &&
84+
!connectionFilterAllowEmptyObjectInput &&
85+
"evalLength" in $raw
86+
) {
87+
const l = $raw.evalLength();
88+
if (l != null) {
89+
for (let i = 0; i < l; i++) {
90+
const $entry = $raw.at(i);
91+
if ("evalIsEmpty" in $entry && $entry.evalIsEmpty()) {
92+
throw Object.assign(
93+
new Error(
94+
"Empty objects are forbidden in filter argument input."
95+
),
96+
{
97+
//TODO: mark this error as safe
98+
}
99+
);
98100
}
99-
);
101+
}
100102
}
101103
}
102-
}
103-
}
104-
// For all modes, check null
105-
if (!connectionFilterAllowNullInput && $raw.evalIs(null)) {
106-
throw Object.assign(
107-
new Error("Null literals are forbidden in filter argument input."),
108-
{
109-
//TODO: mark this error as safe
104+
// For all modes, check null
105+
if (!connectionFilterAllowNullInput && $raw.evalIs(null)) {
106+
throw Object.assign(
107+
new Error("Null literals are forbidden in filter argument input."),
108+
{
109+
//TODO: mark this error as safe
110+
}
111+
);
110112
}
111-
);
112-
}
113-
};
113+
},
114+
[connectionFilterAllowEmptyObjectInput, connectionFilterAllowNullInput]
115+
);
114116
return assertAllowed;
115117
}

0 commit comments

Comments
 (0)