Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add inheritResolversFromInterfaces option - mergeSchemas #812

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
# Change log

### vNext
### vNEXT

* Loosens the apollo-link dependency [PR #765](https://github.com/apollographql/graphql-tools/pull/765)
* Use `getDescription` from `graphql-js` package [PR #672](https://github.com/apollographql/graphql-tools/pull/672)
* Update `IResolvers` to use source & context generics and to support all resolver use cases. [#896](https://github.com/apollographql/graphql-tools/pull/896)
* `WrapQuery`'s `wrapper` param can now return a SelectionSet. [PR #902](https://github.com/apollographql/graphql-tools/pull/902) [Issue #901](https://github.com/apollographql/graphql-tools/issues/901)
* Add null to return type of directive visitors in the TypeScript definition.
* Make sure mergeSchemas keeps Enum descriptions and deprecation status. [PR 898](https://github.com/apollographql/graphql-tools/pull/898/)
* Add `inheritResolversFromInterfaces` option to `mergeSchemas` [PR #812](https://github.com/apollographql/graphql-tools/pull/812)

### v3.0.5

Expand Down
5 changes: 5 additions & 0 deletions docs/source/schema-stitching.md
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,7 @@ mergeSchemas({
};
},
) => GraphQLNamedType;
inheritResolversFromInterfaces?: boolean;
})
```

Expand Down Expand Up @@ -406,3 +407,7 @@ const onTypeConflict = (left, right, info) => {
```

When using schema transforms, `onTypeConflict` is often unnecessary, since transforms can be used to prevent conflicts before merging schemas. However, if you're not using schema transforms, `onTypeConflict` can be a quick way to make `mergeSchemas` produce more desirable results.

#### inheritResolversFromInterfaces

The `inheritResolversFromInterfaces` option is simply passed through to `addResolveFunctionsToSchema`, which is called when adding resolvers to the schema under the covers. See [`addResolveFunctionsToSchema`](/docs/graphql-tools/resolvers.md#addResolveFunctionsToSchema) for more info.
11 changes: 8 additions & 3 deletions src/stitching/mergeSchemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,14 @@ export default function mergeSchemas({
schemas,
onTypeConflict,
resolvers,
schemaDirectives
schemaDirectives,
inheritResolversFromInterfaces
}: {
schemas: Array<string | GraphQLSchema | Array<GraphQLNamedType>>;
onTypeConflict?: OnTypeConflict;
resolvers?: IResolversParameter;
schemaDirectives?: { [name: string]: typeof SchemaDirectiveVisitor };
inheritResolversFromInterfaces?: boolean;
}): GraphQLSchema {
let visitType: VisitType = defaultVisitType;
if (onTypeConflict) {
Expand All @@ -75,19 +77,21 @@ export default function mergeSchemas({
);
visitType = createVisitTypeFromOnTypeConflict(onTypeConflict);
}
return mergeSchemasImplementation({ schemas, visitType, resolvers, schemaDirectives });
return mergeSchemasImplementation({ schemas, visitType, resolvers, schemaDirectives, inheritResolversFromInterfaces });
}

function mergeSchemasImplementation({
schemas,
visitType,
resolvers,
schemaDirectives
schemaDirectives,
inheritResolversFromInterfaces
}: {
schemas: Array<string | GraphQLSchema | Array<GraphQLNamedType>>;
visitType?: VisitType;
resolvers?: IResolversParameter;
schemaDirectives?: { [name: string]: typeof SchemaDirectiveVisitor };
inheritResolversFromInterfaces?: boolean;
}): GraphQLSchema {
const allSchemas: Array<GraphQLSchema> = [];
const typeCandidates: { [name: string]: Array<MergeTypeCandidate> } = {};
Expand Down Expand Up @@ -283,6 +287,7 @@ function mergeSchemasImplementation({
addResolveFunctionsToSchema({
schema: mergedSchema,
resolvers: mergeDeep(generatedResolvers, resolvers),
inheritResolversFromInterfaces
});

forEachField(mergedSchema, field => {
Expand Down
94 changes: 94 additions & 0 deletions src/test/testAlternateMergeSchemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,3 +235,97 @@ describe('merge schemas through transforms', () => {
});
});
});

describe('interface resolver inheritance', () => {
const testSchemaWithInterfaceResolvers = `
interface Node {
id: ID!
}
type User implements Node {
id: ID!
name: String!
}
type Query {
user: User!
}
schema {
query: Query
}
`;
const user = { _id: 1, name: 'Ada', type: 'User' };
const resolvers = {
Node: {
__resolveType: ({ type }: { type: string }) => type,
id: ({ _id }: { _id: number }) => `Node:${_id}`,
},
User: {
name: ({ name }: { name: string}) => `User:${name}`
},
Query: {
user: () => user
}
};

it('copies resolvers from interface', async () => {
const mergedSchema = mergeSchemas({
schemas: [
// pull in an executable schema just so mergeSchema doesn't complain
// about not finding default types (e.g. ID)
propertySchema,
testSchemaWithInterfaceResolvers
],
resolvers,
inheritResolversFromInterfaces: true
});
const query = `{ user { id name } }`;
const response = await graphql(mergedSchema, query);
expect(response).to.deep.equal({
data: {
user: {
id: `Node:1`,
name: `User:Ada`
}
}
});
});

it('does not copy resolvers from interface when flag is false',
async () => {
const mergedSchema = mergeSchemas({
schemas: [
// pull in an executable schema just so mergeSchema doesn't complain
// about not finding default types (e.g. ID)
propertySchema,
testSchemaWithInterfaceResolvers
],
resolvers,
inheritResolversFromInterfaces: false
});
const query = `{ user { id name } }`;
const response = await graphql(mergedSchema, query);
expect(response.errors.length).to.equal(1);
expect(response.errors[0].message).to.equal('Cannot return null for ' +
'non-nullable field User.id.');
expect(response.errors[0].path).to.deep.equal(['user', 'id']);
});

it('does not copy resolvers from interface when flag is not provided',
async () => {
const mergedSchema = mergeSchemas({
schemas: [
// pull in an executable schema just so mergeSchema doesn't complain
// about not finding default types (e.g. ID)
propertySchema,
testSchemaWithInterfaceResolvers
],
resolvers
});
const query = `{ user { id name } }`;
const response = await graphql(mergedSchema, query);
expect(response.errors.length).to.equal(1);
expect(response.errors[0].message).to.equal('Cannot return null for ' +
'non-nullable field User.id.');
expect(response.errors[0].path).to.deep.equal(['user', 'id']);
});
});