Skip to content

Commit

Permalink
Core fixes and new tests
Browse files Browse the repository at this point in the history
+ Uses reduce instead of external object iteration.
+ I have added tests for field level resolvers.
+ Resolver arguments are now automatically added when found omited.
+ Tests
  • Loading branch information
maticzav committed Mar 25, 2018
1 parent af090b8 commit 50cfe17
Show file tree
Hide file tree
Showing 4 changed files with 355 additions and 146 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ node_modules
dist
.idea
.DS_Store
*.log*
13 changes: 7 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,7 @@
"test": "npm run lint && npm run build && npm run test-ava",
"semantic-release": "semantic-release"
},
"files": [
"dist"
],
"files": ["dist"],
"release": {
"branch": "master"
},
Expand All @@ -43,8 +41,11 @@
"bugs": {
"url": "https://github.com/graphcool/graphql-middleware/issues"
},
"keywords": [
"graphql"
],
"keywords": ["graphql"],
"prettier": {
"semi": false,
"trailingComma": "all",
"singleQuote": true
},
"license": "MIT"
}
243 changes: 169 additions & 74 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,137 +7,226 @@ import {
GraphQLNamedType,
GraphQLObjectType,
GraphQLField,
GraphQLFieldResolver
} from "graphql";
import { mergeSchemas } from "graphql-tools";
import { IResolvers } from "graphql-tools/dist/Interfaces";
GraphQLFieldResolver,
GraphQLInterfaceType,
} from 'graphql'
import { mergeSchemas } from 'graphql-tools'
import { IResolvers } from 'graphql-tools/dist/Interfaces'
import {
IDocumentMiddlewareFunction,
IFieldMiddleware,
IFieldMiddlewareFunction,
IFieldMiddlewareTypeMap,
IFieldMiddlewareFieldMap
} from "./types";
IFieldMiddlewareFieldMap,
} from './types'

// Type checks

function isMiddlewareFunction(obj: any): boolean {
return (
typeof obj === "function" ||
(typeof obj === "object" && obj.then !== undefined)
);
typeof obj === 'function' ||
(typeof obj === 'object' && obj.then !== undefined)
)
}

function isGraphQLObjectType(obj: any): boolean {
return obj._fields !== undefined;
return obj instanceof GraphQLObjectType || obj instanceof GraphQLInterfaceType
}

//
// Wrapper

function wrapResolverInMiddleware(
resolver: GraphQLFieldResolver<any, any, any>,
middleware: IFieldMiddlewareFunction
middleware: IFieldMiddlewareFunction,
): GraphQLFieldResolver<any, any> {
return (parent, args, ctx, info) => {
return middleware(resolver, parent, args, ctx, info);
};
return middleware(
(_parent = parent, _args = args, _ctx = ctx, _info = info) =>
resolver(_parent, _args, _ctx, _info),
parent,
args,
ctx,
info,
)
}
}

// Inspired by graphql-tools.
function transformDocumentMiddlewareToFieldMiddleware(
middleware: IDocumentMiddlewareFunction,
): IFieldMiddlewareFunction {
let value = null
const randomNumber = Math.random()

return async (resolve, parent, args, ctx, info) => {
if (!info.operation['__runAtMostOnce']) {
info.operation['__runAtMostOnce'] = {}
}

if (!info.operation['__runAtMostOnce'][randomNumber]) {
info.operation['__runAtMostOnce'][randomNumber] = true
value = await middleware(resolve, parent, ctx, info)
return value
}

return value
}
}

// Merge

function applyMiddlewareToField(
field: GraphQLField<any, any, any>,
middleware: IFieldMiddlewareFunction
middleware: IFieldMiddlewareFunction,
): GraphQLFieldResolver<any, any, any> {
let resolver = field.resolve;
let resolver = field.resolve

if (field.subscribe) {
resolver = field.subscribe;
resolver = field.subscribe
}

return wrapResolverInMiddleware(resolver, middleware);
return wrapResolverInMiddleware(resolver, middleware)
}

function applyMiddlewareToType(
type: GraphQLObjectType,
middleware: IFieldMiddlewareFunction | IFieldMiddlewareFieldMap
middleware: IFieldMiddlewareFunction | IFieldMiddlewareFieldMap,
): IResolvers {
let resolvers = {};
const fieldMap = type.getFields();

Object.keys(fieldMap).forEach(field => {
if (isMiddlewareFunction(middleware)) {
resolvers[field] = applyMiddlewareToField(
fieldMap[field],
middleware as IFieldMiddlewareFunction
);
} else {
resolvers[field] = applyMiddlewareToField(
fieldMap[field],
middleware[field]
);
}
});
const fieldMap = type.getFields()

return resolvers;
if (isMiddlewareFunction(middleware)) {
const resolvers = Object.keys(fieldMap).reduce(
(resolvers, field) => ({
...resolvers,
[field]: applyMiddlewareToField(
fieldMap[field],
middleware as IFieldMiddlewareFunction,
),
}),
{},
)

return resolvers
} else {
const resolvers = Object.keys(middleware).reduce(
(resolvers, field) => ({
...resolvers,
[field]: applyMiddlewareToField(fieldMap[field], middleware[field]),
}),
{},
)

return resolvers
}
}

