Skip to content

Commit 8f6c590

Browse files
authored
Add EXPORTABLE methods for schema export support (#202)
2 parents ce40268 + ff53836 commit 8f6c590

16 files changed

+4099
-1532
lines changed

.eslintrc.js

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,55 @@
11
module.exports = {
22
root: true,
3-
parser: "@typescript-eslint/parser",
3+
parser: "@babel/eslint-parser",
4+
parserOptions: {
5+
sourceType: "module",
6+
},
47
env: {
8+
jest: true,
59
node: true,
610
es6: true,
711
"jest/globals": true,
812
},
9-
plugins: ["@typescript-eslint", "jest"],
13+
plugins: [
14+
"@typescript-eslint",
15+
"jest",
16+
//"tsdoc",
17+
//"simple-import-sort",
18+
//"import",
19+
"graphile-export",
20+
],
1021
extends: [
1122
"eslint:recommended",
23+
"plugin:@typescript-eslint/eslint-recommended",
1224
"plugin:@typescript-eslint/recommended",
25+
// "plugin:import/errors",
26+
// "plugin:import/typescript",
27+
"plugin:graphile-export/recommended",
1328
"plugin:jest/recommended",
1429
"prettier",
1530
],
1631
rules: {
1732
"jest/expect-expect": ["off"],
33+
"no-fallthrough": ["error", { allowEmptyCase: true }],
34+
"@typescript-eslint/no-var-requires": ["off"],
35+
"@typescript-eslint/no-explicit-any": ["off"],
36+
// We need this for our `GraphileBuild`/`GraphileConfig`/etc namespaces
37+
"@typescript-eslint/no-namespace": "off",
38+
"@typescript-eslint/no-unused-vars": [
39+
"warn",
40+
{
41+
argsIgnorePattern: "^_",
42+
varsIgnorePattern: "^_",
43+
args: "after-used",
44+
ignoreRestSiblings: true,
45+
},
46+
],
1847
},
48+
overrides: [
49+
// Rules for TypeScript only
50+
{
51+
files: ["*.ts", "*.tsx"],
52+
parser: "@typescript-eslint/parser",
53+
},
54+
],
1955
};

.github/workflows/ci.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: Node CI
22

3-
on: push
3+
on: [push, pull_request]
44

55
jobs:
66
lint:
@@ -51,7 +51,6 @@ jobs:
5151
- 14
5252
- 15
5353
node-version:
54-
- 14.x
5554
- 16.x
5655
- 18.x
5756

__tests__/customOperatorsPlugin.ts

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,29 +11,72 @@ const CustomOperatorsPlugin: GraphileConfig.Plugin = {
1111
sql,
1212
graphql: { GraphQLInt, GraphQLBoolean },
1313
addConnectionFilterOperator,
14+
EXPORTABLE,
1415
} = build;
1516

1617
// simple
1718
addConnectionFilterOperator("InternetAddress", "familyEqualTo", {
1819
description: "Address family equal to specified value.",
19-
resolveInputCodec: () => TYPES.int,
20-
resolve: (i, v) => sql.fragment`family(${i}) = ${v}`,
20+
resolveInputCodec: EXPORTABLE(
21+
(TYPES) =>
22+
function () {
23+
return TYPES.int;
24+
},
25+
[TYPES]
26+
),
27+
resolve: EXPORTABLE(
28+
(sql) =>
29+
function (i, v) {
30+
return sql.fragment`family(${i}) = ${v}`;
31+
},
32+
[sql]
33+
),
2134
});
2235

2336
// using resolveSqlIdentifier
2437
addConnectionFilterOperator("InternetAddress", "familyNotEqualTo", {
2538
description: "Address family equal to specified value.",
26-
resolveInputCodec: () => TYPES.int,
27-
resolve: (i, v) => sql.fragment`${i} <> ${v}`,
28-
resolveSqlIdentifier: (i) => [sql.fragment`family(${i})`, TYPES.int],
39+
resolveInputCodec: EXPORTABLE(
40+
(TYPES) =>
41+
function () {
42+
return TYPES.int;
43+
},
44+
[TYPES]
45+
),
46+
resolve: EXPORTABLE(
47+
(sql) =>
48+
function (i, v) {
49+
return sql.fragment`${i} <> ${v}`;
50+
},
51+
[sql]
52+
),
53+
resolveSqlIdentifier: EXPORTABLE(
54+
(TYPES, sql) =>
55+
function (i) {
56+
return [sql.fragment`family(${i})`, TYPES.int];
57+
},
58+
[TYPES, sql]
59+
),
2960
});
3061

3162
// using resolveInput // typeNames: string | string[]
3263
addConnectionFilterOperator(["InternetAddress"], "isV4", {
3364
description: "Address family equal to specified value.",
34-
resolve: (i, v) => sql.fragment`family(${i}) = ${v}`,
65+
resolve: EXPORTABLE(
66+
(sql) =>
67+
function (i, v) {
68+
return sql.fragment`family(${i}) = ${v}`;
69+
},
70+
[sql]
71+
),
3572
resolveInput: (input) => (input === true ? 4 : 6),
36-
resolveInputCodec: () => TYPES.int,
73+
resolveInputCodec: EXPORTABLE(
74+
(TYPES) =>
75+
function () {
76+
return TYPES.int;
77+
},
78+
[TYPES]
79+
),
3780
resolveType: () => GraphQLBoolean,
3881
});
3982

__tests__/integration/queries.test.ts

Lines changed: 94 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,38 @@
11
import * as fs from "fs";
22
import * as path from "path";
33
import * as pg from "pg";
4+
45
import { promisify } from "util";
5-
import { ExecutionArgs, parse, validate } from "graphql";
6+
import {
7+
ExecutionArgs,
8+
GraphQLError,
9+
GraphQLSchema,
10+
parse,
11+
validate,
12+
} from "graphql";
613
import { withPgClient, withPgPool } from "../helpers";
714
import { PgConditionArgumentPlugin } from "graphile-build-pg";
815
import { postgraphilePresetAmber } from "postgraphile/presets/amber";
916
import { makeV4Preset, V4Options } from "postgraphile/presets/v4";
1017
import { makeSchema } from "postgraphile";
1118
import { PostGraphileConnectionFilterPreset } from "../../src/index";
1219
import CustomOperatorsPlugin from "./../customOperatorsPlugin";
13-
import { execute, hookArgs } from "grafast";
20+
import { execute, hookArgs, isSafeError } from "grafast";
1421
import { SchemaResult } from "graphile-build";
1522
import { makeWithPgClientViaPgClientAlreadyInTransaction } from "@dataplan/pg/adaptors/pg";
23+
import { exportSchemaAsString } from "graphile-export";
24+
import { importFromStringSync } from "module-from-string";
1625

1726
// TODO: remove this once Grafast gets it's planning under control :D
18-
jest.setTimeout(30000);
27+
jest.setTimeout(300000);
28+
29+
const vmEval = (code: string) => {
30+
const { schema } = importFromStringSync(code, {
31+
transformOptions: { loader: "js" },
32+
useCurrentGlobal: true,
33+
});
34+
return schema as GraphQLSchema;
35+
};
1936

2037
const createPostGraphileSchema = async (
2138
pool: pg.Pool,
@@ -44,7 +61,23 @@ const createPostGraphileSchema = async (
4461
],
4562
};
4663
const params = await makeSchema(preset);
47-
return params;
64+
if (process.env.TEST_EXPORTED_SCHEMA) {
65+
return {
66+
...params,
67+
schema: vmEval(
68+
(
69+
await exportSchemaAsString(params.schema, {
70+
mode: "graphql-js",
71+
// or:
72+
// mode: "typeDefs",
73+
modules: {},
74+
})
75+
).code
76+
),
77+
};
78+
} else {
79+
return params;
80+
}
4881
};
4982

