Skip to content

Commit

Permalink
feat: add NonNullComposer helper
Browse files Browse the repository at this point in the history
  • Loading branch information
nodkz committed Apr 28, 2019
1 parent 4c00c18 commit 7d31785
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 0 deletions.
19 changes: 19 additions & 0 deletions 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<T extends AnyTypeComposer<any>> {
public ofType: T;

constructor(type: T);

public getType(): GraphQLNonNull<any>;

public getTypeName(): string;

public getUnwrappedTC(): NamedTypeComposer<any>;

public getTypePlural(): ListComposer<NonNullComposer<T>>;

public getTypeNonNull(): NonNullComposer<T>;
}
49 changes: 49 additions & 0 deletions 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<any>> {
+ofType: T;

constructor(type: T): NonNullComposer<T> {
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<any> {
return new GraphQLNonNull(this.ofType.getType());
}

getTypeName(): string {
return `${this.ofType.getTypeName()}!`;
}

getUnwrappedTC(): NamedTypeComposer<any> {
let tc = this;
while (!isNamedTypeComposer(tc)) {
tc = tc.ofType;
}
return tc;
}

getTypePlural(): ListComposer<NonNullComposer<T>> {
return new ListComposer(this);
}

getTypeNonNull(): NonNullComposer<T> {
return this;
}
}
66 changes: 66 additions & 0 deletions 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<any>;
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);
});
});

0 comments on commit 7d31785

Please sign in to comment.