From 4fa6a7dde28e37bd251faf1b54f36724db07410b Mon Sep 17 00:00:00 2001 From: Marcelo Risse Date: Wed, 22 Nov 2017 19:40:02 -0200 Subject: [PATCH 1/4] Add After decorator --- src/decorator/after.decorator.ts | 44 ++++++++ src/decorator/index.ts | 1 + src/metadata-builder/metadata-args.storage.ts | 12 ++- src/metadata-builder/metadata.builder.ts | 16 +++ src/metadata/args/after.arg.ts | 8 ++ src/metadata/args/index.ts | 1 + src/metadata/options/after.option.ts | 13 +++ src/metadata/options/index.ts | 1 + src/metadata/types/after.metadata.ts | 6 ++ src/metadata/types/field.metadata.ts | 2 + src/metadata/types/index.ts | 1 + src/middleware.ts | 7 ++ src/specs/functional.spec.ts | 100 +++++++++++++++++- src/specs/schema_factory.spec.ts | 6 +- src/type-factory/field.type-factory.ts | 24 +++-- 15 files changed, 229 insertions(+), 13 deletions(-) create mode 100644 src/decorator/after.decorator.ts create mode 100644 src/metadata/args/after.arg.ts create mode 100644 src/metadata/options/after.option.ts create mode 100644 src/metadata/types/after.metadata.ts diff --git a/src/decorator/after.decorator.ts b/src/decorator/after.decorator.ts new file mode 100644 index 0000000..56d8272 --- /dev/null +++ b/src/decorator/after.decorator.ts @@ -0,0 +1,44 @@ +import { AfterMiddleware } from '../middleware'; +import { AfterOption } from '../metadata'; +import { getMetadataArgsStorage } from '../metadata-builder'; + +/** + * Adding ability to declarative override function resolution on schemas with an implementation analog to an express middleware. + * + * Usage example: + * ``` + * let middleware: AfterMiddleware = (context: any, args: { [key: string]: any }, next: (error?: Error, value?: any) => any): any => { + * if(context.user.role != 'any role') { + * // can use context and resolve/return value as `1000`, for example, regardless of what resolve function actually implements + * next(null, 1000); + * } else { + * // keeps regular resolution flow + * next(); + * } + * }; + * + * ... + * + * class ObjType { + * @Field() + * @After(middleware) + * myFunction(input: number): number { + * return input * 2; + * } + * } + * + * ``` + * @param option Options for an Schema + */ +export function After(option: AfterOption | AfterMiddleware) { + return function (target: any, propertyKey: any, index: number) { + getMetadataArgsStorage().afters.push({ + target: target, + name: target.name || propertyKey, + description: option && (option as AfterOption).description ? (option as AfterOption).description : null, + index: index, + property: propertyKey, + middleware: option && (option as AfterOption).middleware ? (option as AfterOption).middleware : option as AfterMiddleware, + }); + } as Function; +} diff --git a/src/decorator/index.ts b/src/decorator/index.ts index 1704630..c0e5195 100644 --- a/src/decorator/index.ts +++ b/src/decorator/index.ts @@ -10,3 +10,4 @@ export * from './order-by.decorator'; export * from './root.decorator'; export * from './before.decorator'; export * from './interface-type.decorator'; +export * from './after.decorator'; diff --git a/src/metadata-builder/metadata-args.storage.ts b/src/metadata-builder/metadata-args.storage.ts index bcdf80d..3db2d16 100644 --- a/src/metadata-builder/metadata-args.storage.ts +++ b/src/metadata-builder/metadata-args.storage.ts @@ -1,4 +1,5 @@ import { + AfterArg, ArgumentArg, BeforeArg, ContextArg, @@ -7,12 +8,12 @@ import { EnumTypeArg, EnumValueArg, FieldArg, + InterfaceTypeArg, ObjectTypeArg, OrderByArg, RootArg, SchemaArg, UnionTypeArg, - InterfaceTypeArg, } from '../metadata/args'; import { MetadataUtils } from './metadata.utils'; @@ -31,6 +32,7 @@ export class MetadataArgsStorage { orderBys: OrderByArg[] = []; befores: BeforeArg[] = []; interfaces: InterfaceTypeArg[] = []; + afters: AfterArg[] = []; filterEnumsByClass(target: any): EnumTypeArg[] { return this.enums.filter(item => item.target === target); @@ -85,9 +87,13 @@ export class MetadataArgsStorage { return this.befores.filter(item => item.target === target && item.property === property); } + filterAfterByByClassAndProperty(target: any, property: string): AfterArg[] { + return this.afters.filter(item => item.target === target && item.property === property); + } + /** - * Filters given array by a given target or targets and prevents duplicate property names. - */ + * Filters given array by a given target or targets and prevents duplicate property names. + */ protected filterByTargetAndWithoutDuplicateProperties( array: T[], target: (Function | string) | (Function | string)[], diff --git a/src/metadata-builder/metadata.builder.ts b/src/metadata-builder/metadata.builder.ts index 885aa49..42a1eb8 100644 --- a/src/metadata-builder/metadata.builder.ts +++ b/src/metadata-builder/metadata.builder.ts @@ -1,4 +1,5 @@ import { + AfterMetadata, ArgumentMetadata, BeforeMetadata, ContextMetadata, @@ -105,6 +106,7 @@ export class MetadataBuilder { root: this.buildRootMetadata(target, arg.property), orderBy: this.buildOrderByMetadata(target, arg.property), before: this.buildBeforeMetadata(target, arg.property), + after: this.buildAfterMetadata(target, arg.property), })); } @@ -191,6 +193,20 @@ export class MetadataBuilder { })); } + protected buildAfterMetadata(target: any, property: string): AfterMetadata | undefined { + return getMetadataArgsStorage() + .filterAfterByByClassAndProperty(target, property) + .map(arg => ({ + target: arg.target, + name: arg.name, + description: arg.description, + index: arg.index, + property: arg.property, + middleware: arg.middleware, + })) + .find((_, index) => index === 0); + } + } /** diff --git a/src/metadata/args/after.arg.ts b/src/metadata/args/after.arg.ts new file mode 100644 index 0000000..c52a9ac --- /dev/null +++ b/src/metadata/args/after.arg.ts @@ -0,0 +1,8 @@ +import { AfterMiddleware } from '../../middleware'; +import { Argument } from './argument'; + +export interface AfterArg extends Argument { + index: number; + property: string; + middleware: AfterMiddleware; +} diff --git a/src/metadata/args/index.ts b/src/metadata/args/index.ts index deb70d2..0d9a3b5 100644 --- a/src/metadata/args/index.ts +++ b/src/metadata/args/index.ts @@ -10,3 +10,4 @@ export * from './root.arg'; export * from './order-by.arg'; export * from './before.arg'; export * from './interface-type.arg'; +export * from './after.arg'; diff --git a/src/metadata/options/after.option.ts b/src/metadata/options/after.option.ts new file mode 100644 index 0000000..189c716 --- /dev/null +++ b/src/metadata/options/after.option.ts @@ -0,0 +1,13 @@ +import { AfterMiddleware } from '../../middleware'; +import { Option } from './option'; + +/** + * After (aka AfterMiddleware) options + */ +export interface AfterOption extends Option { + /** + * Middeware to change resolver behavior. + * Check decorator docs for example usage. + */ + middleware: AfterMiddleware; +} diff --git a/src/metadata/options/index.ts b/src/metadata/options/index.ts index 96b9dc5..8ec8118 100644 --- a/src/metadata/options/index.ts +++ b/src/metadata/options/index.ts @@ -8,3 +8,4 @@ export * from './argument.option'; export * from './order-by.option'; export * from './before.option'; export * from './interface.option'; +export * from './after.option'; diff --git a/src/metadata/types/after.metadata.ts b/src/metadata/types/after.metadata.ts new file mode 100644 index 0000000..05c127d --- /dev/null +++ b/src/metadata/types/after.metadata.ts @@ -0,0 +1,6 @@ +import { AfterMiddleware } from '../../middleware'; +import { FieldArgumentMetadata } from './field-argument.metadata'; + +export interface AfterMetadata extends FieldArgumentMetadata { + middleware: AfterMiddleware; +} diff --git a/src/metadata/types/field.metadata.ts b/src/metadata/types/field.metadata.ts index 35b28d9..16299ff 100644 --- a/src/metadata/types/field.metadata.ts +++ b/src/metadata/types/field.metadata.ts @@ -1,3 +1,4 @@ +import { AfterMetadata } from './after.metadata'; import { ArgumentMetadata } from './argument.metadata'; import { BeforeMetadata } from './before.metadata'; import { ContextMetadata } from './context.metadata'; @@ -16,4 +17,5 @@ export interface FieldMetadata extends Metadata { root?: RootMetadata; orderBy?: OrderByMetadata; before?: BeforeMetadata; + after?: AfterMetadata; } diff --git a/src/metadata/types/index.ts b/src/metadata/types/index.ts index c421582..9605e3f 100644 --- a/src/metadata/types/index.ts +++ b/src/metadata/types/index.ts @@ -11,3 +11,4 @@ export * from './order-by.metadata'; export * from './root.metadata'; export * from './field.metadata'; export * from './interface.metadata'; +export * from './after.metadata'; diff --git a/src/middleware.ts b/src/middleware.ts index ac659a1..b87d2d7 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -1 +1,8 @@ export type Middleware = (context: any, args: { [key: string]: any }, next: (error?: Error, value?: any) => any) => Promise | any; + +export type AfterMiddleware = ( + context: any, + args: { [key: string]: any }, + result: Promise | any, + next: (error?: Error, value?: any) => any, +) => Promise | any; diff --git a/src/specs/functional.spec.ts b/src/specs/functional.spec.ts index 8abea61..628281a 100644 --- a/src/specs/functional.spec.ts +++ b/src/specs/functional.spec.ts @@ -178,12 +178,110 @@ describe('Functional', function () { }); it('resolves @Field decorated with @Before Middleware and returning from resolver', async function () { + const schema = schemaFactory(SchemaType); + const result = await graphql.graphql(schema, `query { ignore }`); + assert(result.data.ignore === 'Hello, world!'); + }); + + it('resolves @Field decorated with @Before Middleware and erroring', async function () { + const schema = schemaFactory(SchemaType); + const result = await graphql.graphql(schema, `query { callError }`); + + assert(result.data.callError === null); + assert(typeof (result.errors) !== 'undefined'); + assert(result.errors.length === 1); + assert(result.errors[0].message === 'Error from middleware'); + }); + + }); + + describe('After Middleware', function () { + + @D.ObjectType() + class QueryType { + @D.Field({ type: graphql.GraphQLString }) + @D.After({ + middleware: (context, args, result, next) => next(null, 'Hello from middleware'), + }) + async replace(): Promise { + return 'Hello, world!'; + } + + @D.Field({ type: graphql.GraphQLString }) + @D.After({ + middleware: (context, args, result, next) => { + if (args.replace) { + next(null, 'Hello from middleware'); + } else { + next(); + } + }, + }) + async replaceWithCondition( + @D.Arg({ name: 'replace', description: 'any desc' }) replace: boolean, + ): Promise { + return 'Hello!'; + } + + @D.Field({ type: graphql.GraphQLString }) + @D.After({ + middleware: (context, args, result, next) => { + next(null, Promise.resolve(result).then(str => str.replace('world', 'after middleware'))); + }, + }) + async modifyResult(): Promise { + return 'Hello, world!'; + } + + @D.Field({ type: graphql.GraphQLString }) + @D.After({ middleware: (context, args, result, next) => next() }) + async ignore(): Promise { + return 'Hello, world!'; + } + + @D.Field({ type: graphql.GraphQLString }) + @D.After({ middleware: (context, args, result, next) => next(new Error('Error from middleware'), 'x') }) + async callError(): Promise { + return 'Hello, world!'; + } + } + + @D.Schema() + class SchemaType { + @D.Query() query: QueryType; + } + + it('resolves @Field decorated with @After Middleware replacing resolver', async function () { const schema = schemaFactory(SchemaType); const result = await graphql.graphql(schema, `query { replace }`); assert(result.data.replace === 'Hello from middleware'); }); - it('resolves @Field decorated with @Before Middleware and erroring', async function () { + it('resolves @Field decorated with @After Middleware and modifying resolver depending on condition', async function () { + const schema = schemaFactory(SchemaType); + const result = await graphql.graphql(schema, `query { replaceWithCondition(replace: true) }`); + assert(result.data.replaceWithCondition === 'Hello from middleware'); + }); + + it('resolves @Field decorated with @After Middleware and modifying resolver depending on condition', async function () { + const schema = schemaFactory(SchemaType); + const result = await graphql.graphql(schema, `query { replaceWithCondition }`); + assert(result.data.replaceWithCondition === 'Hello!'); + }); + + it('resolves @Field decorated with @After Middleware and modifying resolver result', async function () { + const schema = schemaFactory(SchemaType); + const result = await graphql.graphql(schema, `query { modifyResult }`); + assert(result.data.modifyResult === 'Hello, after middleware!'); + }); + + it('resolves @Field decorated with @After Middleware and returning from resolver', async function () { + const schema = schemaFactory(SchemaType); + const result = await graphql.graphql(schema, `query { ignore }`); + assert(result.data.ignore === 'Hello, world!'); + }); + + it('resolves @Field decorated with @After Middleware and erroring', async function () { const schema = schemaFactory(SchemaType); const result = await graphql.graphql(schema, `query { callError }`); diff --git a/src/specs/schema_factory.spec.ts b/src/specs/schema_factory.spec.ts index afdd378..eebbc9b 100644 --- a/src/specs/schema_factory.spec.ts +++ b/src/specs/schema_factory.spec.ts @@ -252,7 +252,7 @@ describe('schemaFactory', function () { describe('Pagination', function () { - it('returns a GraphQL Pagination object with custom @OrberBy fields', async function () { + it('returns a GraphQL Pagination object with custom @OrderBy fields', async function () { @D.ObjectType() class Obj { @D.Field({ type: GraphQLString, description: 'a field' }) @@ -278,7 +278,7 @@ describe('schemaFactory', function () { assert.deepEqual(validate(schema, ast), []); }); - it('returns a GraphQL Pagination object with custom @OrberBy fields (backwards compatibility)', async function () { + it('returns a GraphQL Pagination object with custom @OrderBy fields (backwards compatibility)', async function () { @D.ObjectType() class Obj { @@ -305,7 +305,7 @@ describe('schemaFactory', function () { assert.deepEqual(validate(schema, ast), []); }); - it('returns a GraphQL Pagination object with custom @OrberBy fields ignoring schema fields', async function () { + it('returns a GraphQL Pagination object with custom @OrderBy fields ignoring schema fields', async function () { @D.ObjectType() class Obj { diff --git a/src/type-factory/field.type-factory.ts b/src/type-factory/field.type-factory.ts index 308485b..f048612 100644 --- a/src/type-factory/field.type-factory.ts +++ b/src/type-factory/field.type-factory.ts @@ -8,14 +8,13 @@ import { import { SchemaFactoryError, SchemaFactoryErrorType } from './schema.type-factory'; import { IoCContainer } from '../ioc-container'; -import { Middleware } from '../middleware'; import { OrderByTypeFactory } from './order-by.type-factory'; import { PaginationType } from '../pagination.type'; import { enumTypeFactory } from './enum.type-factory'; import { getMetadataArgsStorage } from '../metadata-builder'; +import { interfaceTypeFactory } from './interface.type-factory'; import { objectTypeFactory } from './object.type-factory'; import { unionTypeFactory } from './union.type-factory'; -import { interfaceTypeFactory } from './interface.type-factory'; export interface ResolverHolder { fn: Function; @@ -138,11 +137,12 @@ export function resolverFactory( rest[index] = root; } } + + let result: any = null; if (metadata.before) { // TODO: This whole chain should be promise based but this would impact the whole `schemaFactory` call chain. // So Promise will be added as a future feature/enhancement - let result: any = null; let next: (error?: Error) => void = (error?: Error, value?: any): any => { if (error) { throw error; @@ -153,12 +153,24 @@ export function resolverFactory( } }; metadata.before.middleware.call(fieldParentClass, context, args, next); - return result; } else { - return originalFn.apply(fieldParentClass, rest); + result = originalFn.apply(fieldParentClass, rest); + } + + if (metadata.after) { + let next: (error?: Error) => void = (error?: Error, value?: any): any => { + if (error) { + throw error; + } else if (typeof (value) !== 'undefined') { + result = value; + } + return result; + }; + metadata.after.middleware.call(fieldParentClass, context, args, result, next); } - }; + return result; + }; return { fn, From 909335df0adef7b8e684d0103542a60dbf17e0f7 Mon Sep 17 00:00:00 2001 From: Marcelo Risse Date: Thu, 23 Nov 2017 14:10:41 -0200 Subject: [PATCH 2/4] Add result parameter to After JSDoc --- src/decorator/after.decorator.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/decorator/after.decorator.ts b/src/decorator/after.decorator.ts index 56d8272..d1c3468 100644 --- a/src/decorator/after.decorator.ts +++ b/src/decorator/after.decorator.ts @@ -7,7 +7,12 @@ import { getMetadataArgsStorage } from '../metadata-builder'; * * Usage example: * ``` - * let middleware: AfterMiddleware = (context: any, args: { [key: string]: any }, next: (error?: Error, value?: any) => any): any => { + * let middleware: AfterMiddleware = ( + * context: any, + * args: { [key: string]: any }, + * result: Promise | any, + * next: (error?: Error, value?: any) => any + * ): any => { * if(context.user.role != 'any role') { * // can use context and resolve/return value as `1000`, for example, regardless of what resolve function actually implements * next(null, 1000); From 8bfff74f6901399c0477566032fa46eb530406b6 Mon Sep 17 00:00:00 2001 From: Marcelo Risse Date: Thu, 23 Nov 2017 14:27:57 -0200 Subject: [PATCH 3/4] Rename before middleware, move middlewares to folder --- src/decorator/before.decorator.ts | 8 ++++---- src/decorator/field.decorator.ts | 2 +- src/metadata/args/before.arg.ts | 4 ++-- src/metadata/options/before.option.ts | 4 ++-- src/metadata/types/before.metadata.ts | 4 ++-- src/{middleware.ts => middleware/after.middleware.ts} | 2 -- src/middleware/before.middleware.ts | 5 +++++ src/middleware/index.ts | 3 +++ src/{ => middleware}/pagination.middleware.ts | 8 ++++---- src/specs/field.type-factory.spec.ts | 8 ++++---- 10 files changed, 27 insertions(+), 21 deletions(-) rename src/{middleware.ts => middleware/after.middleware.ts} (56%) create mode 100644 src/middleware/before.middleware.ts create mode 100644 src/middleware/index.ts rename src/{ => middleware}/pagination.middleware.ts (81%) diff --git a/src/decorator/before.decorator.ts b/src/decorator/before.decorator.ts index 7ff1169..275d395 100644 --- a/src/decorator/before.decorator.ts +++ b/src/decorator/before.decorator.ts @@ -1,5 +1,5 @@ +import { BeforeMiddleware } from '../middleware'; import { BeforeOption } from '../metadata'; -import { Middleware } from '../middleware'; import { getMetadataArgsStorage } from '../metadata-builder'; /** @@ -7,7 +7,7 @@ import { getMetadataArgsStorage } from '../metadata-builder'; * * Usage example: * ``` - * let middleware: Middleware = (context: any, args: { [key: string]: any }, next: (error?: Error, value?: any) => any): any => { + * let middleware: BeforeMiddleware = (context: any, args: { [key: string]: any }, next: (error?: Error, value?: any) => any): any => { * if(context.user.role != 'any role') { * // can use context and resolve/return value as `1000`, for example, regardless of what resolve function actually implements * next(null, 1000); @@ -30,7 +30,7 @@ import { getMetadataArgsStorage } from '../metadata-builder'; * ``` * @param option Options for an Schema */ -export function Before(option: BeforeOption | Middleware) { +export function Before(option: BeforeOption | BeforeMiddleware) { return function (target: any, propertyKey: any, index: number) { getMetadataArgsStorage().befores.push({ target: target, @@ -38,7 +38,7 @@ export function Before(option: BeforeOption | Middleware) { description: option && (option as BeforeOption).description ? (option as BeforeOption).description : null, index: index, property: propertyKey, - middleware: option && (option as BeforeOption).middleware ? (option as BeforeOption).middleware : option as Middleware, + middleware: option && (option as BeforeOption).middleware ? (option as BeforeOption).middleware : option as BeforeMiddleware, }); } as Function; } diff --git a/src/decorator/field.decorator.ts b/src/decorator/field.decorator.ts index 205460c..0823739 100644 --- a/src/decorator/field.decorator.ts +++ b/src/decorator/field.decorator.ts @@ -1,7 +1,7 @@ import { SchemaFactoryError, SchemaFactoryErrorType } from '../type-factory'; import { FieldOption } from '../metadata'; -import { PaginationMiddleware } from '../pagination.middleware'; +import { PaginationMiddleware } from '../middleware'; import { getMetadataArgsStorage } from '../metadata-builder'; /** diff --git a/src/metadata/args/before.arg.ts b/src/metadata/args/before.arg.ts index ec77afa..77964e0 100644 --- a/src/metadata/args/before.arg.ts +++ b/src/metadata/args/before.arg.ts @@ -1,8 +1,8 @@ import { Argument } from './argument'; -import { Middleware } from '../../middleware'; +import { BeforeMiddleware } from '../../middleware'; export interface BeforeArg extends Argument { index: number; property: string; - middleware: Middleware; + middleware: BeforeMiddleware; } diff --git a/src/metadata/options/before.option.ts b/src/metadata/options/before.option.ts index 46913a5..459c512 100644 --- a/src/metadata/options/before.option.ts +++ b/src/metadata/options/before.option.ts @@ -1,4 +1,4 @@ -import { Middleware } from '../../middleware'; +import { BeforeMiddleware } from '../../middleware'; import { Option } from './option'; /** @@ -9,5 +9,5 @@ export interface BeforeOption extends Option { * Middeware to change resolver behavior. * Check decorator docs for example usage. */ - middleware: Middleware; + middleware: BeforeMiddleware; } diff --git a/src/metadata/types/before.metadata.ts b/src/metadata/types/before.metadata.ts index a564f57..1c44df5 100644 --- a/src/metadata/types/before.metadata.ts +++ b/src/metadata/types/before.metadata.ts @@ -1,6 +1,6 @@ +import { BeforeMiddleware } from '../../middleware'; import { FieldArgumentMetadata } from './field-argument.metadata'; -import { Middleware } from '../../middleware'; export interface BeforeMetadata extends FieldArgumentMetadata { - middleware: Middleware; + middleware: BeforeMiddleware; } diff --git a/src/middleware.ts b/src/middleware/after.middleware.ts similarity index 56% rename from src/middleware.ts rename to src/middleware/after.middleware.ts index b87d2d7..a639a57 100644 --- a/src/middleware.ts +++ b/src/middleware/after.middleware.ts @@ -1,5 +1,3 @@ -export type Middleware = (context: any, args: { [key: string]: any }, next: (error?: Error, value?: any) => any) => Promise | any; - export type AfterMiddleware = ( context: any, args: { [key: string]: any }, diff --git a/src/middleware/before.middleware.ts b/src/middleware/before.middleware.ts new file mode 100644 index 0000000..7ddf1a3 --- /dev/null +++ b/src/middleware/before.middleware.ts @@ -0,0 +1,5 @@ +export type BeforeMiddleware = ( + context: any, + args: { [key: string]: any }, + next: (error?: Error, value?: any) => any, +) => Promise | any; diff --git a/src/middleware/index.ts b/src/middleware/index.ts new file mode 100644 index 0000000..c0998e5 --- /dev/null +++ b/src/middleware/index.ts @@ -0,0 +1,3 @@ +export * from './after.middleware'; +export * from './before.middleware'; +export * from './pagination.middleware'; diff --git a/src/pagination.middleware.ts b/src/middleware/pagination.middleware.ts similarity index 81% rename from src/pagination.middleware.ts rename to src/middleware/pagination.middleware.ts index 9862695..ff4e8f8 100644 --- a/src/pagination.middleware.ts +++ b/src/middleware/pagination.middleware.ts @@ -1,7 +1,7 @@ -import { FieldMetadata } from './metadata'; -import { PageInfo } from './page-info.type'; -import { PaginationResponse } from './pagination.type'; -import { getMetadataBuilder } from './metadata-builder'; +import { FieldMetadata } from '../metadata'; +import { PageInfo } from '../page-info.type'; +import { PaginationResponse } from '../pagination.type'; +import { getMetadataBuilder } from '../metadata-builder'; export function PaginationMiddleware(target: any, propertyKey: string, methodDescriptor: TypedPropertyDescriptor): any { return { diff --git a/src/specs/field.type-factory.spec.ts b/src/specs/field.type-factory.spec.ts index fdb89af..9e4a79b 100644 --- a/src/specs/field.type-factory.spec.ts +++ b/src/specs/field.type-factory.spec.ts @@ -5,8 +5,8 @@ import * as graphql from 'graphql'; import { clearFieldTypeCache, clearObjectTypeRepository, fieldTypeFactory, resolverFactory } from '../type-factory'; +import { BeforeMiddleware } from '../middleware'; import { FieldMetadata } from '../metadata'; -import { Middleware } from '../middleware'; import { getMetadataBuilder } from '../metadata-builder'; const assert = require('assert'); @@ -80,7 +80,7 @@ describe('field specs', function () { it('makes sure Before is executed before resolving', function (done) { - let middleware: Middleware = (context: any, args: { [key: string]: any }, next: (error?: Error, value?: any) => any): any => { + let middleware: BeforeMiddleware = (context: any, args: { [key: string]: any }, next: (error?: Error, value?: any) => any): any => { assert(true); done(); }; @@ -92,7 +92,7 @@ describe('field specs', function () { it('makes sure middleware can override function execution if next is called with a value', function () { - let middleware: Middleware = (context: any, args: { [key: string]: any }, next: (error?: Error, value?: any) => any): any => { + let middleware: BeforeMiddleware = (context: any, args: { [key: string]: any }, next: (error?: Error, value?: any) => any): any => { next(null, 5); }; class Obj { @D.Field() @D.Before(middleware) twice(input: number): number { return input * 2; } } @@ -105,7 +105,7 @@ describe('field specs', function () { // tslint:disable-next-line:max-line-length it('makes sure middleware can override function execution if next is called with a value even if it is null (as long it ir not undefined)', function () { - let middleware: Middleware = (context: any, args: { [key: string]: any }, next: (error?: Error, value?: any) => any): any => { + let middleware: BeforeMiddleware = (context: any, args: { [key: string]: any }, next: (error?: Error, value?: any) => any): any => { next(null, null); }; class Obj { @D.Field() @D.Before(middleware) twice(input: number): number { return input * 2; } } From d65938feb8f6a5d5d6fdce2426a947d69493a62c Mon Sep 17 00:00:00 2001 From: Marcelo Risse Date: Thu, 23 Nov 2017 14:34:32 -0200 Subject: [PATCH 4/4] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 79ff00b..536e828 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ - Fixed `@Root` decorator now being correctly exported - #51 - [@babbarankit](https://github.com/babbarankit) - Fixed README examples - #51 - [@babbarankit](https://github.com/babbarankit) - Improved tests by adding @Root ones - #52 - [@babbarankit](https://github.com/babbarankit) +- Added @InterfaceType decorator - #46 - [@felipesabino](https://github.com/felipesabino) +- Added @After middleware decorator - #56 - [@marcelorisse](https://github.com/marcelorisse) ### Breaking changes