function applyMiddlewareToSchema(
schema: GraphQLSchema,
middleware: IFieldMiddlewareFunction
middleware: IFieldMiddlewareFunction,
): IResolvers {
let resolvers = {};
const typeMap = schema.getTypeMap();
const typeMap = schema.getTypeMap()

Object.keys(typeMap)
const resolvers = Object.keys(typeMap)
.filter(type => isGraphQLObjectType(typeMap[type]))
.forEach(type => {
resolvers[type] = applyMiddlewareToType(
typeMap[type] as GraphQLObjectType,
middleware
);
});

return resolvers;
.reduce(
(resolvers, type) => ({
...resolvers,
[type]: applyMiddlewareToType(
typeMap[type] as GraphQLObjectType,
middleware,
),
}),
{},
)

return resolvers
}

// Generator

function generateResolverFromSchemaAndMiddleware(
function generateResolverFromSchemaAndFieldMiddleware(
schema: GraphQLSchema,
middleware: IFieldMiddleware
middleware: IFieldMiddleware,
): IResolvers {
let resolvers = {};

if (isMiddlewareFunction(middleware)) {
resolvers = applyMiddlewareToSchema(
return applyMiddlewareToSchema(
schema,
middleware as IFieldMiddlewareFunction
);
middleware as IFieldMiddlewareFunction,
)
} else {
const typeMap = schema.getTypeMap();

Object.keys(middleware).forEach(type => {
resolvers[type] = applyMiddlewareToType(
typeMap[type] as GraphQLObjectType,
middleware[type]
);
});
const typeMap = schema.getTypeMap()

const resolvers = Object.keys(middleware).reduce(
(resolvers, type) => ({
...resolvers,
[type]: applyMiddlewareToType(
typeMap[type] as GraphQLObjectType,
middleware[type],
),
}),
{},
)

return resolvers
}
}

function generateResolverFromSchemaAndDocumentMiddleware(
schema: GraphQLSchema,
middleware: IDocumentMiddlewareFunction,
): IResolvers {
const typeMap = {
Query: schema.getQueryType(),
Mutation: schema.getMutationType(),
Subscription: schema.getSubscriptionType(),
}

const resolvers = Object.keys(typeMap)
.filter(type => isGraphQLObjectType(typeMap[type]))
.reduce(
(resolvers, type) => ({
...resolvers,
[type]: applyMiddlewareToType(
typeMap[type] as GraphQLObjectType,
transformDocumentMiddlewareToFieldMiddleware(middleware),
),
}),
{},
)

return {}
}

return resolvers;
// Reducers

function addFieldMiddlewareToSchema(
schema: GraphQLSchema,
middleware: IFieldMiddleware,
): GraphQLSchema {
const resolvers = generateResolverFromSchemaAndFieldMiddleware(
schema,
middleware,
)

return mergeSchemas({
schemas: [schema],
resolvers,
})
}

function addMiddlewareToSchema(
function addDocumentMiddlewareToSchema(
schema: GraphQLSchema,
middleware: IFieldMiddleware
middleware: IDocumentMiddlewareFunction,
): GraphQLSchema {
const resolvers = generateResolverFromSchemaAndMiddleware(schema, middleware);
const resolvers = generateResolverFromSchemaAndDocumentMiddleware(
schema,
middleware,
)

return mergeSchemas({
schemas: [schema],
resolvers
});
resolvers,
})
}

// Exposed functions
Expand All @@ -148,16 +237,22 @@ export function applyFieldMiddleware(
): GraphQLSchema {
const schemaWithMiddleware = middlewares.reduce(
(currentSchema, middleware) =>
addMiddlewareToSchema(currentSchema, middleware),
schema
);
addFieldMiddlewareToSchema(currentSchema, middleware),
schema,
)

return schemaWithMiddleware;
return schemaWithMiddleware
}

export function applyDocumentMiddleware(
schema: GraphQLSchema,
...middlewares: IDocumentMiddlewareFunction[]
): GraphQLSchema {
return null;
const schemaWithMiddleware = middlewares.reduce(
(currentSchema, middleware) =>
addDocumentMiddlewareToSchema(currentSchema, middleware),
schema,
)

return schemaWithMiddleware
}
Loading

0 comments on commit 50cfe17

Please sign in to comment.