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

Bring attachDirectiveResolvers to graphql-tools #518

Merged
merged 32 commits into from
Dec 12, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
86fcbb5
Bring attachDirectives to graphql-tools
Dec 3, 2017
702ccaa
Mark directiveResolvers as optional
Dec 3, 2017
ccc2b39
Fix eslint
Dec 3, 2017
695a08a
Fix typescript
Dec 3, 2017
2f6281d
Add simple test for @upper directive
Dec 3, 2017
711c5c6
Fix tests
Dec 3, 2017
aa42674
Add more test
Dec 3, 2017
90df60c
Add test
Dec 3, 2017
1527df6
Add test
Dec 3, 2017
13b4a7a
Add test for multi-directives on single field
Dec 3, 2017
4f82522
Allow to catch error from next resolver
Dec 4, 2017
b80aadb
Throw error if directive is undefined
Dec 4, 2017
766f14a
Fix test case text
Dec 4, 2017
8f281e4
Add test
Dec 4, 2017
4da2217
add test
Dec 4, 2017
ced0f4c
Update changelog
Dec 4, 2017
c7eade3
Fix for preview
Dec 4, 2017
27e4cfc
rename to attachDirectiveResolvers
Dec 4, 2017
e17c34b
Update NextResolverFn
Dec 4, 2017
9e02d49
Update DirectiveResolverFn
Dec 4, 2017
34a51b3
Change function signal to match others
Dec 4, 2017
7d6d5a8
Update
Dec 4, 2017
78351c2
Add tests
Dec 4, 2017
43aedb8
Update
Dec 5, 2017
8a7a6a9
Added a unit test for directives with arguments
yaworsw Dec 6, 2017
3c4f705
Merge pull request #1 from yaworsw/feature/attach-directives
giautm Dec 6, 2017
0e54002
Merge branch 'master' into feature/attach-directives
giautm Dec 6, 2017
e38f6ed
Merge branch 'master' into feature/attach-directives
giautm Dec 6, 2017
45e3a54
Merge branch 'master' into feature/attach-directives
giautm Dec 8, 2017
47067db
Merge branch 'master' into feature/attach-directives
giautm Dec 12, 2017
0bf395c
Merge branch 'master' into feature/attach-directives
giautm Dec 12, 2017
788f785
Merge branch 'master' into feature/attach-directives
giautm Dec 12, 2017
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

### VNEXT

* Added support for custom Directives on FIELD [Issue #212](https://github.com/apollographql/graphql-tools/issues/212) [PR #518](https://github.com/apollographql/graphql-tools/pull/518)
* Allow passing in a string `schema` to `makeRemoteExecutableSchema` [PR #521](https://github.com/apollographql/graphql-tools/pull/521)
* ...

Expand Down
14 changes: 14 additions & 0 deletions src/Interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ export interface IExecutableSchemaDefinition {
logger?: ILogger;
allowUndefinedInResolve?: boolean;
resolverValidationOptions?: IResolverValidationOptions;
directiveResolvers?: IDirectiveResolvers<any, any>;
}

export type IFieldIteratorFn = (
Expand All @@ -84,6 +85,19 @@ export type IFieldIteratorFn = (
fieldName: string,
) => void;

export type NextResolverFn = () => Promise<any>;
export type DirectiveResolverFn<TSource, TContext> = (
next: NextResolverFn,
source: TSource,
args: { [argName: string]: any },
context: TContext,
info: GraphQLResolveInfo,
) => any;

export interface IDirectiveResolvers<TSource, TContext> {
[directiveName: string]: DirectiveResolverFn<TSource, TContext>;
}

/* XXX on mocks, args are optional, Not sure if a bug. */
export type IMockFn = GraphQLFieldResolver<any, any>;
export type IMocks = { [key: string]: IMockFn };
Expand Down
57 changes: 57 additions & 0 deletions src/schemaGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
print,
Kind,
DefinitionNode,
DirectiveNode,
defaultFieldResolver,
buildASTSchema,
extendSchema,
Expand All @@ -27,6 +28,7 @@ import {
GraphQLInterfaceType,
GraphQLFieldMap,
} from 'graphql';
import { getArgumentValues } from 'graphql/execution/values';
import {
IExecutableSchemaDefinition,
ILogger,
Expand All @@ -38,6 +40,7 @@ import {
IConnector,
IConnectorCls,
IResolverValidationOptions,
IDirectiveResolvers,
} from './Interfaces';

import { deprecated } from 'deprecated-decorator';
Expand All @@ -62,6 +65,7 @@ function _generateSchema(
// TODO: rename to allowUndefinedInResolve to be consistent
allowUndefinedInResolve: boolean,
resolverValidationOptions: IResolverValidationOptions,
directiveResolvers: IDirectiveResolvers<any, any>,
) {
if (typeof resolverValidationOptions !== 'object') {
throw new SchemaError(
Expand Down Expand Up @@ -95,6 +99,10 @@ function _generateSchema(
addErrorLoggingToSchema(schema, logger);
}

if (directiveResolvers) {
attachDirectiveResolvers(schema, directiveResolvers);
}

return schema;
}

Expand All @@ -105,13 +113,15 @@ function makeExecutableSchema({
logger,
allowUndefinedInResolve = true,
resolverValidationOptions = {},
directiveResolvers = null,
}: IExecutableSchemaDefinition) {
const jsSchema = _generateSchema(
typeDefs,
resolvers,
logger,
allowUndefinedInResolve,
resolverValidationOptions,
directiveResolvers,
);
if (typeof resolvers['__schema'] === 'function') {
// TODO a bit of a hack now, better rewrite generateSchema to attach it there.
Expand Down Expand Up @@ -637,6 +647,52 @@ function runAtMostOncePerRequest(
};
}

function attachDirectiveResolvers(
schema: GraphQLSchema,
directiveResolvers: IDirectiveResolvers<any, any>,
) {
if (typeof directiveResolvers !== 'object') {
throw new Error(
`Expected directiveResolvers to be of type object, got ${typeof directiveResolvers}`,
);
}
if (Array.isArray(directiveResolvers)) {
throw new Error('Expected directiveResolvers to be of type object, got Array');
}
forEachField(schema, (field: GraphQLField<any, any>) => {
const directives = field.astNode.directives;
directives.forEach((directive: DirectiveNode) => {
const directiveName = directive.name.value;
const resolver = directiveResolvers[directiveName];

if (resolver) {
const originalResolver = field.resolve || defaultFieldResolver;
const Directive = schema.getDirective(directiveName);
if (typeof Directive === 'undefined') {
throw new Error(`Directive @${directiveName} is undefined. ` +
'Please define in schema before using');
}
const directiveArgs = getArgumentValues(Directive, directive);

field.resolve = (...args: any[]) => {
const [source, , context, info] = args;
return resolver(() => {
try {
const promise = originalResolver.call(field, ...args);
if (promise instanceof Promise) {
return promise;
}
return Promise.resolve(promise);
} catch (error) {
return Promise.reject(error);
}
}, source, directiveArgs, context, info);
};
}
});
});
}

export {
makeExecutableSchema,
SchemaError,
Expand All @@ -650,4 +706,5 @@ export {
addSchemaLevelResolveFunction,
attachConnectorsToContext,
concatenateTypeDefs,
attachDirectiveResolvers,
};
Loading