Skip to content

Commit

Permalink
Add support for inheritResolversFromInterfaces in mergeSchemas (#812
Browse files Browse the repository at this point in the history
)
  • Loading branch information
Druotic authored and stubailo committed Jul 31, 2018
1 parent 3eb50a9 commit 1f2aead
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 4 deletions.
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']);
});
});

0 comments on commit 1f2aead

Please sign in to comment.