From 0edb9027cf2a9038ca9fc44b55f1382295447e9a Mon Sep 17 00:00:00 2001 From: nodkz Date: Wed, 14 Feb 2018 13:59:09 +0600 Subject: [PATCH] fix(SchemaComposer): add helper methods `getOrCreateTC()`, `getOrCreateITC()`, `getOrCreateETC()`, fix flowtype autocompletion --- src/SchemaComposer.js | 73 ++++++------- src/__tests__/SchemaComposer-test.js | 154 ++++++++++++++++++++++----- 2 files changed, 162 insertions(+), 65 deletions(-) diff --git a/src/SchemaComposer.js b/src/SchemaComposer.js index d9e298df..52f29712 100644 --- a/src/SchemaComposer.js +++ b/src/SchemaComposer.js @@ -1,24 +1,23 @@ /* @flow strict */ /* eslint-disable class-methods-use-this */ -import { GraphQLObjectType, GraphQLSchema, type GraphQLNamedType } from './graphql'; +import { GraphQLObjectType, GraphQLSchema } from './graphql'; import { TypeStorage } from './TypeStorage'; import { TypeMapper } from './TypeMapper'; import { TypeComposer as _TypeComposer } from './TypeComposer'; import { InputTypeComposer as _InputTypeComposer } from './InputTypeComposer'; import { EnumTypeComposer as _EnumTypeComposer } from './EnumTypeComposer'; import { Resolver as _Resolver } from './Resolver'; +import { isFunction } from './utils/is'; -export class SchemaComposer extends TypeStorage< - _TypeComposer | _InputTypeComposer | GraphQLNamedType -> { +export class SchemaComposer extends TypeStorage { typeMapper: TypeMapper; TypeComposer: Class<_TypeComposer>; InputTypeComposer: typeof _InputTypeComposer; EnumTypeComposer: typeof _EnumTypeComposer; Resolver: Class<_Resolver>; - constructor() { + constructor(): SchemaComposer { super(); const schema = this; @@ -44,32 +43,8 @@ export class SchemaComposer extends TypeStorage< this.typeMapper = new TypeMapper(schema); - getOrCreateTC(typeName: string): _TypeComposer { - if (!this.hasInstance(typeName, this.TypeComposer)) { - this.set(typeName, this.TypeComposer.create(typeName)); - } - return (this.get(typeName): any); - } - - getOrCreateITC(typeName: string): _InputTypeComposer { - if (!this.hasInstance(typeName, this.InputTypeComposer)) { - this.set(typeName, this.InputTypeComposer.create(typeName)); - } - return (this.get(typeName): any); - } - - getTC(typeName: string): _TypeComposer { - if (!this.hasInstance(typeName, this.TypeComposer)) { - throw new Error(`GQC does not have TypeComposer with name ${typeName}`); - } - return (this.get(typeName): any); - } - - getITC(typeName: string): _InputTypeComposer { - if (!this.hasInstance(typeName, this.InputTypeComposer)) { - throw new Error(`GQC does not have InputTypeComposer with name ${typeName}`); - } - return (this.get(typeName): any); + // alive proper Flow type casting in autosuggestions + /* :: return this; */ } rootQuery(): _TypeComposer { @@ -140,18 +115,44 @@ export class SchemaComposer extends TypeStorage< typeComposer.setFields(fields); } + getOrCreateTC( + typeName: string, + onCreate?: (_TypeComposer) => any + ): _TypeComposer { + if (!this.hasInstance(typeName, _TypeComposer)) { + const tc = this.TypeComposer.create(typeName); + this.set(typeName, tc); + if (onCreate && isFunction(onCreate)) onCreate(tc); + } + return (this.get(typeName): any); + } + + getOrCreateITC(typeName: string, onCreate?: _InputTypeComposer => any): _InputTypeComposer { + if (!this.hasInstance(typeName, _InputTypeComposer)) { + const itc = this.InputTypeComposer.create(typeName); + this.set(typeName, itc); + if (onCreate && isFunction(onCreate)) onCreate(itc); + } + return (this.get(typeName): any); + } + + getOrCreateETC(typeName: string, onCreate?: _EnumTypeComposer => any): _EnumTypeComposer { + if (!this.hasInstance(typeName, _EnumTypeComposer)) { + const etc = this.EnumTypeComposer.create(typeName); + this.set(typeName, etc); + if (onCreate && isFunction(onCreate)) onCreate(etc); + } + return (this.get(typeName): any); + } + + // disable redundant noise in console.logs toString(): string { - // disable redundant noise in console.logs return 'SchemaComposer'; } - toJSON() { - // disable redundant noise in console.logs return 'SchemaComposer'; } - inspect() { - // disable redundant noise in console.logs return 'SchemaComposer'; } } diff --git a/src/__tests__/SchemaComposer-test.js b/src/__tests__/SchemaComposer-test.js index a7f6866f..712745e3 100644 --- a/src/__tests__/SchemaComposer-test.js +++ b/src/__tests__/SchemaComposer-test.js @@ -1,58 +1,154 @@ /* @flow strict */ import { SchemaComposer } from '../'; +import { TypeComposer } from '../TypeComposer'; +import { InputTypeComposer } from '../InputTypeComposer'; +import { EnumTypeComposer } from '../EnumTypeComposer'; -describe('Storage [Class]', () => { +describe('SchemaComposer', () => { it('should implements `add` method', () => { - const storage = new SchemaComposer(); - const someTC = storage.TypeComposer.create({ name: 'validType' }); - storage.add(someTC); - expect(storage.get('validType')).toBe(someTC); + const sc = new SchemaComposer(); + const SomeTC = sc.TypeComposer.create({ name: 'validType' }); + sc.add(SomeTC); + expect(sc.get('validType')).toBe(SomeTC); }); it('should implements `get` method', () => { - const storage = new SchemaComposer(); - const someTC = storage.TypeComposer.create({ name: 'validType' }); - storage.add(someTC); - expect(storage.get('validType')).toBe(someTC); + const sc = new SchemaComposer(); + const SomeTC = sc.TypeComposer.create({ name: 'validType' }); + sc.add(SomeTC); + expect(sc.get('validType')).toBe(SomeTC); }); it('should implements `has` method`', () => { - const storage = new SchemaComposer(); - const someTC = storage.TypeComposer.create({ name: 'validType' }); - storage.add(someTC); - expect(storage.has('validType')).toBe(true); - expect(storage.has('unexistedType')).toBe(false); + const sc = new SchemaComposer(); + const SomeTC = sc.TypeComposer.create({ name: 'validType' }); + sc.add(SomeTC); + expect(sc.has('validType')).toBe(true); + expect(sc.has('unexistedType')).toBe(false); + }); + + describe('getOrCreateTC()', () => { + it('should create TC if not exists', () => { + const sc = new SchemaComposer(); + const UserTC = sc.getOrCreateTC('User'); + expect(UserTC).toBeInstanceOf(TypeComposer); + expect(sc.has('User')).toBeTruthy(); + expect(sc.hasInstance('User', TypeComposer)).toBeTruthy(); + expect(sc.getTC('User')).toBe(UserTC); + }); + + it('should create TC if not exists with onCreate', () => { + const sc = new SchemaComposer(); + const UserTC = sc.getOrCreateTC('User', tc => { + tc.setDescription('User model'); + }); + expect(UserTC.getDescription()).toBe('User model'); + }); + + it('should return already created TC without onCreate', () => { + const sc = new SchemaComposer(); + const UserTC = sc.getOrCreateTC('User', tc => { + tc.setDescription('User model'); + }); + const UserTC2 = sc.getOrCreateTC('User', tc => { + tc.setDescription('updated description'); + }); + expect(UserTC).toBe(UserTC2); + expect(UserTC.getDescription()).toBe('User model'); + }); + }); + + describe('getOrCreateITC()', () => { + it('should create ITC if not exists', () => { + const sc = new SchemaComposer(); + const UserITC = sc.getOrCreateITC('UserInput'); + expect(UserITC).toBeInstanceOf(InputTypeComposer); + expect(sc.has('UserInput')).toBeTruthy(); + expect(sc.hasInstance('UserInput', InputTypeComposer)).toBeTruthy(); + expect(sc.getITC('UserInput')).toBe(UserITC); + }); + + it('should create ITC if not exists with onCreate', () => { + const sc = new SchemaComposer(); + const UserITC = sc.getOrCreateITC('UserInput', tc => { + tc.setDescription('User input'); + }); + expect(UserITC.getDescription()).toBe('User input'); + }); + + it('should return already created ITC without onCreate', () => { + const sc = new SchemaComposer(); + const UserITC = sc.getOrCreateITC('UserInput', tc => { + tc.setDescription('User input'); + }); + const UserITC2 = sc.getOrCreateITC('UserInput', tc => { + tc.setDescription('updated description'); + }); + expect(UserITC).toBe(UserITC2); + expect(UserITC.getDescription()).toBe('User input'); + }); + }); + + describe('getOrCreateETC()', () => { + it('should create ETC if not exists', () => { + const sc = new SchemaComposer(); + const UserETC = sc.getOrCreateETC('UserEnum'); + expect(UserETC).toBeInstanceOf(EnumTypeComposer); + expect(sc.has('UserEnum')).toBeTruthy(); + expect(sc.hasInstance('UserEnum', EnumTypeComposer)).toBeTruthy(); + expect(sc.getETC('UserEnum')).toBe(UserETC); + }); + + it('should create ETC if not exists with onCreate', () => { + const sc = new SchemaComposer(); + const UserETC = sc.getOrCreateETC('UserEnum', tc => { + tc.setDescription('User enum'); + }); + expect(UserETC.getDescription()).toBe('User enum'); + }); + + it('should return already created ETC without onCreate', () => { + const sc = new SchemaComposer(); + const UserETC = sc.getOrCreateETC('UserEnum', tc => { + tc.setDescription('User enum'); + }); + const UserETC2 = sc.getOrCreateETC('UserEnum', tc => { + tc.setDescription('updated description'); + }); + expect(UserETC).toBe(UserETC2); + expect(UserETC.getDescription()).toBe('User enum'); + }); }); describe('buildSchema()', () => { it('should throw error, if root fields not defined', () => { - const storage = new SchemaComposer(); - storage.clear(); + const sc = new SchemaComposer(); + sc.clear(); expect(() => { - storage.buildSchema(); + sc.buildSchema(); }).toThrowError(); }); }); describe('removeEmptyTypes()', () => { it('should remove fields with Types which have no fields', () => { - const storage = new SchemaComposer(); - const typeWithoutFieldsTC = storage.getOrCreateTC('Stub'); - typeWithoutFieldsTC.setFields({}); + const sc = new SchemaComposer(); + const TypeWithoutFieldsTC = sc.getOrCreateTC('Stub'); + TypeWithoutFieldsTC.setFields({}); - const viewerTC = storage.getOrCreateTC('Viewer'); - viewerTC.setFields({ + const ViewerTC = sc.getOrCreateTC('Viewer'); + ViewerTC.setFields({ name: 'String', - stub: typeWithoutFieldsTC, + stub: TypeWithoutFieldsTC, }); /* eslint-disable */ const oldConsoleLog = console.log; global.console.log = jest.fn(); - storage.removeEmptyTypes(viewerTC); + sc.removeEmptyTypes(ViewerTC); expect(console.log).lastCalledWith( "GQC: Delete field 'Viewer.stub' with type 'Stub', cause it does not have fields.", @@ -60,15 +156,15 @@ describe('Storage [Class]', () => { global.console.log = oldConsoleLog; /* eslint-enable */ - expect(viewerTC.hasField('stub')).toBe(false); + expect(ViewerTC.hasField('stub')).toBe(false); }); it('should not produce Maximum call stack size exceeded', () => { - const storage = new SchemaComposer(); - const userTC = storage.getOrCreateTC('User'); - userTC.setField('friend', userTC); + const sc = new SchemaComposer(); + const UserTC = sc.getOrCreateTC('User'); + UserTC.setField('friend', UserTC); - storage.removeEmptyTypes(userTC); + sc.removeEmptyTypes(UserTC); }); }); });