Skip to content

Commit

Permalink
Prevent illegal resolver invocation and use way to access children cache
Browse files Browse the repository at this point in the history
Signed-off-by: Arda TANRIKULU <ardatanrikulu@gmail.com>
  • Loading branch information
ardatan committed Dec 11, 2018
1 parent ff10d03 commit 24aa36a
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 6 deletions.
26 changes: 26 additions & 0 deletions packages/core/src/errors/illegal-resolver-invocation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
export class IllegalResolverInvocationError extends Error {
constructor(private _resolverPath: string, private _moduleName: string, private _detail: string) {
super(`
GraphQL-Modules Error: Illegal Resolver Invocation!
- Resolver #${_resolverPath} is invoked unsafely outside of GraphQL-Modules.
-- Detail: ${_detail}
Possible solutions:
- You may forget to pass context of the module to your GraphQL Server.
-- Check if it is passed like below;
--- const { schema, context } = YourModule;
--- new ApolloServer({ schema, context });'
`);
Object.setPrototypeOf(this, IllegalResolverInvocationError.prototype);
Error.captureStackTrace(this, IllegalResolverInvocationError);
}
get resolverPath() {
return this._resolverPath;
}
get moduleName() {
return this._moduleName;
}
get detail() {
return this._detail;
}
}
1 change: 1 addition & 0 deletions packages/core/src/errors/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export { DependencyModuleUndefinedError } from './dependency-module-undefined';
export { TypeDefNotFoundError } from './typedef-not-found';
export { ProviderClassNotDecoratedError } from './provider-class-not-decorated';
export { ModuleConfigRequiredError } from './module-config-required';
export { IllegalResolverInvocationError } from './illegal-resolver-invocation';
20 changes: 15 additions & 5 deletions packages/core/src/graphql-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Provider, Injector, ProviderScope } from '@graphql-modules/di';
import { DocumentNode, GraphQLSchema, parse } from 'graphql';
import { IResolversComposerMapping, composeResolvers } from './resolvers-composition';
import { DepGraph } from 'dependency-graph';
import { DependencyModuleNotFoundError, SchemaNotValidError, DependencyModuleUndefinedError, TypeDefNotFoundError, ModuleConfigRequiredError } from './errors';
import { DependencyModuleNotFoundError, SchemaNotValidError, DependencyModuleUndefinedError, TypeDefNotFoundError, ModuleConfigRequiredError, IllegalResolverInvocationError } from './errors';
import * as deepmerge from 'deepmerge';
import { ModuleSessionInfo } from './module-session-info';
import { asArray } from './utils';
Expand Down Expand Up @@ -385,6 +385,15 @@ export class GraphQLModule<Config = any, Request = any, Context = any> {
return directiveResolvers;
}

private checkIfResolverCalledSafely(resolverPath: string, appContext: any, info: any) {
if (!('networkRequest' in appContext)) {
throw new IllegalResolverInvocationError(resolverPath, this.name, `Network Request hasn't been passed!`);
}
if (typeof info === 'undefined') {
throw new IllegalResolverInvocationError(resolverPath, this.name, `GraphQL Resolve Information hasn't been passed!`);
}
}

private addSessionInjectorToSelfResolversContext() {
const resolvers = this.selfResolvers;
// tslint:disable-next-line:forin
Expand All @@ -396,13 +405,15 @@ export class GraphQLModule<Config = any, Request = any, Context = any> {
if (typeof resolver === 'function') {
if (prop !== '__resolveType') {
typeResolvers[prop] = async (root: any, args: any, appContext: any, info: any) => {
this.checkIfResolverCalledSafely(`${type}.${prop}`, appContext, info);
const { networkRequest } = appContext;
const moduleContext = await this.context(networkRequest);
info.schema = this._cache.schema;
return resolver.call(typeResolvers, root, args, moduleContext, info);
};
} else {
typeResolvers[prop] = async (root: any, appContext: any, info: any) => {
this.checkIfResolverCalledSafely(`${type}.${prop}`, appContext, info);
const { networkRequest } = appContext;
const moduleContext = await this.context(networkRequest);
info.schema = this._cache.schema;
Expand All @@ -422,6 +433,7 @@ export class GraphQLModule<Config = any, Request = any, Context = any> {
const compositionArr = asArray(resolversComposition[path]);
resolversComposition[path] = [
(next: any) => async (root: any, args: any, appContext: any, info: any) => {
this.checkIfResolverCalledSafely(path, appContext, info);
const { networkRequest } = appContext;
const moduleContext = await this.context(networkRequest);
info.schema = this._cache.schema;
Expand Down Expand Up @@ -474,13 +486,11 @@ export class GraphQLModule<Config = any, Request = any, Context = any> {

if (module._cache.modulesMap !== modulesMap) {
module._cache.modulesMap = modulesMap;
module._cache.injector = undefined;
module._cache.schema = undefined;
module._cache.contextBuilder = undefined;
module.buildSchemaAndInjector();
}

const { injector, resolvers, typeDefs, contextBuilder, schemaDirectives, extraSchemas, directiveResolvers } = module._cache;
const { injector, resolvers, typeDefs, schemaDirectives } = module;
const { contextBuilder, extraSchemas, directiveResolvers } = module._cache;

importsInjectors.add(injector);
importsResolvers.add(resolvers);
Expand Down
23 changes: 22 additions & 1 deletion packages/core/tests/graphql-module.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
OnRequest,
ModuleConfigRequiredError,
} from '../src';
import { execute, GraphQLSchema, printSchema, GraphQLString, defaultFieldResolver } from 'graphql';
import { execute, GraphQLSchema, printSchema, GraphQLString, defaultFieldResolver, print } from 'graphql';
import { stripWhitespaces } from './utils';
import gql from 'graphql-tag';
import { SchemaDirectiveVisitor, makeExecutableSchema } from 'graphql-tools';
Expand Down Expand Up @@ -987,4 +987,25 @@ describe('GraphQLModule', () => {
expect(result.errors).toBeFalsy();
expect(result.data['foo']).toBe('FOO');
});
it('should export correct typeDefs and resolvers', async () => {
const gqlModule = new GraphQLModule({
imports: [
new GraphQLModule({
name: 'test',
typeDefs: 'type Query { test: Int }',
resolvers: {
Query: {
test: () => 1,
},
},
}),
],
});

const typeDefs = gqlModule.typeDefs;
expect(stripWhitespaces(print(typeDefs))).toBe(stripWhitespaces('type Query { test: Int }'));
const context = await gqlModule.context({});
const resolvers = gqlModule.resolvers;
expect(await resolvers['Query']['test'](null, {}, context, {})).toBe(1);
});
});

0 comments on commit 24aa36a

Please sign in to comment.