5083
const readFile = promisify(fs.readFile);
@@ -91,36 +124,66 @@ beforeAll(async () => {
91124
nullAndEmptyAllowed,
92125
addConnectionFilterOperator,
93126
] = await Promise.all([
94-
createPostGraphileSchema(pool, ["p"], {
95-
skipPlugins: [PgConditionArgumentPlugin],
96-
}),
97-
createPostGraphileSchema(pool, ["p"], {
98-
skipPlugins: [PgConditionArgumentPlugin],
99-
dynamicJson: true,
100-
}),
101-
createPostGraphileSchema(pool, ["p"], {
102-
skipPlugins: [PgConditionArgumentPlugin],
103-
graphileBuildOptions: {
104-
pgUseCustomNetworkScalars: true,
127+
createPostGraphileSchema(
128+
pool,
129+
["p"],
130+
{
131+
skipPlugins: [PgConditionArgumentPlugin],
132+
},
133+
{}
134+
),
135+
createPostGraphileSchema(
136+
pool,
137+
["p"],
138+
{
139+
skipPlugins: [PgConditionArgumentPlugin],
140+
dynamicJson: true,
141+
},
142+
{}
143+
),
144+
createPostGraphileSchema(
145+
pool,
146+
["p"],
147+
{
148+
skipPlugins: [PgConditionArgumentPlugin],
149+
graphileBuildOptions: {
150+
pgUseCustomNetworkScalars: true,
151+
},
152+
},
153+
{}
154+
),
155+
createPostGraphileSchema(
156+
pool,
157+
["p"],
158+
{
159+
skipPlugins: [PgConditionArgumentPlugin],
160+
graphileBuildOptions: {
161+
connectionFilterRelations: true,
162+
},
105163
},
106-
}),
107-
createPostGraphileSchema(pool, ["p"], {
108-
skipPlugins: [PgConditionArgumentPlugin],
109-
graphileBuildOptions: {
110-
connectionFilterRelations: true,
164+
{}
165+
),
166+
createPostGraphileSchema(
167+
pool,
168+
["p"],
169+
{
170+
skipPlugins: [PgConditionArgumentPlugin],
171+
simpleCollections: "only",
111172
},
112-
}),
113-
createPostGraphileSchema(pool, ["p"], {
114-
skipPlugins: [PgConditionArgumentPlugin],
115-
simpleCollections: "only",
116-
}),
117-
createPostGraphileSchema(pool, ["p"], {
118-
skipPlugins: [PgConditionArgumentPlugin],
119-
graphileBuildOptions: {
120-
connectionFilterAllowNullInput: true,
121-
connectionFilterAllowEmptyObjectInput: true,
173+
{}
174+
),
175+
createPostGraphileSchema(
176+
pool,
177+
["p"],
178+
{
179+
skipPlugins: [PgConditionArgumentPlugin],
180+
graphileBuildOptions: {
181+
connectionFilterAllowNullInput: true,
182+
connectionFilterAllowEmptyObjectInput: true,
183+
},
122184
},
123-
}),
185+
{}
186+
),
124187
createPostGraphileSchema(
125188
pool,
126189
["p"],
@@ -173,7 +236,6 @@ for (const queryFileName of queryFileNames) {
173236
queryFileName in gqlSchemaByQueryFileName
174237
? gqlSchemaByQueryFileName[queryFileName]
175238
: gqlSchemas.normal;
176-
177239
const document = parse(query);
178240
const errors = validate(schema, document);
179241
if (errors.length > 0) {

babel.config.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Babel config, used by Jest
2+
module.exports = {
3+
plugins: ["@babel/plugin-transform-modules-commonjs"],
4+
presets: [
5+
[
6+
"@babel/env",
7+
{
8+
targets: {
9+
node: "16.12",
10+
},
11+
},
12+
],
13+
"@babel/preset-typescript",
14+
],
15+
env: {
16+
test: {
17+
plugins: ["babel-plugin-transform-import-meta"],
18+
presets: [
19+
[
20+
"@babel/env",
21+
{
22+
targets: {
23+
node: "current",
24+
},
25+
},
26+
],
27+
"@babel/preset-typescript",
28+
],
29+
},
30+
},
31+
};

0 commit comments

Comments
 (0)