From 7d31785b16553cbab32a03ed8292a58a93b2c8fb Mon Sep 17 00:00:00 2001 From: nodkz Date: Mon, 29 Apr 2019 00:39:36 +0600 Subject: [PATCH] feat: add `NonNullComposer` helper --- src/NonNullComposer.d.ts | 19 ++++++++ src/NonNullComposer.js | 49 ++++++++++++++++++++ src/__tests__/NonNullComposer-test.js | 66 +++++++++++++++++++++++++++ 3 files changed, 134 insertions(+) create mode 100644 src/NonNullComposer.d.ts create mode 100644 src/NonNullComposer.js create mode 100644 src/__tests__/NonNullComposer-test.js diff --git a/src/NonNullComposer.d.ts b/src/NonNullComposer.d.ts new file mode 100644 index 00000000..06813ea2 --- /dev/null +++ b/src/NonNullComposer.d.ts @@ -0,0 +1,19 @@ +import { GraphQLNonNull } from 'graphql'; +import { ListComposer } from './ListComposer'; +import { AnyTypeComposer, NamedTypeComposer } from './utils/typeHelpers'; + +export class NonNullComposer> { + public ofType: T; + + constructor(type: T); + + public getType(): GraphQLNonNull; + + public getTypeName(): string; + + public getUnwrappedTC(): NamedTypeComposer; + + public getTypePlural(): ListComposer>; + + public getTypeNonNull(): NonNullComposer; +} diff --git a/src/NonNullComposer.js b/src/NonNullComposer.js new file mode 100644 index 00000000..2955c19b --- /dev/null +++ b/src/NonNullComposer.js @@ -0,0 +1,49 @@ +/* @flow strict */ + +import invariant from 'graphql/jsutils/invariant'; +import { GraphQLNonNull } from './graphql'; +import { + isNamedTypeComposer, + type AnyTypeComposer, + type NamedTypeComposer, +} from './utils/typeHelpers'; +import { ListComposer } from './ListComposer'; + +export class NonNullComposer<+T: AnyTypeComposer> { + +ofType: T; + + constructor(type: T): NonNullComposer { + invariant( + !(type instanceof NonNullComposer), + 'You provide NonNull value to NonNullCOmposer constructor. Nesting NonNull is not allowed.' + ); + this.ofType = type; + + // alive proper Flow type casting in autosuggestions for class with Generics + /* :: return this; */ + } + + getType(): GraphQLNonNull { + return new GraphQLNonNull(this.ofType.getType()); + } + + getTypeName(): string { + return `${this.ofType.getTypeName()}!`; + } + + getUnwrappedTC(): NamedTypeComposer { + let tc = this; + while (!isNamedTypeComposer(tc)) { + tc = tc.ofType; + } + return tc; + } + + getTypePlural(): ListComposer> { + return new ListComposer(this); + } + + getTypeNonNull(): NonNullComposer { + return this; + } +} diff --git a/src/__tests__/NonNullComposer-test.js b/src/__tests__/NonNullComposer-test.js new file mode 100644 index 00000000..3c74636c --- /dev/null +++ b/src/__tests__/NonNullComposer-test.js @@ -0,0 +1,66 @@ +/* @flow strict */ + +import { schemaComposer } from '..'; +import { GraphQLNonNull } from '../graphql'; +import { NonNullComposer } from '../NonNullComposer'; +import { ListComposer } from '../ListComposer'; +import { ObjectTypeComposer } from '../ObjectTypeComposer'; + +beforeEach(() => { + schemaComposer.clear(); +}); + +describe('NonNullComposer', () => { + let tc: NonNullComposer; + beforeEach(() => { + tc = new NonNullComposer( + schemaComposer.createTC(` + type User { + name: String + } + `) + ); + }); + + it('no nesting NonNull', () => { + expect(() => { + // eslint-disable-next-line no-new + new NonNullComposer((tc: any)); + }).toThrow('Nesting NonNull is not allowed'); + }); + + it('getType()', () => { + const type: any = tc.getType(); + expect(type).toBeInstanceOf(GraphQLNonNull); + expect(type.ofType.name).toBe('User'); + }); + + it('getTypeName()', () => { + expect(tc.getTypeName()).toBe('User!'); + }); + + it('getTypePlural() should return wrapped type with ListComposer', () => { + const tc2 = tc.getTypePlural(); + expect(tc2).toBeInstanceOf(ListComposer); + expect(tc2.ofType).toBe(tc); + expect(tc2.getTypeName()).toBe('[User!]'); + }); + + it('getTypeNonNull() should return wrapped type with NonNullComposer', () => { + expect(tc.getTypeNonNull()).toBeInstanceOf(NonNullComposer); + // should return itself + expect(tc.getTypeNonNull()).toBe(tc); + }); + + it('getUnwrappedTC() should return NamedTypeComposer', () => { + const UserTC1 = tc.getUnwrappedTC(); + expect(UserTC1).toBeInstanceOf(ObjectTypeComposer); + expect(UserTC1.getTypeName()).toBe('User'); + + // should unwrap deeply wrapped Types + const tc2: any = schemaComposer.typeMapper.convertSDLWrappedTypeName('[User!]!'); + const UserTC2 = tc2.getUnwrappedTC(); + expect(UserTC2.getTypeName()).toBe('User'); + expect(UserTC1).toBe(UserTC2); + }); +});