Skip to content

Commit 662acc3

Browse files
authored
Fix for loss of scalar and field level resolvers (#297)
* wrapCustomResolvers removed in favour of schema level resolver auth injection * Add test cases for this fix
1 parent 6fdaeda commit 662acc3

File tree

8 files changed

+311
-150
lines changed

8 files changed

+311
-150
lines changed

packages/graphql/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
"rimraf": "3.0.2",
5757
"semver": "7.3.5",
5858
"ts-jest": "26.1.4",
59+
"ts-node": "^10.0.0",
5960
"typescript": "3.9.7"
6061
},
6162
"dependencies": {

packages/graphql/src/classes/Neo4jGraphQL.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,15 @@
2020
import Debug from "debug";
2121
import { Driver } from "neo4j-driver";
2222
import { DocumentNode, GraphQLResolveInfo, GraphQLSchema, parse, printSchema, print } from "graphql";
23-
import { addSchemaLevelResolver, IExecutableSchemaDefinition } from "@graphql-tools/schema";
23+
import { addResolversToSchema, addSchemaLevelResolver, IExecutableSchemaDefinition } from "@graphql-tools/schema";
2424
import type { DriverConfig } from "../types";
2525
import { makeAugmentedSchema } from "../schema";
2626
import Node from "./Node";
2727
import { checkNeo4jCompat } from "../utils";
2828
import { getJWT } from "../auth/index";
2929
import { DEBUG_GRAPHQL } from "../constants";
3030
import getNeo4jResolveTree from "../utils/get-neo4j-resolve-tree";
31+
import createAuthParam from "../translate/create-auth-param";
3132

3233
const debug = Debug(DEBUG_GRAPHQL);
3334

@@ -60,13 +61,26 @@ class Neo4jGraphQL {
6061
public config?: Neo4jGraphQLConfig;
6162

6263
constructor(input: Neo4jGraphQLConstructor) {
63-
const { config = {}, driver, ...schemaDefinition } = input;
64+
const { config = {}, driver, resolvers, ...schemaDefinition } = input;
6465
const { nodes, schema } = makeAugmentedSchema(schemaDefinition, { enableRegex: config.enableRegex });
6566

6667
this.driver = driver;
6768
this.config = config;
6869
this.nodes = nodes;
69-
this.schema = this.createWrappedSchema({ schema, config });
70+
this.schema = schema;
71+
/*
72+
addResolversToSchema must be first, so that custom resolvers also get schema level resolvers
73+
*/
74+
if (resolvers) {
75+
if (Array.isArray(resolvers)) {
76+
resolvers.forEach((r) => {
77+
this.schema = addResolversToSchema(this.schema, r);
78+
});
79+
} else {
80+
this.schema = addResolversToSchema(this.schema, resolvers);
81+
}
82+
}
83+
this.schema = this.createWrappedSchema({ schema: this.schema, config });
7084
this.document = parse(printSchema(schema));
7185
}
7286

@@ -118,6 +132,8 @@ class Neo4jGraphQL {
118132
context.resolveTree = getNeo4jResolveTree(resolveInfo);
119133

120134
context.jwt = getJWT(context);
135+
136+
context.auth = createAuthParam({ context });
121137
});
122138
}
123139

