Skip to content

Instantly share code, notes, and snippets.

@giautm
Last active December 5, 2017 18:43
Show Gist options
  • Save giautm/d402c8f9131156f56a441477b47657c7 to your computer and use it in GitHub Desktop.
Save giautm/d402c8f9131156f56a441477b47657c7 to your computer and use it in GitHub Desktop.
import {
defaultFieldResolver,
DirectiveNode,
GraphQLField,
GraphQLFieldResolver,
GraphQLResolveInfo,
GraphQLSchema,
} from 'graphql';
import { forEachField } from 'graphql-tools';
import { getArgumentValues } from 'graphql/execution/values';
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>;
}
/**
* Attach directive resolvers to Schema
* https://github.com/apollographql/graphql-tools/issues/212#issuecomment-324447078
*
* @author Agon Bina <agon_bina@hotmail.com>
* @author Giau. Tran Minh <giau.tmg@gmail.com>
* @param resolvers: resolvers of directive
* @param schema: GraphQL schema to attach directive resolvers
*/
export 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);
};
}
});
});
}
import jwt from 'jsonwebtoken';
import { AuthorizationError } from './../errors';
import { NextResolver } from './attachDirectives';
export { attachDirectives } from './attachDirectives';
export const directiveResolvers = {
upperCase(next: NextResolver, source, args, context) {
return next().then((r) => {
if (typeof(r) === 'string') {
return r.toUpperCase();
}
return r;
});
},
auth(next: NextResolver, source, args, { auth, scopes }) {
if (auth) {
const expected = args.scope;
if (expected.length() === 0 || expected.some((e) => scopes.includes(e))) {
return next();
}
throw new AuthorizationError({
message: `You are not authorized. Expected scopes: ${expected.join(', ')}`,
});
}
throw new AuthorizationError({
message: 'You must supply a JWT for authorization!',
});
},
};
directive @auth(scope: [String]) on QUERY | FIELD
directive @upperCase on QUERY | FIELD
# this is needed for upload to work
scalar Upload
type Query {
uploads: [File]
hello: String @upperCase
}
type Mutation {
singleUpload(file: Upload!): File! @auth
multipleUpload (files: [Upload!]!): [File!]!
}
type File {
id: ID!
path: String!
filename: String!
mimetype: String!
encoding: String!
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment