diff --git a/ROADMAP.md b/ROADMAP.md index a0e27b1..245bd91 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -29,6 +29,8 @@ type User { } ``` +### Reorganize, rewrite specs + ### Infer basic types from ts metadata - investigate if it makes sense, because this feature would be very limited - only for couple of types. diff --git a/lib/decorators/fields.ts b/lib/decorators/fields.ts index cecabc9..d613818 100644 --- a/lib/decorators/fields.ts +++ b/lib/decorators/fields.ts @@ -1,7 +1,6 @@ import {GraphQLFieldResolver, GraphQLID} from "graphql"; -import {FieldType} from "../types-conversion/TypeProxy"; -import {ArgsType} from "../types-conversion/ArgumentsTypeProxy"; import {createFieldDecorator} from "./helpers"; +import {ArgsType, FieldType} from "../fields-metadata/FieldConfig"; export const id = ():PropertyDecorator => createFieldDecorator(fieldConfig => { fieldConfig.setType(GraphQLID) diff --git a/lib/factories/createSchema.ts b/lib/factories/createSchema.ts index e3e4ccf..72ddbdd 100644 --- a/lib/factories/createSchema.ts +++ b/lib/factories/createSchema.ts @@ -1,19 +1,11 @@ -import {GraphQLSchema} from "graphql"; -import {someOrThrow} from "../utils/core"; -import {TypeMetadata} from "../types-metadata/TypeMetadata"; +import {GraphQLObjectType, GraphQLSchema} from "graphql"; +import {typesResolver} from "../types-conversion/TypesResolver"; export function createSchema(annotatedRootClass, annotatedMutationClass):GraphQLSchema { - let query = someOrThrow( - TypeMetadata.getForClass(annotatedRootClass), - 'Class provided as query root is not decorated with @type'); - let mutation = someOrThrow( - TypeMetadata.getForClass(annotatedMutationClass), - 'Class provided as query root is not decorated with @type'); - return new GraphQLSchema({ - query: query.toGraphQLObjectType(), - mutation: mutation.toGraphQLObjectType() + query: typesResolver.toGraphQLType(annotatedRootClass) as GraphQLObjectType, + mutation: typesResolver.toGraphQLType(annotatedMutationClass) as GraphQLObjectType }) } \ No newline at end of file diff --git a/lib/factories/createUnion.ts b/lib/factories/createUnion.ts index 9fed688..a22ab67 100644 --- a/lib/factories/createUnion.ts +++ b/lib/factories/createUnion.ts @@ -1,13 +1,13 @@ import {GraphQLObjectType, GraphQLUnionType, Thunk} from "graphql"; import * as _ from 'lodash'; -import {TypeProxy} from "../types-conversion/TypeProxy"; +import {typesResolver} from "../types-conversion/TypesResolver"; //TODO: rename to - unionType export function createUnion(name:string, types:Thunk>, resolveType):GraphQLUnionType { return new GraphQLUnionType({ name, - types: _.castArray(types).map(type => TypeProxy.inferType(type)) as any, //TODO: fix types - resolveType: (value, context) => TypeProxy.inferType(resolveType(value, context)) as any //TODO: fix types + types: _.castArray(types).map(type => typesResolver.toGraphQLType(type)) as any, //TODO: fix types + resolveType: (value, context) => typesResolver.toGraphQLType(resolveType(value, context)) as any //TODO: fix types }); } \ No newline at end of file diff --git a/lib/fields-metadata/FieldConfig.ts b/lib/fields-metadata/FieldConfig.ts index 57e6dc5..0802c73 100644 --- a/lib/fields-metadata/FieldConfig.ts +++ b/lib/fields-metadata/FieldConfig.ts @@ -1,18 +1,28 @@ -import {FieldType, TypeProxy} from "../types-conversion/TypeProxy"; -import {TypeWrapper} from "../types-conversion/TypeWrapper"; +import {TypeValue} from "./TypeValue"; +import {TypeWrapper} from "./TypeWrapper"; import { - GraphQLArgumentConfig, GraphQLFieldConfig, GraphQLFieldResolver, GraphQLInputFieldConfig, - GraphQLInputType, GraphQLOutputType + GraphQLArgumentConfig, GraphQLFieldConfig, GraphQLFieldConfigArgumentMap, GraphQLFieldResolver, + GraphQLInputFieldConfig, + GraphQLInputType, GraphQLOutputType, GraphQLType } from "graphql"; -import {ArgsType, ArgumentsTypeProxy} from "../types-conversion/ArgumentsTypeProxy"; +import {ITypeResolver} from "../types-conversion/abstract/ITypeResolver"; +import {ClassType} from "../utils/types"; +export type FieldType = + GraphQLType | + ClassType | + Object; + +export type ArgsType = + GraphQLFieldConfigArgumentMap | + ClassType //TODO: throw errors from setters for wrong data export class FieldConfig { - private type = new TypeProxy(); + private type = new TypeValue(); + private args:TypeValue; private compositeType:TypeWrapper = new TypeWrapper(); private description:string; - private args:ArgumentsTypeProxy; private resolveFn:GraphQLFieldResolver; private defaultValue; @@ -21,12 +31,12 @@ export class FieldConfig { } setParamsType(paramsFieldType:ArgsType) { - this.args = new ArgumentsTypeProxy(); + this.args = new TypeValue(); this.args.setType(paramsFieldType); } setParamsThunk(paramsFieldTypeThunk:() => ArgsType) { - this.args = new ArgumentsTypeProxy(); + this.args = new TypeValue(); this.args.setTypeThunk(paramsFieldTypeThunk); } @@ -64,8 +74,9 @@ export class FieldConfig { this.description = name; } - toGraphQLInputFieldConfig():GraphQLArgumentConfig | GraphQLInputFieldConfig { - let type = this.type.toGraphQLType() as GraphQLInputType; + //TODO: extract this to type resolver + toGraphQLInputFieldConfig(typeResolver:ITypeResolver):GraphQLArgumentConfig | GraphQLInputFieldConfig { + let type = typeResolver.toGraphQLType(this.type.inferType()) as GraphQLInputType; type = this.compositeType.wrap(type) as GraphQLInputType; return { @@ -75,13 +86,14 @@ export class FieldConfig { } } - toGraphQLFieldConfig():GraphQLFieldConfig { - let type = this.type.toGraphQLType(); + //TODO: extract this to type resolver + toGraphQLFieldConfig(typeResolver:ITypeResolver, argsResolver:ITypeResolver):GraphQLFieldConfig { + let type = typeResolver.toGraphQLType(this.type.inferType()); type = this.compositeType.wrap(type) as GraphQLOutputType; return { type, - args: this.args && this.args.toGraphQLType() as any, + args: this.args && argsResolver.toGraphQLType(this.args.inferType()) as any, resolve: this.resolveFn, description: this.description } diff --git a/lib/fields-metadata/FieldsMetadata.ts b/lib/fields-metadata/FieldsMetadata.ts index 19d8403..5455716 100644 --- a/lib/fields-metadata/FieldsMetadata.ts +++ b/lib/fields-metadata/FieldsMetadata.ts @@ -1,9 +1,6 @@ import 'reflect-metadata'; import {metadataGet, metadataGetOrSet} from "../utils/metadataFactories"; import {FieldConfig} from "./FieldConfig"; -import {GraphQLFieldConfigMap, GraphQLInputFieldConfigMap} from "graphql"; -import {propagateErrorWithContext} from "../utils/core"; -import * as _ from "lodash"; const FIELDS_METADATA_KEY = '__FIELDS_METADATA_KEY'; @@ -21,16 +18,4 @@ export class FieldsMetadata { getFields():{ [fieldName:string]:FieldConfig } { return this._fields; } - - toGraphQLFieldConfigMap():GraphQLFieldConfigMap { - return _.mapValues(this.getFields(), (fieldConfig, fieldName) => { - return propagateErrorWithContext(fieldName, () => fieldConfig.toGraphQLFieldConfig()) - }); - } - - toGraphQLInputFieldConfigMap():GraphQLInputFieldConfigMap{ - return _.mapValues(this.getFields(), (fieldConfig, fieldName) => { - return propagateErrorWithContext(`fieldName: ${fieldName}`, () => fieldConfig.toGraphQLInputFieldConfig()) - }); - } } \ No newline at end of file diff --git a/lib/fields-metadata/TypeValue.ts b/lib/fields-metadata/TypeValue.ts new file mode 100644 index 0000000..82a25b1 --- /dev/null +++ b/lib/fields-metadata/TypeValue.ts @@ -0,0 +1,19 @@ +import {Thunk} from "graphql"; +import {resolveThunk} from "../utils/graphql"; + +export class TypeValue { + private _type:T; + private _typeThunk:Thunk; + + setType(type:T) { + this._type = type; + } + + setTypeThunk(typeThunk:Thunk) { + this._typeThunk = typeThunk; + } + + inferType() { + return this._type || resolveThunk(this._typeThunk); + } +} \ No newline at end of file diff --git a/lib/types-conversion/TypeWrapper.ts b/lib/fields-metadata/TypeWrapper.ts similarity index 100% rename from lib/types-conversion/TypeWrapper.ts rename to lib/fields-metadata/TypeWrapper.ts diff --git a/lib/types-conversion/ArgumentsTypeProxy.ts b/lib/types-conversion/ArgumentsTypeProxy.ts deleted file mode 100644 index 57fc86e..0000000 --- a/lib/types-conversion/ArgumentsTypeProxy.ts +++ /dev/null @@ -1,36 +0,0 @@ -import {GraphQLFieldConfigArgumentMap, Thunk} from "graphql"; - -import * as _ from "lodash"; -import {resolveThunk} from "../utils/graphql"; -import {ParamsMetadata} from "../types-metadata/ParamsMetadata"; -import {invariant} from "../utils/core"; -import {ClassType} from "../utils/types"; - -export type ArgsType = - GraphQLFieldConfigArgumentMap | - ClassType - -export class ArgumentsTypeProxy { - private _type:ArgsType; - private _typeThunk:Thunk; - - setType(type:ArgsType) { - this._type = type; - } - - setTypeThunk(typeThunk:Thunk) { - this._typeThunk = typeThunk; - } - - toGraphQLType():GraphQLFieldConfigArgumentMap { - let type = (this._type || resolveThunk(this._typeThunk)) as GraphQLFieldConfigArgumentMap; - - if (_.isFunction(type)) { - let metadata = ParamsMetadata.getForClass(type); - invariant(!!metadata, `Missing ParamsMetadata for ${type}. Decorate class using @params decorator`); - return metadata.toGraphQLFieldConfigArgumentMap(); - } - - throw new Error(`Cannot infer type for ${JSON.stringify(type)}`) - } -} \ No newline at end of file diff --git a/lib/types-conversion/ArgumentsTypeResolver.ts b/lib/types-conversion/ArgumentsTypeResolver.ts new file mode 100644 index 0000000..25b1bc9 --- /dev/null +++ b/lib/types-conversion/ArgumentsTypeResolver.ts @@ -0,0 +1,24 @@ +import {invariant} from "../utils/core"; +import * as _ from "lodash"; +import {ITypeResolver} from "./abstract/ITypeResolver"; +import {ParamsMetadata} from "../types-metadata/ParamsMetadata"; +import {ArgumentMapResolver} from "./input/ArgumentMapResolver"; + +export class ArgumentsTypeResolver implements ITypeResolver { + private argumentsMapBuilder:ArgumentMapResolver; + + constructor(protected typesResolver:ITypeResolver) { + this.argumentsMapBuilder = new ArgumentMapResolver(this.typesResolver, this); + } + + toGraphQLType(type):any { + if (_.isFunction(type)) { + let metadata:any = ParamsMetadata.getForClass(type); + invariant(!!metadata, `Missing ParamsMetadata for ${type}. Decorate class using @params decorator`); + + return this.argumentsMapBuilder.toGraphQLType(metadata); + } + + throw new Error(`Cannot infer type for ${JSON.stringify(type)}`) + } +} \ No newline at end of file diff --git a/lib/types-conversion/TypeProxy.ts b/lib/types-conversion/TypeProxy.ts deleted file mode 100644 index ed0678d..0000000 --- a/lib/types-conversion/TypeProxy.ts +++ /dev/null @@ -1,77 +0,0 @@ -import {enumsRegistry} from "../registry/typesRegistry"; -import {GraphQLType, isType, Thunk} from "graphql"; -import * as _ from "lodash"; -import {resolveThunk} from "../utils/graphql"; -import {TypeMetadata} from "../types-metadata/TypeMetadata"; -import {invariant} from "../utils/core"; -import {getMetadata} from "../utils/metadata"; -import {ParamsMetadata} from "../types-metadata/ParamsMetadata"; -import {InterfaceTypeMetadata} from "../types-metadata/InterfaceTypeMetadata"; -import {ClassType} from "../utils/types"; - -export type FieldType = - GraphQLType | - ClassType | - Object; - -export class TypeProxy { - static inferType(type:FieldType):GraphQLType { - let proxy = new TypeProxy(); - proxy.setType(type); - return proxy.toGraphQLType() - } - - static inferTypeThunk(typeThunk:() => FieldType):GraphQLType { - let proxy = new TypeProxy(); - proxy.setTypeThunk(typeThunk); - return proxy.toGraphQLType() - } - - private _type:FieldType; - private _typeThunk:Thunk; - - setType(type:FieldType) { - this._type = type; - } - - setTypeThunk(typeThunk:Thunk) { - this._typeThunk = typeThunk; - } - - toGraphQLType():GraphQLType { - return this.inferType(); - } - - private inferType():GraphQLType { - let type = this._type || resolveThunk(this._typeThunk); - - if (isType(type)) { - return type; - } - - if (enumsRegistry.hasEnum(type)) { - return enumsRegistry.getGraphQLEnumType(type); - } - - if (_.isFunction(type)) { - let metadata = getMetadata(type as any); - invariant(!!metadata, `Missing TypeMetadata for ${type}. Decorate class using @type decorator.`); - - if (metadata instanceof ParamsMetadata) { - return metadata.toGraphQLInputObjectType(); - } - - if (metadata instanceof TypeMetadata) { - return metadata.toGraphQLObjectType(); - } - - if (metadata instanceof InterfaceTypeMetadata) { - return metadata.toGraphQLInterfaceType(); - } - - throw new Error(`Unknown metadata found ${metadata}`) - } - - throw new Error(`Cannot infer type for ${JSON.stringify(type)}`) - } -} \ No newline at end of file diff --git a/lib/types-conversion/TypesResolver.ts b/lib/types-conversion/TypesResolver.ts new file mode 100644 index 0000000..1fd1070 --- /dev/null +++ b/lib/types-conversion/TypesResolver.ts @@ -0,0 +1,69 @@ +import {TypeMetadata} from "../types-metadata/TypeMetadata"; +import {InterfaceTypeMetadata} from "../types-metadata/InterfaceTypeMetadata"; +import {enumsRegistry} from "../registry/typesRegistry"; +import {invariant} from "../utils/core"; +import {GraphQLType, isType} from "graphql"; +import * as _ from "lodash"; +import {getMetadata} from "../utils/metadata"; +import {ParamsMetadata} from "../types-metadata/ParamsMetadata"; +import {ITypeResolver} from "./abstract/ITypeResolver"; +import {ArgumentsTypeResolver} from "./ArgumentsTypeResolver"; +import {ObjectTypeResolver} from "./output/ObjectTypeResolver"; +import {InterfaceTypeResolver} from "./output/InterfaceTypeResolver"; +import {InputObjectTypeResolver} from "./input/InputObjectTypeResolver"; +import {ArgsType, FieldType} from "../fields-metadata/FieldConfig"; + +export type TypeResolversParams = { + inputObjectTypeResolver:ITypeResolver, + objectTypeResolver:ITypeResolver, + interfaceTypeResolver:ITypeResolver, + argumentTypeResolver:ITypeResolver +} + +export class TypesResolver implements ITypeResolver { + private argumentTypeResolver:ArgumentsTypeResolver; + private inputObjectTypeResolver:InputObjectTypeResolver; + private objectTypeResolver:ObjectTypeResolver; + private interfaceTypeResolver:InterfaceTypeResolver; + + constructor(builders:Partial = {}) { + this.argumentTypeResolver = builders.argumentTypeResolver || new ArgumentsTypeResolver(this) as any; + this.inputObjectTypeResolver = builders.inputObjectTypeResolver || new InputObjectTypeResolver(this, this.argumentTypeResolver) as any; + this.objectTypeResolver = builders.objectTypeResolver || new ObjectTypeResolver(this, this.argumentTypeResolver) as any; + this.interfaceTypeResolver = builders.interfaceTypeResolver || new InterfaceTypeResolver(this, this.argumentTypeResolver) as any; + } + + toGraphQLType(type:FieldType | ArgsType):GraphQLType { + if (isType(type)) { + return type; + } + + if (enumsRegistry.hasEnum(type)) { + return enumsRegistry.getGraphQLEnumType(type); + } + + if (_.isFunction(type)) { + let metadata = getMetadata(type as any); + invariant(!!metadata, `Missing TypeMetadata for ${type}. Decorate class using @type decorator.`); + + if (metadata instanceof ParamsMetadata) { + return this.inputObjectTypeResolver.toGraphQLType(metadata); + } + + if (metadata instanceof TypeMetadata) { + return this.objectTypeResolver.toGraphQLType(metadata); + } + + if (metadata instanceof InterfaceTypeMetadata) { + return this.interfaceTypeResolver.toGraphQLType(metadata); + } + + throw new Error(`Unknown metadata found ${metadata}`) + } + + throw new Error(`Cannot infer type for ${JSON.stringify(type)}`) + } +} + +export const typesResolver = new TypesResolver(); + diff --git a/lib/types-conversion/abstract/AbstractTypeResolver.ts b/lib/types-conversion/abstract/AbstractTypeResolver.ts new file mode 100644 index 0000000..55fdda5 --- /dev/null +++ b/lib/types-conversion/abstract/AbstractTypeResolver.ts @@ -0,0 +1,10 @@ +import {ITypeResolver} from "./ITypeResolver"; +import * as _ from 'lodash'; + +export abstract class AbstractTypeResolver { + constructor(protected typeResolver:ITypeResolver, protected argsTypeResolver:ITypeResolver) { + this.toGraphQLType = _.memoize(this.toGraphQLType); + } + + abstract toGraphQLType(target:IN):O +} \ No newline at end of file diff --git a/lib/types-conversion/abstract/ITypeResolver.ts b/lib/types-conversion/abstract/ITypeResolver.ts new file mode 100644 index 0000000..5a26e6d --- /dev/null +++ b/lib/types-conversion/abstract/ITypeResolver.ts @@ -0,0 +1,3 @@ +export interface ITypeResolver { + toGraphQLType(target:IN):O +} \ No newline at end of file diff --git a/lib/types-conversion/input/ArgumentMapResolver.ts b/lib/types-conversion/input/ArgumentMapResolver.ts new file mode 100644 index 0000000..753c7df --- /dev/null +++ b/lib/types-conversion/input/ArgumentMapResolver.ts @@ -0,0 +1,15 @@ +import {propagateErrorWithContext} from "../../utils/core"; +import {ParamsMetadata} from "../../types-metadata/ParamsMetadata"; +import {AbstractTypeResolver} from "../abstract/AbstractTypeResolver"; +import {InputFieldConfigMapResolver} from "./InputFieldConfigMapResolver"; +import {GraphQLFieldConfigArgumentMap} from "graphql"; + +export class ArgumentMapResolver extends AbstractTypeResolver { + private inputFieldConfigMapBuilder:InputFieldConfigMapResolver = new InputFieldConfigMapResolver(this.typeResolver, this.argsTypeResolver); + + toGraphQLType(metadata:ParamsMetadata):GraphQLFieldConfigArgumentMap { + return propagateErrorWithContext(metadata.config.get('name'), () => { + return this.inputFieldConfigMapBuilder.toGraphQLType(metadata.getFieldsMetadata()) + }) + } +} \ No newline at end of file diff --git a/lib/types-conversion/input/InputFieldConfigMapResolver.ts b/lib/types-conversion/input/InputFieldConfigMapResolver.ts new file mode 100644 index 0000000..0eda34b --- /dev/null +++ b/lib/types-conversion/input/InputFieldConfigMapResolver.ts @@ -0,0 +1,14 @@ +import {propagateErrorWithContext} from "../../utils/core"; +import * as _ from "lodash"; +import {GraphQLInputFieldConfigMap} from "graphql"; +import {FieldsMetadata} from "../../fields-metadata/FieldsMetadata"; +import {AbstractTypeResolver} from "../abstract/AbstractTypeResolver"; + + +export class InputFieldConfigMapResolver extends AbstractTypeResolver { + toGraphQLType(fieldsMetadata:FieldsMetadata):GraphQLInputFieldConfigMap { + return _.mapValues(fieldsMetadata.getFields(), (fieldConfig, fieldName) => { + return propagateErrorWithContext(`fieldName: ${fieldName}`, () => fieldConfig.toGraphQLInputFieldConfig(this.typeResolver)) + }); + } +} \ No newline at end of file diff --git a/lib/types-conversion/input/InputObjectTypeResolver.ts b/lib/types-conversion/input/InputObjectTypeResolver.ts new file mode 100644 index 0000000..124d3cf --- /dev/null +++ b/lib/types-conversion/input/InputObjectTypeResolver.ts @@ -0,0 +1,20 @@ +import {propagateErrorWithContext} from "../../utils/core"; +import {GraphQLInputObjectType} from "graphql"; +import {ParamsMetadata} from "../../types-metadata/ParamsMetadata"; +import {AbstractTypeResolver} from "../abstract/AbstractTypeResolver"; +import {InputFieldConfigMapResolver} from "./InputFieldConfigMapResolver"; + +export class InputObjectTypeResolver extends AbstractTypeResolver { + private inputFieldConfigMapBuilder:InputFieldConfigMapResolver = new InputFieldConfigMapResolver(this.typeResolver, this.argsTypeResolver); + + toGraphQLType(metadata:ParamsMetadata):GraphQLInputObjectType { + return propagateErrorWithContext(metadata.config.get('name'), () => { + let fieldsThunk = () => this.inputFieldConfigMapBuilder.toGraphQLType(metadata.getFieldsMetadata()); + return new GraphQLInputObjectType({ + name: metadata.config.get('name'), + description: metadata.config.get('description'), + fields: fieldsThunk + }) + }) + } +} \ No newline at end of file diff --git a/lib/types-conversion/output/FieldConfigMapResolver.ts b/lib/types-conversion/output/FieldConfigMapResolver.ts new file mode 100644 index 0000000..9050731 --- /dev/null +++ b/lib/types-conversion/output/FieldConfigMapResolver.ts @@ -0,0 +1,14 @@ +import {propagateErrorWithContext} from "../../utils/core"; +import * as _ from "lodash"; +import {FieldsMetadata} from "../../fields-metadata/FieldsMetadata"; +import {GraphQLFieldConfigMap} from "graphql"; +import {AbstractTypeResolver} from "../abstract/AbstractTypeResolver"; + + +export class FieldConfigMapResolver extends AbstractTypeResolver> { + toGraphQLType(fieldsMetadata:FieldsMetadata):GraphQLFieldConfigMap { + return _.mapValues(fieldsMetadata.getFields(), (fieldConfig, fieldName) => { + return propagateErrorWithContext(fieldName, () => fieldConfig.toGraphQLFieldConfig(this.typeResolver, this.argsTypeResolver)) + }); + } +} \ No newline at end of file diff --git a/lib/types-conversion/output/InterfaceTypeResolver.ts b/lib/types-conversion/output/InterfaceTypeResolver.ts new file mode 100644 index 0000000..705bb45 --- /dev/null +++ b/lib/types-conversion/output/InterfaceTypeResolver.ts @@ -0,0 +1,24 @@ +import {InterfaceTypeMetadata} from "../../types-metadata/InterfaceTypeMetadata"; +import {GraphQLInterfaceType, GraphQLObjectType, GraphQLTypeResolver} from "graphql"; +import {isPresent} from "../../utils/core"; +import {AbstractTypeResolver} from "../abstract/AbstractTypeResolver"; +import {FieldConfigMapResolver} from "./FieldConfigMapResolver"; + +export class InterfaceTypeResolver extends AbstractTypeResolver { + private fieldConfigMapBuilder:FieldConfigMapResolver = new FieldConfigMapResolver(this.typeResolver, this.argsTypeResolver); + + toGraphQLType(metadata:InterfaceTypeMetadata):GraphQLInterfaceType { + let resolveType:GraphQLTypeResolver = isPresent(metadata.config.get('resolveType')) ? + (value, context, info) => this.typeResolver.toGraphQLType(metadata.config.get('resolveType')(value, context, info)) as GraphQLObjectType : + null; + + let fieldsThunk = () => this.fieldConfigMapBuilder.toGraphQLType(metadata.getFieldsMetadata()); + + return new GraphQLInterfaceType({ + name: metadata.config.get('name'), + description: metadata.config.get('description'), + resolveType, + fields: fieldsThunk + }) + } +} \ No newline at end of file diff --git a/lib/types-conversion/output/ObjectTypeResolver.ts b/lib/types-conversion/output/ObjectTypeResolver.ts new file mode 100644 index 0000000..0a4e6ce --- /dev/null +++ b/lib/types-conversion/output/ObjectTypeResolver.ts @@ -0,0 +1,27 @@ +import {TypeMetadata} from "../../types-metadata/TypeMetadata"; +import {GraphQLInterfaceType, GraphQLObjectType} from "graphql"; +import {isPresent, propagateErrorWithContext} from "../../utils/core"; +import {resolveThunk} from "../../utils/graphql"; +import {AbstractTypeResolver} from "../abstract/AbstractTypeResolver"; +import {FieldConfigMapResolver} from "./FieldConfigMapResolver"; + +export class ObjectTypeResolver extends AbstractTypeResolver { + private fieldConfigMapBuilder:FieldConfigMapResolver = new FieldConfigMapResolver(this.typeResolver, this.argsTypeResolver); + + toGraphQLType(metadata:TypeMetadata):GraphQLObjectType { + return propagateErrorWithContext(metadata.config.get('name'), () => { + let fieldsThunk = () => this.fieldConfigMapBuilder.toGraphQLType(metadata.getFieldsMetadata()); + + let interfaces = isPresent(metadata.config.get('interfaces')) ? + resolveThunk(metadata.config.get('interfaces')).map(type => this.typeResolver.toGraphQLType(type) as GraphQLInterfaceType) : + null; + + return new GraphQLObjectType({ + name: metadata.config.get('name'), + description: metadata.config.get('description'), + interfaces, + fields: fieldsThunk + }) + }) + } +} \ No newline at end of file diff --git a/lib/types-metadata/BaseTypeMetadata.ts b/lib/types-metadata/BaseTypeMetadata.ts index 7a6d2bb..2f19c47 100644 --- a/lib/types-metadata/BaseTypeMetadata.ts +++ b/lib/types-metadata/BaseTypeMetadata.ts @@ -1,11 +1,11 @@ import {ConfigData} from "../utils/ConfigData"; import {FieldsMetadata} from "../fields-metadata/FieldsMetadata"; import {invariant} from "../utils/core"; +import {IBaseMetadata} from "./IBaseMetadata"; -export const GRAPHQL_METADATA_KEY:string = '__GRAPHQL_METADATA'; -export class BaseTypeMetadata { - protected config:ConfigData = new ConfigData(); +export class BaseTypeMetadata implements IBaseMetadata{ + config:ConfigData = new ConfigData(); constructor(protected klass) { this.config.set('name', this.klass.name); @@ -15,7 +15,7 @@ export class BaseTypeMetadata { this.config.setConfig(config); } - protected getFieldsMetadata():FieldsMetadata { + getFieldsMetadata():FieldsMetadata { let fieldsMetadata = FieldsMetadata.getForClass(this.klass); invariant(!!fieldsMetadata, `Missing fields definition for ${this.klass.constructor.name}, ${this.klass}`); diff --git a/lib/types-metadata/IBaseMetadata.ts b/lib/types-metadata/IBaseMetadata.ts new file mode 100644 index 0000000..56e382e --- /dev/null +++ b/lib/types-metadata/IBaseMetadata.ts @@ -0,0 +1,5 @@ +export const GRAPHQL_METADATA_KEY:string = '__GRAPHQL_METADATA'; + +export interface IBaseMetadata { + setConfig(config:Partial) +} \ No newline at end of file diff --git a/lib/types-metadata/InterfaceTypeMetadata.ts b/lib/types-metadata/InterfaceTypeMetadata.ts index e1ec164..e68c11d 100644 --- a/lib/types-metadata/InterfaceTypeMetadata.ts +++ b/lib/types-metadata/InterfaceTypeMetadata.ts @@ -1,10 +1,9 @@ import 'reflect-metadata'; -import {isPresent} from "../utils/core"; import {metadataGet, metadataGetOrSet} from "../utils/metadataFactories"; -import {GraphQLInterfaceType, GraphQLObjectType, GraphQLResolveInfo, GraphQLTypeResolver} from "graphql"; +import {GraphQLResolveInfo, GraphQLTypeResolver} from "graphql"; import {ClassType} from "../utils/types"; -import {TypeProxy} from "../types-conversion/TypeProxy"; -import {BaseTypeMetadata, GRAPHQL_METADATA_KEY} from "./BaseTypeMetadata"; +import {BaseTypeMetadata} from "./BaseTypeMetadata"; +import {GRAPHQL_METADATA_KEY} from "./IBaseMetadata"; export type TypeResolverForAnnotatedClass = (value:TSource, context:TContext, @@ -19,27 +18,4 @@ export interface InterfaceTypeConfig { export class InterfaceTypeMetadata extends BaseTypeMetadata { static getForClass = metadataGet(GRAPHQL_METADATA_KEY); static getOrCreateForClass = metadataGetOrSet(GRAPHQL_METADATA_KEY, InterfaceTypeMetadata); - - private _memoizedInterfaceType:GraphQLInterfaceType; - - toGraphQLInterfaceType():GraphQLInterfaceType { - return this._memoizedInterfaceType || (this._memoizedInterfaceType = this.buildGraphQLInterfaceType()); - } - - private buildGraphQLInterfaceType():GraphQLInterfaceType { - - //TODO: create some type proxy class - let resolveType:GraphQLTypeResolver = isPresent(this.config.get('resolveType')) ? - (value, context, info) => TypeProxy.inferType(this.config.get('resolveType')(value, context, info)) as GraphQLObjectType : - null; - - let fieldsThunk = () => this.getFieldsMetadata().toGraphQLFieldConfigMap(); - - return new GraphQLInterfaceType({ - name: this.config.get('name'), - description: this.config.get('description'), - resolveType, - fields: fieldsThunk - }) - } } \ No newline at end of file diff --git a/lib/types-metadata/ParamsMetadata.ts b/lib/types-metadata/ParamsMetadata.ts index 67d381a..c484828 100644 --- a/lib/types-metadata/ParamsMetadata.ts +++ b/lib/types-metadata/ParamsMetadata.ts @@ -1,7 +1,6 @@ -import {BaseTypeMetadata, GRAPHQL_METADATA_KEY} from "./BaseTypeMetadata"; -import {GraphQLFieldConfigArgumentMap, GraphQLInputObjectType} from "graphql"; +import {BaseTypeMetadata} from "./BaseTypeMetadata"; import {metadataGet, metadataGetOrSet} from "../utils/metadataFactories"; -import {propagateErrorWithContext} from "../utils/core"; +import {GRAPHQL_METADATA_KEY} from "./IBaseMetadata"; export interface InputConfig { name:string; @@ -11,33 +10,4 @@ export interface InputConfig { export class ParamsMetadata extends BaseTypeMetadata { static getForClass = metadataGet(GRAPHQL_METADATA_KEY); static getOrCreateForClass = metadataGetOrSet(GRAPHQL_METADATA_KEY, ParamsMetadata); - - private _memoizedInputType:GraphQLInputObjectType; - private _memoizedArgumentsType:GraphQLFieldConfigArgumentMap; - - toGraphQLInputObjectType():GraphQLInputObjectType { - return this._memoizedInputType || (this._memoizedInputType = this.buildGraphQLInputObjectType()); - } - - toGraphQLFieldConfigArgumentMap():GraphQLFieldConfigArgumentMap { - return this._memoizedArgumentsType || (this._memoizedArgumentsType = this.buildGraphQLFieldConfigArgumentMap()); - } - - private buildGraphQLInputObjectType():GraphQLInputObjectType { - return propagateErrorWithContext(this.klass.name, () => { - let fieldsThunk = this.getFieldsMetadata().toGraphQLInputFieldConfigMap(); - - return new GraphQLInputObjectType({ - name: this.config.get('name'), - description: this.config.get('description'), - fields: fieldsThunk - }) - }) - } - - private buildGraphQLFieldConfigArgumentMap():GraphQLFieldConfigArgumentMap { - return propagateErrorWithContext(this.klass.name, () => { - return this.getFieldsMetadata().toGraphQLInputFieldConfigMap() - }) - } } \ No newline at end of file diff --git a/lib/types-metadata/TypeMetadata.ts b/lib/types-metadata/TypeMetadata.ts index e4e5618..de0e8e4 100644 --- a/lib/types-metadata/TypeMetadata.ts +++ b/lib/types-metadata/TypeMetadata.ts @@ -1,10 +1,8 @@ -import {BaseTypeMetadata, GRAPHQL_METADATA_KEY} from "./BaseTypeMetadata"; +import {BaseTypeMetadata} from "./BaseTypeMetadata"; import {metadataGet, metadataGetOrSet} from "../utils/metadataFactories"; -import {GraphQLInterfaceType, GraphQLIsTypeOfFn, GraphQLObjectType, Thunk} from "graphql"; -import {isPresent, propagateErrorWithContext} from "../utils/core"; -import {resolveThunk} from "../utils/graphql"; -import {TypeProxy} from "../types-conversion/TypeProxy"; +import {GraphQLInterfaceType, GraphQLIsTypeOfFn, Thunk} from "graphql"; import {ClassType} from "../utils/types"; +import {GRAPHQL_METADATA_KEY} from "./IBaseMetadata"; export interface TypeConfigParams { name:string; @@ -16,27 +14,4 @@ export interface TypeConfigParams { export class TypeMetadata extends BaseTypeMetadata { static getForClass = metadataGet(GRAPHQL_METADATA_KEY); static getOrCreateForClass = metadataGetOrSet(GRAPHQL_METADATA_KEY, TypeMetadata); - - private _memoizedGraphQLObjectType:GraphQLObjectType; - - toGraphQLObjectType():GraphQLObjectType { - return this._memoizedGraphQLObjectType || (this._memoizedGraphQLObjectType = this.buildGraphQLObjectType()); - } - - private buildGraphQLObjectType():GraphQLObjectType { - return propagateErrorWithContext(this.klass.name, () => { - let fieldsThunk = () => this.getFieldsMetadata().toGraphQLFieldConfigMap(); - - let interfaces = isPresent(this.config.get('interfaces')) ? - resolveThunk(this.config.get('interfaces')).map(type => TypeProxy.inferType(type) as GraphQLInterfaceType) : - null; - - return new GraphQLObjectType({ - name: this.config.get('name'), - description: this.config.get('description'), - interfaces, - fields: fieldsThunk - }) - }) - } } \ No newline at end of file diff --git a/lib/utils/core.ts b/lib/utils/core.ts index a6ce650..93264b2 100644 --- a/lib/utils/core.ts +++ b/lib/utils/core.ts @@ -29,8 +29,7 @@ export function propagateErrorWithContext(ctx:string, fn:() => T):T { try { return fn(); } catch (e) { - let error = new Error(`[${ctx}] ${e.message}`); - throw error + throw new Error(`[${ctx}] ${e.message}`); } } diff --git a/lib/utils/metadata.ts b/lib/utils/metadata.ts index 8f3339c..504b8a7 100644 --- a/lib/utils/metadata.ts +++ b/lib/utils/metadata.ts @@ -1,6 +1,6 @@ import {someOrThrow} from "./core"; -import {BaseTypeMetadata, GRAPHQL_METADATA_KEY} from "../types-metadata/BaseTypeMetadata"; +import {GRAPHQL_METADATA_KEY, IBaseMetadata} from "../types-metadata/IBaseMetadata"; -export function getMetadata(klass:Object):BaseTypeMetadata { +export function getMetadata(klass:Object):IBaseMetadata { return someOrThrow(Reflect.getMetadata(GRAPHQL_METADATA_KEY, klass), `Missing GraphQL metadata for ${klass.constructor.name}`); } \ No newline at end of file diff --git a/package.json b/package.json index c40b93b..602f3fb 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "build:dist": "npm run build:clean && npm run build:copy && npm run build:compile", "build:watch": "npm run build:clean && npm run build:copy && tsc --watch --sourceMap --pretty", "build:coverage": "nyc --reporter=text-lcov mocha -- -- spec dist/spec/**/*.spec.js | coveralls", - "build:coverage:local": "nyc --reporter=text-lcov mocha -- -- spec dist/spec/**/*.spec.js" + "build:coverage:local": "nyc mocha -- -- spec dist/spec/**/*.spec.js" }, "author": "Tomasz Robaczewski", "license": "MIT", @@ -35,7 +35,7 @@ "lodash": "^4.17.4" }, "peerDependencies": { - "graphql": "0.11.7" + "graphql": "^0.11.7" }, "nyc": { "exclude": [ diff --git a/spec/decorators/fields.spec.ts b/spec/decorators/fields.spec.ts index b550826..9a1084d 100644 --- a/spec/decorators/fields.spec.ts +++ b/spec/decorators/fields.spec.ts @@ -1,14 +1,17 @@ -import {GraphQLFieldConfig, GraphQLID, GraphQLList, GraphQLNonNull, GraphQLString} from "graphql"; -import {FieldsMetadata} from "../../lib/fields-metadata/FieldsMetadata"; +import {GraphQLField, GraphQLID, GraphQLList, GraphQLNonNull, GraphQLObjectType, GraphQLString} from "graphql"; import {expect} from 'chai'; import {description, field, fieldThunk, id, list, listThunk, nonNull, nonNullItems} from '../../lib/'; +import {typesResolver} from "../../lib/types-conversion/TypesResolver"; +import {TypeMetadata} from "../../lib/types-metadata/TypeMetadata"; -function getSpecField(klass, propertyKey:string = 'someField'):GraphQLFieldConfig { - return FieldsMetadata.getForClass(klass).getField(propertyKey).toGraphQLFieldConfig(); +function getSpecField(klass, propertyKey:string = 'someField'):GraphQLField { + TypeMetadata.getOrCreateForClass(klass); + let objectType = typesResolver.toGraphQLType(klass) as GraphQLObjectType; + return objectType.getFields()[propertyKey]; } describe("fields decorators", () => { - let fieldConfig:GraphQLFieldConfig; + let fieldConfig:GraphQLField; describe("@id()", () => { class SomeClass { diff --git a/spec/decorators/input.spec.ts b/spec/decorators/input.spec.ts index f9b00ed..bfee6cb 100644 --- a/spec/decorators/input.spec.ts +++ b/spec/decorators/input.spec.ts @@ -1,8 +1,8 @@ import {expect} from 'chai'; -import {input} from "../../lib"; -import {GraphQLInputField, GraphQLObjectType, GraphQLString} from "graphql"; -import {field} from "../../lib"; +import {field, input} from "../../lib"; +import {GraphQLString} from "graphql"; import {ParamsMetadata} from "../../lib/types-metadata/ParamsMetadata"; +import {getMetadata} from "../../lib/utils/metadata"; describe("@input", () => { @@ -13,57 +13,15 @@ describe("@input", () => { } it("attaches metadata object", () => { - expect(ParamsMetadata.getForClass(SomeInputParams)).to.be.instanceof(ParamsMetadata) + expect(getMetadata(SomeInputParams)).to.be.instanceof(ParamsMetadata) }); it("sets default name using class name", () => { - expect(ParamsMetadata.getForClass(SomeInputParams).toGraphQLInputObjectType().name).to.eq('SomeInputParams'); + expect(ParamsMetadata.getForClass(SomeInputParams).config.get('name')).to.eq('SomeInputParams'); }); it("sets description property", () => { - expect(ParamsMetadata.getForClass(SomeInputParams).toGraphQLInputObjectType().description).to.eq('Some description'); - }); - }); - - describe("@field()", () => { - describe("native types", () => { - @input() - class SomeInputParams { - @field(GraphQLString) - someField:string; - } - - describe("type", () => { - it("properly passes native type", () => { - let graphQLObjectType = ParamsMetadata.getOrCreateForClass(SomeInputParams).toGraphQLInputObjectType(); - let someFieldField:GraphQLInputField = graphQLObjectType.getFields()['someField']; - - expect(someFieldField.name).to.eq('someField'); - expect(someFieldField.type).to.eq(GraphQLString); - }); - }); - }); - - describe("decorated class type", () => { - @input() - class NewUserAddressParams { - @field(GraphQLString) - street:string; - } - - @input() - class NewUserParams { - @field(NewUserAddressParams) - address:NewUserAddressParams; - } - - it("it accepts annotated class as type parameter", () => { - let graphQLObjectType = ParamsMetadata.getOrCreateForClass(NewUserParams).toGraphQLInputObjectType(); - let someFieldField:GraphQLInputField = graphQLObjectType.getFields()['address']; - let someFieldGraphQLObjectType:GraphQLObjectType = someFieldField.type as any; - - expect(someFieldGraphQLObjectType).to.eql(ParamsMetadata.getForClass(NewUserAddressParams).toGraphQLInputObjectType()); - }); + expect(ParamsMetadata.getForClass(SomeInputParams).config.get('description')).to.eq('Some description'); }); }); }); \ No newline at end of file diff --git a/spec/decorators/interface.spec.ts b/spec/decorators/interface.spec.ts index 6b23ef6..b009960 100644 --- a/spec/decorators/interface.spec.ts +++ b/spec/decorators/interface.spec.ts @@ -1,8 +1,8 @@ import {expect} from 'chai'; import {GraphQLString} from "graphql"; -import {field} from "../../lib"; -import {interfaceType} from "../../lib/decorators/interface"; +import {field, interfaceType} from "../../lib"; import {InterfaceTypeMetadata} from "../../lib/types-metadata/InterfaceTypeMetadata"; +import {getMetadata} from "../../lib/utils/metadata"; describe("@interfaceType", () => { describe("@interfaceType()", () => { @@ -13,11 +13,11 @@ describe("@interfaceType", () => { } it("attaches metadata object", () => { - expect(InterfaceTypeMetadata.getForClass(SomeType)).to.be.instanceof(InterfaceTypeMetadata) + expect(getMetadata(SomeType)).to.be.instanceof(InterfaceTypeMetadata) }); it("sets default name using class name", () => { - expect(InterfaceTypeMetadata.getForClass(SomeType).toGraphQLInterfaceType().name).to.eq('SomeType'); + expect(InterfaceTypeMetadata.getForClass(SomeType).config.get('name')).to.eq('SomeType'); }); }); }); \ No newline at end of file diff --git a/spec/decorators/type.spec.ts b/spec/decorators/type.spec.ts index 29306b2..335276a 100644 --- a/spec/decorators/type.spec.ts +++ b/spec/decorators/type.spec.ts @@ -1,8 +1,8 @@ import {expect} from 'chai'; -import {GraphQLEnumType, GraphQLField, GraphQLObjectType, GraphQLString} from "graphql"; -import * as _ from 'lodash/fp'; -import {createEnum, decorateEnum, field, input, params, resolve, type} from "../../lib"; +import {GraphQLString} from "graphql"; +import {field, type} from "../../lib"; import {TypeMetadata} from "../../lib/types-metadata/TypeMetadata"; +import {getMetadata} from "../../lib/utils/metadata"; describe("@type", () => { describe("@type()", () => { @@ -13,168 +13,11 @@ describe("@type", () => { } it("attaches metadata object", () => { - expect(TypeMetadata.getForClass(SomeType)).to.be.instanceof(TypeMetadata) + expect(getMetadata(SomeType)).to.be.instanceof(TypeMetadata) }); it("sets default name using class name", () => { - expect(TypeMetadata.getForClass(SomeType).toGraphQLObjectType().name).to.eq('SomeType'); - }); - }); - - describe("@field()", () => { - describe("native types", () => { - const resolveFunction = () => null; - - @type() - class SomeType { - @resolve(resolveFunction) - @field(GraphQLString) - someField:string; - } - - describe("resolve", () => { - it("properly passes resolve function to GraphQLFieldMap", () => { - let graphQLObjectType = TypeMetadata.getOrCreateForClass(SomeType).toGraphQLObjectType(); - let someFieldField:GraphQLField = graphQLObjectType.getFields()['someField']; - expect(someFieldField.resolve).to.eq(resolveFunction); - }); - }); - - describe("type", () => { - it("properly passes native type", () => { - let graphQLObjectType = TypeMetadata.getOrCreateForClass(SomeType).toGraphQLObjectType(); - let someFieldField:GraphQLField = graphQLObjectType.getFields()['someField']; - - expect(someFieldField.name).to.eq('someField'); - expect(someFieldField.type).to.eq(GraphQLString); - }); - }); - }); - - describe("annotated type", () => { - @type() - class SomeOtherType { - @field(GraphQLString) - some:string; - } - - @type() - class SomeType { - @field(SomeOtherType) - someField:SomeOtherType; - } - - it("it accepts annotated class as type parameter", () => { - let graphQLObjectType = TypeMetadata.getOrCreateForClass(SomeType).toGraphQLObjectType(); - let someFieldField:GraphQLField = graphQLObjectType.getFields()['someField']; - let someFieldGraphQLObjectType:GraphQLObjectType = someFieldField.type as any; - - expect(someFieldGraphQLObjectType).to.eql(TypeMetadata.getForClass(SomeOtherType).toGraphQLObjectType()); - }); - }); - - describe("union type created with createUnion", () => { - // @type() - // class UnionType1 { - // @field(GraphQLString) - // someField:SomeOtherType; - // } - // - // @type() - // class UnionType2 { - // @field(GraphQLString) - // someField:SomeOtherType; - // } - // - // type SomeUnion = UnionType1 | UnionType2; - // // FieldsMetadata.getOrCreateForClass(UnionType1); - // // let wtf = FieldsMetadata.getForClass(UnionType1); - // - // const SomeUnionType = createUnion('SomeUnionType', [UnionType1, UnionType2], _.noop); - // - // @type() - // class SomeOtherType { - // @field(SomeUnionType) - // someField:SomeUnion; - // } - // - // it("uses GraphQLUnionType native type", () => { - // let graphQLObjectType = TypeMetadata.getOrCreateForClass(SomeOtherType).toGraphQLObjectType(); - // let someFieldField:GraphQLField = graphQLObjectType.getFields()['someField']; - // expect(someFieldField.type).to.eql(SomeUnionType); - // }); - }); - - describe("enum type created with createEnum", () => { - type Status = 'started' | 'stopped'; - const StatusType = createEnum('Status', ['started', 'stopped']); - - @type() - class SomeOtherType { - @field(StatusType) - status:Status; - } - - it("uses GraphQLUnionType native type", () => { - let graphQLObjectType = TypeMetadata.getOrCreateForClass(SomeOtherType).toGraphQLObjectType(); - let statusField:GraphQLField = graphQLObjectType.getFields()['status']; - expect(statusField.type).to.eql(StatusType); - }); - }); - - describe("ts enum decorated with decorateEnum function", () => { - enum Status { - started = 'started', - stopped = 'stopped' - } - - decorateEnum('Status', Status); - - @type() - class SomeOtherType { - @field(Status) - status:Status; - } - - it("uses GraphQLEnumType native type", () => { - let graphQLObjectType = TypeMetadata.getOrCreateForClass(SomeOtherType).toGraphQLObjectType(); - let statusField:GraphQLField = graphQLObjectType.getFields()['status']; - let type:GraphQLEnumType = statusField.type as any; - expect(type).to.be.instanceOf(GraphQLEnumType); - expect(type.getValues().map(_.pick(['value', 'name']))).to.eql([ - { - name: "started", - value: "started" - }, - { - name: "stopped", - value: "stopped" - } - ]) - }); - }); - - describe("arguments", () => { - - @input() - class SomeFieldArguments { - @field(GraphQLString) - someArgument:string; - } - - @type() - class SomeType { - @field(GraphQLString) - @params(SomeFieldArguments) - someField:string; - } - - it("accepts annotated class for params parameter", () => { - let graphQLObjectType = TypeMetadata.getOrCreateForClass(SomeType).toGraphQLObjectType(); - let someFieldArgs = graphQLObjectType.getFields()['someField'].args; - expect(someFieldArgs[0].type).to.eq(GraphQLString); - expect(someFieldArgs[0].name).to.eq('someArgument'); - }); + expect(TypeMetadata.getForClass(SomeType).config.get('name')).to.eq('SomeType'); }); }); }); \ No newline at end of file diff --git a/spec/factories/createUnion.spec.ts b/spec/factories/createUnion.spec.ts index 6e5dab2..2d5deb1 100644 --- a/spec/factories/createUnion.spec.ts +++ b/spec/factories/createUnion.spec.ts @@ -5,6 +5,7 @@ import {expect} from 'chai'; import {GraphQLString} from "graphql"; import {field} from "../../lib"; import {TypeMetadata} from "../../lib/types-metadata/TypeMetadata"; +import {typesResolver} from "../../lib/types-conversion/TypesResolver"; describe(".createUnion", () => { let SomeUnionTypeDefinition; @@ -29,8 +30,8 @@ describe(".createUnion", () => { describe("inferring of decorated type classes", () => { it("accepts decorated classes", () => { let types = SomeUnionTypeDefinition.getTypes(); - expect(types[0]).to.eq(TypeMetadata.getForClass(SomeType).toGraphQLObjectType()); - expect(types[1]).to.eq(TypeMetadata.getForClass(SomeOtherType).toGraphQLObjectType()); + expect(types[0]).to.eq(typesResolver.toGraphQLType(SomeType)); + expect(types[1]).to.eq(typesResolver.toGraphQLType(SomeOtherType)); }); }); }); \ No newline at end of file diff --git a/spec/types-conversion/TypeWrapper.spec.ts b/spec/fields-metadata/TypeWrapper.spec.ts similarity index 96% rename from spec/types-conversion/TypeWrapper.spec.ts rename to spec/fields-metadata/TypeWrapper.spec.ts index c4ff9b5..67f14ee 100644 --- a/spec/types-conversion/TypeWrapper.spec.ts +++ b/spec/fields-metadata/TypeWrapper.spec.ts @@ -1,4 +1,4 @@ -import {TypeWrapper} from "../../lib/types-conversion/TypeWrapper"; +import {TypeWrapper} from "../../lib/fields-metadata/TypeWrapper"; import {GraphQLList, GraphQLNonNull, GraphQLString} from "graphql"; import {expect} from 'chai'; diff --git a/spec/integration/types-conversion.spec.ts b/spec/integration/types-conversion.spec.ts new file mode 100644 index 0000000..2082666 --- /dev/null +++ b/spec/integration/types-conversion.spec.ts @@ -0,0 +1,203 @@ +import {createEnum, createUnion, decorateEnum, field, input, params, resolve, type} from "../../lib"; +import * as _ from "lodash"; +import * as fp from 'lodash/fp'; +import {GraphQLEnumType, GraphQLEnumValue, GraphQLField, GraphQLInt, GraphQLObjectType, GraphQLString} from "graphql"; +import {TypesResolver} from "../../lib/types-conversion/TypesResolver"; +import {expect} from "chai"; +import {enumsRegistry} from "../../lib/registry/typesRegistry"; + +describe("Types conversion", () => { + let typesResolver:TypesResolver; + + describe("converting native types", () => { + beforeEach(() => { + typesResolver = new TypesResolver(); + }); + + describe("GraphQLString", () => { + it(`returns GraphQLString`, () => { + expect(typesResolver.toGraphQLType(GraphQLString)).to.eq(GraphQLString); + }); + }); + + describe("GraphQLInt", () => { + it(`returns GraphQLInt`, () => { + expect(typesResolver.toGraphQLType(GraphQLInt)).to.eq(GraphQLInt); + }) + }); + }); + + describe("native types", () => { + const resolveFunction = () => null; + + @type() + class SomeType { + @resolve(resolveFunction) + @field(GraphQLString) + someField:string; + } + + describe("resolve", () => { + it("properly passes resolve function to GraphQLFieldMap", () => { + let graphQLObjectType = typesResolver.toGraphQLType(SomeType) as GraphQLObjectType; + let someFieldField:GraphQLField = graphQLObjectType.getFields()['someField']; + expect(someFieldField.resolve).to.eq(resolveFunction); + }); + }); + + describe("type", () => { + it("properly passes native type", () => { + let graphQLObjectType = typesResolver.toGraphQLType(SomeType) as GraphQLObjectType; + let someFieldField:GraphQLField = graphQLObjectType.getFields()['someField']; + + expect(someFieldField.name).to.eq('someField'); + expect(someFieldField.type).to.eq(GraphQLString); + }); + }); + }); + + describe("union type created with createUnion", () => { + @type() + class UnionType1 { + @field(GraphQLString) + someField:SomeOtherType; + } + + @type() + class UnionType2 { + @field(GraphQLString) + someField:SomeOtherType; + } + + type SomeUnion = UnionType1 | UnionType2; + const SomeUnionType = createUnion('SomeUnionType', [UnionType1, UnionType2], _.noop); + + @type() + class SomeOtherType { + @field(SomeUnionType) + someField:SomeUnion; + } + + it("uses GraphQLUnionType native type", () => { + let graphQLObjectType = typesResolver.toGraphQLType(SomeOtherType) as GraphQLObjectType; + let someFieldField:GraphQLField = graphQLObjectType.getFields()['someField']; + expect(someFieldField.type).to.eql(SomeUnionType); + }); + }); + + describe("annotated type", () => { + @type() + class SomeOtherType { + @field(GraphQLString) + some:string; + } + + @type() + class SomeType { + @field(SomeOtherType) + someField:SomeOtherType; + } + + it("it accepts annotated class as type parameter", () => { + let graphQLObjectType = typesResolver.toGraphQLType(SomeType) as GraphQLObjectType; + let someFieldField:GraphQLField = graphQLObjectType.getFields()['someField']; + let someFieldGraphQLObjectType:GraphQLObjectType = someFieldField.type as any; + + expect(someFieldGraphQLObjectType).to.eql(typesResolver.toGraphQLType(SomeOtherType)); + }); + }); + + describe("enum type created with createEnum", () => { + type Status = 'started' | 'stopped'; + const StatusType = createEnum('Status', ['started', 'stopped']); + + @type() + class SomeOtherType { + @field(StatusType) + status:Status; + } + + it("uses GraphQLUnionType native type", () => { + let graphQLObjectType = typesResolver.toGraphQLType(SomeOtherType) as GraphQLObjectType; + let statusField:GraphQLField = graphQLObjectType.getFields()['status']; + expect(statusField.type).to.eql(StatusType); + }); + }); + + describe("params", () => { + @input() + class SomeFieldArguments { + @field(GraphQLString) + someArgument:string; + } + + @type() + class SomeType { + @field(GraphQLString) + @params(SomeFieldArguments) + someField:string; + } + + it("accepts annotated class for params parameter", () => { + let graphQLObjectType = typesResolver.toGraphQLType(SomeType) as GraphQLObjectType; + let someFieldArgs = graphQLObjectType.getFields()['someField'].args; + expect(someFieldArgs[0].type).to.eq(GraphQLString); + expect(someFieldArgs[0].name).to.eq('someArgument'); + }); + }); + + describe("ts enum decorated with decorateEnum function", () => { + enum Status { + started = 'started', + stopped = 'stopped' + } + + decorateEnum('Status', Status); + + @type() + class SomeOtherType { + @field(Status) + status:Status; + } + + it("uses GraphQLEnumType native type", () => { + let graphQLObjectType = typesResolver.toGraphQLType(SomeOtherType) as GraphQLObjectType; + let statusField:GraphQLField = graphQLObjectType.getFields()['status']; + let type:GraphQLEnumType = statusField.type as any; + expect(type).to.be.instanceOf(GraphQLEnumType); + + expect(type.getValues().map(fp.pick(['value', 'name']))).to.eql([ + { + name: "started", + value: "started" + }, + { + name: "stopped", + value: "stopped" + } + ]) + }); + }); + + describe("converting enum type", () => { + enum Enum { + A = 'a', + B = 'b' + } + + beforeEach(() => { + enumsRegistry.registerEnum('Enum', Enum, 'SomeEnumDescription'); + }); + + it('returns object returned by toGraphQLInterfaceType on InterfaceMetadata', () => { + expect(typesResolver.toGraphQLType(Enum)).to.eql(new GraphQLEnumType({ + name: 'Enum', + description: 'SomeEnumDescription', + values: { + A: {value: 'a'}, + B: {value: 'b'} + } + })); + }); + }); +}); \ No newline at end of file diff --git a/spec/types-conversion/TypeProxy.spec.ts b/spec/types-conversion/TypeProxy.spec.ts deleted file mode 100644 index 10ccdc5..0000000 --- a/spec/types-conversion/TypeProxy.spec.ts +++ /dev/null @@ -1,90 +0,0 @@ -import {GraphQLEnumType, GraphQLInt, GraphQLString} from "graphql"; -import {TypeProxy} from "../../lib/types-conversion/TypeProxy"; -import {expect} from 'chai'; -import {TypeMetadata} from "../../lib/types-metadata/TypeMetadata"; -import * as sinon from 'sinon'; -import {ParamsMetadata} from "../../lib/types-metadata/ParamsMetadata"; -import {InterfaceTypeMetadata} from "../../lib/types-metadata/InterfaceTypeMetadata"; -import {enumsRegistry} from "../../lib/registry/typesRegistry"; - -describe("TypeProxy", () => { - describe(".toGraphQLType()", () => { - describe("converting native types", () => { - describe("GraphQLString", () => { - it(`returns GraphQLString`, () => { - expect(TypeProxy.inferType(GraphQLString)).to.eq(GraphQLString); - expect(TypeProxy.inferTypeThunk(() => GraphQLString)).to.eq(GraphQLString); - }); - }); - - describe("GraphQLInt", () => { - expect(TypeProxy.inferType(GraphQLInt)).to.eq(GraphQLInt); - expect(TypeProxy.inferTypeThunk(() => GraphQLInt)).to.eq(GraphQLInt); - }); - }); - - describe("converting type decorated with TypeMetadata", () => { - let SomeClass; - - beforeEach(() => { - SomeClass = class {}; - let typeMetadata = TypeMetadata.getOrCreateForClass(SomeClass); - sinon.stub(typeMetadata, 'toGraphQLObjectType').returns(GraphQLString); - }); - - it('returns object returned by toGraphQLObjectType on TypeMetadata', () => { - expect(TypeProxy.inferType(SomeClass)).to.eq(GraphQLString); - }); - }); - - describe("converting type decorated with ParamsMetadata", () => { - let SomeClass; - - beforeEach(() => { - SomeClass = class {}; - let paramsMetadata = ParamsMetadata.getOrCreateForClass(SomeClass); - sinon.stub(paramsMetadata, 'toGraphQLInputObjectType').returns(GraphQLString); - }); - - it('returns object returned by toGraphQLInputObjectType on TypeMetadata', () => { - expect(TypeProxy.inferType(SomeClass)).to.eq(GraphQLString); - }); - }); - - describe("converting type decorated with InterfaceMetadata", () => { - let SomeClass; - - beforeEach(() => { - SomeClass = class {}; - let interfaceMetadata = InterfaceTypeMetadata.getOrCreateForClass(SomeClass); - sinon.stub(interfaceMetadata, 'toGraphQLInterfaceType').returns(GraphQLString); - }); - - it('returns object returned by toGraphQLInterfaceType on InterfaceMetadata', () => { - expect(TypeProxy.inferType(SomeClass)).to.eq(GraphQLString); - }); - }); - - describe("converting enum type", () => { - enum Enum { - A = 'a', - B = 'b' - } - - beforeEach(() => { - enumsRegistry.registerEnum('Enum', Enum, 'SomeEnumDescription'); - }); - - it('returns object returned by toGraphQLInterfaceType on InterfaceMetadata', () => { - expect(TypeProxy.inferType(Enum)).to.eql(new GraphQLEnumType({ - name: 'Enum', - description: 'SomeEnumDescription', - values: { - A: {value: 'a'}, - B: {value: 'b'} - } - })); - }); - }); - }); -}); \ No newline at end of file diff --git a/spec/metadata/FieldsMetadata.spec.ts b/spec/types-conversion/output/FieldConfigMapResolver.spec.ts similarity index 60% rename from spec/metadata/FieldsMetadata.spec.ts rename to spec/types-conversion/output/FieldConfigMapResolver.spec.ts index d7a659f..1c278fb 100644 --- a/spec/metadata/FieldsMetadata.spec.ts +++ b/spec/types-conversion/output/FieldConfigMapResolver.spec.ts @@ -1,10 +1,17 @@ import {expect} from 'chai'; -import {GraphQLList, GraphQLNonNull, GraphQLObjectType, GraphQLString} from "graphql"; -import {FieldsMetadata} from "../../lib/fields-metadata/FieldsMetadata"; -import {field, type} from "../../lib"; +import {GraphQLField, GraphQLList, GraphQLNonNull, GraphQLObjectType, GraphQLString} from "graphql"; +import {FieldsMetadata} from "../../../lib/fields-metadata/FieldsMetadata"; +import {field} from "../../../lib"; +import {typesResolver} from "../../../lib/types-conversion/TypesResolver"; +import {TypeMetadata} from "../../../lib/types-metadata/TypeMetadata"; + +function getSpecField(klass, propertyKey:string = 'someField'):GraphQLField { + TypeMetadata.getOrCreateForClass(klass); + let objectType = typesResolver.toGraphQLType(klass) as GraphQLObjectType; + return objectType.getFields()[propertyKey]; +} describe("FieldsTypeMetadata", () => { - describe("static getOrCreateForClass", () => { let SomeClass; @@ -36,29 +43,29 @@ describe("FieldsTypeMetadata", () => { describe(".patchConfig", () => { it("sets type", () => { someClassFieldsMetadata.getField('field').setType(GraphQLString); - expect(someClassFieldsMetadata.getFields()['field'].toGraphQLFieldConfig().type).to.eql(GraphQLString); + expect(getSpecField(SomeClass, 'field').type).to.eql(GraphQLString); }); it("sets thunkType and resolves it", () => { someClassFieldsMetadata.getField('field').setTypeThunk(() => GraphQLString); - expect(someClassFieldsMetadata.getFields()['field'].toGraphQLFieldConfig().type).to.eql(GraphQLString); + expect(getSpecField(SomeClass, 'field').type).to.eql(GraphQLString); }); it("wraps type with GraphQLList if list property is set to true", () => { someClassFieldsMetadata.getField('field').setListType(GraphQLString); - expect(someClassFieldsMetadata.getFields()['field'].toGraphQLFieldConfig().type).to.eql(new GraphQLList(GraphQLString)); + expect(getSpecField(SomeClass, 'field').type).to.eql(new GraphQLList(GraphQLString)); }); it("wraps type with GraphQLNonNull if list property is set to true", () => { someClassFieldsMetadata.getField('field').setType(GraphQLString); someClassFieldsMetadata.getField('field').setNonNullConstraint(); - expect(someClassFieldsMetadata.getFields()['field'].toGraphQLFieldConfig().type).to.eql(new GraphQLNonNull(GraphQLString)); + expect(getSpecField(SomeClass, 'field').type).to.eql(new GraphQLNonNull(GraphQLString)); }); it("wraps type with GraphQLNonNull and GraphQLList for nonNull and array properties set to true", () => { someClassFieldsMetadata.getField('field').setListType(GraphQLString); someClassFieldsMetadata.getField('field').setNonNullConstraint(); - expect(someClassFieldsMetadata.getFields()['field'].toGraphQLFieldConfig().type).to.eql(new GraphQLNonNull(new GraphQLList(GraphQLString))); + expect(getSpecField(SomeClass, 'field').type).to.eql(new GraphQLNonNull(new GraphQLList(GraphQLString))); }); it("wraps type using GraphQLNonNull and GraphQLList for nonNull, array, nonNullItem properties set to true", () => { @@ -67,37 +74,27 @@ describe("FieldsTypeMetadata", () => { field.setNonNullConstraint(); field.setNonNullItemsConstraint(); - - expect(someClassFieldsMetadata.getFields()['field'].toGraphQLFieldConfig().type).to + expect(getSpecField(SomeClass, 'field').type).to .eql(new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(GraphQLString)))); }); - it("only pass valid GraphQLFieldConfigMap properties", () => { + it("sets description property", () => { someClassFieldsMetadata.getField('field').setType(GraphQLString); someClassFieldsMetadata.getField('field').setDescription('some description'); - expect(someClassFieldsMetadata.getFields()['field'].toGraphQLFieldConfig().description).to + expect(getSpecField(SomeClass, 'field').description).to .eql('some description'); - - // expect(Object.keys(someClassFieldsMetadata.getFields()['field']).sort()).to.eql([ - // "args", - // "deprecationReason", - // "description", - // "resolve", - // "type" - // ]) }); - it("creates GraphQLType for annotated class passed as type", () => { - @type() - class SomeClass { - @field(GraphQLString) someField:string; - } + it('sets resolve property', () => { + function resolveIt(){} - (SomeClass as any).kurwa = "WTF"; - - someClassFieldsMetadata.getField('field').setType(SomeClass); - expect(someClassFieldsMetadata.getFields()['field'].toGraphQLFieldConfig().type).to.be.instanceOf(GraphQLObjectType) + someClassFieldsMetadata.getField('field').setType(GraphQLString); + someClassFieldsMetadata.getField('field').setResolver(resolveIt); + expect(getSpecField(SomeClass, 'field').resolve).to + .eq(resolveIt); }); + + it('sets deprecationReason'); }); }); diff --git a/spec/metadata/InterfaceTypeMetadata.spec.ts b/spec/types-conversion/output/InterfaceTypeResolver.spec.ts similarity index 54% rename from spec/metadata/InterfaceTypeMetadata.spec.ts rename to spec/types-conversion/output/InterfaceTypeResolver.spec.ts index 9c9ec4f..321f30d 100644 --- a/spec/metadata/InterfaceTypeMetadata.spec.ts +++ b/spec/types-conversion/output/InterfaceTypeResolver.spec.ts @@ -1,10 +1,10 @@ -import {noop} from "../../lib/utils/core"; +import {noop} from "../../../lib/utils/core"; import {expect} from 'chai'; -import {GraphQLInterfaceType, GraphQLString} from "graphql"; -import {field, type} from "../../lib"; -import {InterfaceTypeMetadata} from "../../lib/types-metadata/InterfaceTypeMetadata"; -import {FieldsMetadata} from "../../lib/fields-metadata/FieldsMetadata"; -import {TypeMetadata} from "../../lib/types-metadata/TypeMetadata"; +import {GraphQLInterfaceType, GraphQLObjectType, GraphQLString} from "graphql"; +import {field, type} from "../../../lib"; +import {InterfaceTypeMetadata} from "../../../lib/types-metadata/InterfaceTypeMetadata"; +import {FieldsMetadata} from "../../../lib/fields-metadata/FieldsMetadata"; +import {typesResolver} from "../../../lib/types-conversion/TypesResolver"; describe("InterfaceTypeMetadata", () => { @@ -12,7 +12,7 @@ describe("InterfaceTypeMetadata", () => { describe("static getOrCreateForClass", noop); describe(".toGraphQLType", () => { - let SomeInterface, + let SomeInterface:Object, someInterfaceMetadata:InterfaceTypeMetadata, someInterfaceFieldsMetadata:FieldsMetadata; @@ -24,19 +24,22 @@ describe("InterfaceTypeMetadata", () => { }); it("returns GraphQLInterfaceType", () => { - expect(someInterfaceMetadata.toGraphQLInterfaceType()).to.be.instanceOf(GraphQLInterfaceType); + expect(typesResolver.toGraphQLType(SomeInterface)).to.be.instanceOf(GraphQLInterfaceType); }); it("it pass name property to GraphQLInterfaceType instance", () => { someInterfaceMetadata.setConfig({name: 'SomeName'}); - expect(someInterfaceMetadata.toGraphQLInterfaceType().name).to.eq('SomeName'); + let graphQLInterfaceType = typesResolver.toGraphQLType(SomeInterface) as GraphQLInterfaceType; + expect(graphQLInterfaceType.name).to.eq('SomeName'); }); it("it pass description property to GraphQLInterfaceType instance", () => { someInterfaceMetadata.setConfig({description: 'Some description'}); - expect(someInterfaceMetadata.toGraphQLInterfaceType().description).to.eq('Some description'); + let graphQLInterfaceType = typesResolver.toGraphQLType(SomeInterface) as GraphQLInterfaceType; + expect(graphQLInterfaceType.description).to.eq('Some description'); }); + it("infers annotated types from resolveType function", () => { @type() class SomeClass { @@ -44,8 +47,9 @@ describe("InterfaceTypeMetadata", () => { } someInterfaceMetadata.setConfig({name: 'SomeName', resolveType: () => SomeClass}); - expect(someInterfaceMetadata.toGraphQLInterfaceType().resolveType(null, null, null)).to - .eql(TypeMetadata.getForClass(SomeClass).toGraphQLObjectType()) + let graphQLType = typesResolver.toGraphQLType(SomeInterface) as GraphQLInterfaceType; + expect(graphQLType.resolveType(null, null, null)).to + .eql(typesResolver.toGraphQLType(SomeClass) as GraphQLObjectType) }); }); }); diff --git a/spec/metadata/TypeMetadata.spec.ts b/spec/types-conversion/output/ObjectTypeResolver.spec.ts similarity index 55% rename from spec/metadata/TypeMetadata.spec.ts rename to spec/types-conversion/output/ObjectTypeResolver.spec.ts index a1f3b81..eda29a1 100644 --- a/spec/metadata/TypeMetadata.spec.ts +++ b/spec/types-conversion/output/ObjectTypeResolver.spec.ts @@ -1,42 +1,43 @@ -import {noop} from "../../lib/utils/core"; - +import {noop} from "../../../lib/utils/core"; import {expect} from 'chai'; import {GraphQLInterfaceType, GraphQLObjectType, GraphQLString} from "graphql"; -import {FieldsMetadata} from "../../lib/fields-metadata/FieldsMetadata"; -import {field, interfaceType} from "../../lib"; -import {InterfaceTypeMetadata} from "../../lib/types-metadata/InterfaceTypeMetadata"; -import {TypeMetadata} from "../../lib/types-metadata/TypeMetadata"; +import {FieldsMetadata} from "../../../lib/fields-metadata/FieldsMetadata"; +import {TypeMetadata} from "../../../lib/types-metadata/TypeMetadata"; +import {ObjectTypeResolver} from "../../../lib/types-conversion/output/ObjectTypeResolver"; +import {typesResolver} from "../../../lib/types-conversion/TypesResolver"; +import {field, interfaceType} from "../../../lib"; -describe("TypeMetadata", () => { +describe("ObjectTypeResolver", () => { describe("static getForClass", noop); describe("static getOrCreateForClass", noop); describe(".toGraphQLType", () => { - let SomeClass, - someClassMetadata:TypeMetadata, - someClassFieldsMetadata:FieldsMetadata; + let SomeClass:Object, + someClassMetadata:TypeMetadata; beforeEach(() => { SomeClass = class {}; someClassMetadata = TypeMetadata.getOrCreateForClass(SomeClass); someClassMetadata.setConfig({name: 'SomeClass'}); - someClassFieldsMetadata = FieldsMetadata.getOrCreateForClass(SomeClass); + let someClassFieldsMetadata = FieldsMetadata.getOrCreateForClass(SomeClass); someClassFieldsMetadata.getField('requiredDummyField').setType(GraphQLString); }); it("returns GraphQLObjectType", () => { - expect(someClassMetadata.toGraphQLObjectType()).to.be.instanceOf(GraphQLObjectType); + expect(typesResolver.toGraphQLType(SomeClass)).to.be.instanceOf(GraphQLObjectType); }); it("it pass name property to GraphQLObjectType instance", () => { someClassMetadata.setConfig({name: 'SomeName'}); - expect(someClassMetadata.toGraphQLObjectType().name).to.eq('SomeName'); + let graphQLObjectType = typesResolver.toGraphQLType(SomeClass) as GraphQLObjectType; + expect(graphQLObjectType.name).to.eq('SomeName'); }); it("it pass description property to GraphQLObjectType instance", () => { someClassMetadata.setConfig({description: 'Some description'}); - expect(someClassMetadata.toGraphQLObjectType().description).to.eq('Some description'); + let graphQLObjectType = typesResolver.toGraphQLType(SomeClass) as GraphQLObjectType; + expect(graphQLObjectType.description).to.eq('Some description'); }); it("pass interfaces property to GraphQLObjectType instance", () => { @@ -51,7 +52,9 @@ describe("TypeMetadata", () => { }); someClassMetadata.setConfig({name: 'SomeClass', interfaces: [SomeInterface]}); - expect(someClassMetadata.toGraphQLObjectType().getInterfaces()).to.eql([SomeInterface]); + + let graphQLObjectType = typesResolver.toGraphQLType(SomeClass) as GraphQLObjectType; + expect(graphQLObjectType.getInterfaces()).to.eql([SomeInterface]); }); it("pass annotated class in interface array ", () => { @@ -63,8 +66,10 @@ describe("TypeMetadata", () => { } someClassMetadata.setConfig({name: 'SomeClass', interfaces: [SomeInterface]}); - expect(someClassMetadata.toGraphQLObjectType().getInterfaces()).to.eql([ - InterfaceTypeMetadata.getForClass(SomeInterface).toGraphQLInterfaceType() + let someClassGQType = typesResolver.toGraphQLType(SomeClass) as GraphQLObjectType; + + expect(someClassGQType.getInterfaces()).to.eql([ + typesResolver.toGraphQLType(SomeInterface) ]); }); }); diff --git a/yarn.lock b/yarn.lock index b05f2b9..e1d2523 100644 --- a/yarn.lock +++ b/yarn.lock @@ -638,12 +638,6 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.2: version "1.0.1" resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" -graphql@^0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/graphql/-/graphql-0.12.0.tgz#a62576995f67f7a94a02f3e57e91d752bae70055" - dependencies: - iterall "1.1.3" - growl@1.9.2: version "1.9.2" resolved "https://registry.yarnpkg.com/growl/-/growl-1.9.2.tgz#0ea7743715db8d8de2c5ede1775e1b45ac85c02f" @@ -903,10 +897,6 @@ istanbul-reports@^1.1.3: dependencies: handlebars "^4.0.3" -iterall@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/iterall/-/iterall-1.1.3.tgz#1cbbff96204056dde6656e2ed2e2226d0e6d72c9" - js-tokens@^3.0.0, js-tokens@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"