packages/graphql/src/schema/make-augmented-schema.ts

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,14 @@ import { findResolver, createResolver, deleteResolver, cypherResolver, updateRes
4848
import checkNodeImplementsInterfaces from "./check-node-implements-interfaces";
4949
import * as Scalars from "./scalars";
5050
import parseExcludeDirective from "./parse-exclude-directive";
51-
import wrapCustomResolvers from "./wrap-custom-resolvers";
5251
import getCustomResolvers from "./get-custom-resolvers";
5352
import getObjFieldMeta from "./get-obj-field-meta";
5453
import * as point from "./point";
5554
import { graphqlDirectivesToCompose, objectFieldsToComposeFields } from "./to-compose";
5655
// import validateTypeDefs from "./validation";
5756

5857
function makeAugmentedSchema(
59-
{ typeDefs, resolvers, ...schemaDefinition }: IExecutableSchemaDefinition,
58+
{ typeDefs, ...schemaDefinition }: IExecutableSchemaDefinition,
6059
{ enableRegex }: { enableRegex?: boolean } = {}
6160
): { schema: GraphQLSchema; nodes: Node[] } {
6261
const document = mergeTypeDefs(Array.isArray(typeDefs) ? (typeDefs as string[]) : [typeDefs as string]);
@@ -175,8 +174,6 @@ function makeAugmentedSchema(
175174
return node;
176175
});
177176

178-
const nodeNames = nodes.map((x) => x.name);
179-
180177
nodes.forEach((node) => {
181178
const nodeFields = objectFieldsToComposeFields([
182179
...node.primitiveFields,
@@ -741,7 +738,7 @@ function makeAugmentedSchema(
741738
}
742739

743740
const generatedTypeDefs = composer.toSDL();
744-
let generatedResolvers: any = {
741+
const generatedResolvers = {
745742
...composer.getResolveMethods(),
746743
...Object.entries(Scalars).reduce((res, [name, scalar]) => {
747744
if (generatedTypeDefs.includes(`scalar ${name}\n`)) {
@@ -751,16 +748,7 @@ function makeAugmentedSchema(
751748
}, {}),
752749
};
753750

754-
if (resolvers) {
755-
generatedResolvers = wrapCustomResolvers({
756-
generatedResolvers,
757-
nodeNames,
758-
resolvers,
759-
});
760-
}
761-
762751
unions.forEach((union) => {
763-
// eslint-disable-next-line no-underscore-dangle
764752
if (!generatedResolvers[union.name.value]) {
765753
generatedResolvers[union.name.value] = { __resolveType: (root) => root.__resolveType };
766754
}

packages/graphql/src/schema/wrap-custom-resolvers.ts

Lines changed: 0 additions & 132 deletions
This file was deleted.
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/*
2+
* Copyright (c) "Neo4j"
3+
* Neo4j Sweden AB [http://neo4j.com]
4+
*
5+
* This file is part of Neo4j.
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
20+
import { Driver } from "neo4j-driver";
21+
import { graphql } from "graphql";
22+
import { gql } from "apollo-server";
23+
import neo4j from "../neo4j";
24+
import { Neo4jGraphQL } from "../../../src/classes";
25+
26+
describe("https://github.com/neo4j/graphql/issues/207", () => {
27+
let driver: Driver;
28+
const typeDefs = gql`
29+
union Result = Book | Author
30+
31+
type Book {
32+
title: String
33+
}
34+
35+
type Author {
36+
name: String
37+
}
38+
39+
type Query {
40+
search: [Result]
41+
}
42+
`;
43+
const resolvers = {
44+
Result: {
45+
__resolveType(obj) {
46+
if (obj.name) {
47+
return "Author";
48+
}
49+
if (obj.title) {
50+
return "Book";
51+
}
52+
return null; // GraphQLError is thrown
53+
},
54+
},
55+
Query: {
56+
search: () => [{ title: "GraphQL Unions for Dummies" }, { name: "Darrell" }],
57+
},
58+
};
59+
60+
beforeAll(async () => {
61+
driver = await neo4j();
62+
});
63+
64+
afterAll(async () => {
65+
await driver.close();
66+
});
67+
68+
test("__resolveType resolvers are correctly evaluated", async () => {
69+
const session = driver.session();
70+
71+
const neoSchema = new Neo4jGraphQL({ typeDefs, resolvers, driver });
72+
73+
const mutation = `
74+
query GetSearchResults {
75+
search {
76+
__typename
77+
... on Book {
78+
title
79+
}
80+
... on Author {
81+
name
82+
}
83+
}
84+
}
85+
`;
86+
87+
try {
88+
await neoSchema.checkNeo4jCompat();
89+
90+
const result = await graphql({
91+
schema: neoSchema.schema,
92+
source: mutation,
93+
contextValue: { driver },
94+
});
95+
96+
expect(result.errors).toBeFalsy();
97+
98+
expect(result?.data?.search).toEqual([
99+
{
100+
__typename: "Book",
101+
title: "GraphQL Unions for Dummies",
102+
},
103+
{
104+
__typename: "Author",
105+
name: "Darrell",
106+
},
107+
]);
108+
} finally {
109+
await session.close();
110+
}
111+
});
112+
});

0 commit comments

Comments
 (0)