Skip to content

Commit

Permalink
feat(TypeComposer, InputTypeComposer): Allow to set type as function …
Browse files Browse the repository at this point in the history
…in field configs

More details here #49
  • Loading branch information
nodkz committed Mar 9, 2017
1 parent 8bfac40 commit 3f9b98e
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 11 deletions.
41 changes: 34 additions & 7 deletions src/__tests__/inputTypeComposer-test.js
Expand Up @@ -49,14 +49,41 @@ describe('InputTypeComposer', () => {
expect(fieldNames).to.include('input3');
});

it('setFields()', () => {
const tc = new InputTypeComposer(objectType);
tc.setFields({
input3: { type: GraphQLString },
input4: { type: GraphQLString },
describe('setFields()', () => {
it('accept regular fields definition', () => {
const tc = new InputTypeComposer(objectType);
tc.setFields({
input3: { type: GraphQLString },
input4: { type: GraphQLString },
});
expect(tc.getFieldNames()).to.not.have.members(['input1', 'input2']);
expect(tc.getFieldNames()).to.have.members(['input3', 'input4']);
expect(tc.getFieldType('input3')).to.equal(GraphQLString);
expect(tc.getFieldType('input4')).to.equal(GraphQLString);
});

it('accept shortand fields definition', () => {
const tc = new InputTypeComposer(objectType);
tc.setFields({
input3: GraphQLString,
input4: 'String',
});
expect(tc.getFieldType('input3')).to.equal(GraphQLString);
expect(tc.getFieldType('input4')).to.equal(GraphQLString);
});

it('accept types as function', () => {
const tc = new InputTypeComposer(objectType);
const typeAsFn = () => GraphQLString;
tc.setFields({
input3: { type: typeAsFn },
});
expect(tc.getFieldType('input3')).to.equal(typeAsFn);

// show provide unwrapped/unhoisted type for graphql
expect(tc.getType()._typeConfig.fields().input3.type)
.to.equal(GraphQLString);
});
expect(tc.getFieldNames()).to.not.have.members(['input1', 'input2']);
expect(tc.getFieldNames()).to.have.members(['input3', 'input4']);
});

it('addFields()', () => {
Expand Down
12 changes: 12 additions & 0 deletions src/__tests__/typeComposer-test.js
Expand Up @@ -96,6 +96,18 @@ describe('TypeComposer', () => {
field5: { field4: true },
});
});

it('accept types as function', () => {
const typeAsFn = () => GraphQLString;
tc.setFields({
input3: { type: typeAsFn },
});
expect(tc.getFieldType('input3')).to.equal(typeAsFn);

// show provide unwrapped/unhoisted type for graphql
expect(tc.getType()._typeConfig.fields().input3.type)
.to.equal(GraphQLString);
});
});

it('addFields()', () => {
Expand Down
7 changes: 5 additions & 2 deletions src/inputTypeComposer.js
Expand Up @@ -7,6 +7,7 @@ import {
import { resolveMaybeThunk } from './utils/misc';
import { deprecate } from './utils/debug';
import { isObject, isString } from './utils/is';
import { unwrapFieldsType, wrapFieldsType } from './utils/typeAsFn';
import TypeMapper from './typeMapper';
import { typeByPath } from './typeByPath';

Expand Down Expand Up @@ -85,7 +86,8 @@ export default class InputTypeComposer {
const fields: Thunk<GraphQLInputFieldConfigMap>
= this.gqType._typeConfig.fields;

const fieldMap:mixed = resolveMaybeThunk(fields);
// $FlowFixMe
const fieldMap:mixed = wrapFieldsType(resolveMaybeThunk(fields));

if (isObject(fieldMap)) {
// $FlowFixMe
Expand Down Expand Up @@ -113,7 +115,8 @@ export default class InputTypeComposer {
this.getTypeName()
);

this.gqType._typeConfig.fields = () => prepearedFields;
// $FlowFixMe
this.gqType._typeConfig.fields = () => unwrapFieldsType(prepearedFields);
delete this.gqType._fields; // if schema was builded, delete defineFieldMap
}

Expand Down
7 changes: 5 additions & 2 deletions src/typeComposer.js
Expand Up @@ -7,6 +7,7 @@ import {
} from 'graphql';
import { resolveMaybeThunk } from './utils/misc';
import { isObject, isFunction, isString } from './utils/is';
import { unwrapFieldsType, wrapFieldsType } from './utils/typeAsFn';
import { deprecate } from './utils/debug';
import Resolver from './resolver';
import { toInputObjectType } from './toInputObjectType';
Expand Down Expand Up @@ -95,7 +96,8 @@ export default class TypeComposer {
const fields: Thunk<GraphQLFieldConfigMap<*, *>>
= this.gqType._typeConfig.fields;

const fieldMap:mixed = resolveMaybeThunk(fields);
// $FlowFixMe
const fieldMap:mixed = wrapFieldsType(resolveMaybeThunk(fields));

if (isObject(fieldMap)) {
// $FlowFixMe
Expand All @@ -118,7 +120,6 @@ export default class TypeComposer {
this.getTypeName()
);

this.gqType._typeConfig.fields = () => prepearedFields;
// if field has a projection option, then add it to projection mapper
Object.keys(prepearedFields).forEach((name) => {
if (prepearedFields[name].projection) {
Expand All @@ -128,6 +129,8 @@ export default class TypeComposer {
}
});

// $FlowFixMe
this.gqType._typeConfig.fields = () => unwrapFieldsType(prepearedFields);
delete this.gqType._fields; // clear builded fields in type
}

Expand Down
60 changes: 60 additions & 0 deletions src/utils/__tests__/typeAsFn-test.js
@@ -0,0 +1,60 @@
import { expect } from 'chai';
import { GraphQLString, GraphQLObjectType } from 'graphql';
import { unwrapFieldsType, wrapFieldsType } from '../typeAsFn';


describe('typeAsFn', () => {
describe('unwrapFieldsType()', () => {
it('should unwrap types from functions', () => {
const fieldMap = {
f1: {
type: GraphQLString,
},
f2: {
type: new GraphQLObjectType({
name: 'MyType',
fields: {
f11: { type: GraphQLString },
},
}),
},
f3: {
type: () => GraphQLString,
},
};
const unwrapped = unwrapFieldsType(fieldMap);
expect(unwrapped.f1.type).to.equal(GraphQLString);
expect(unwrapped.f2.type).to.instanceOf(GraphQLObjectType);
expect(unwrapped.f3.type).to.equal(GraphQLString);
expect(unwrapped.f3._typeFn).to.be.ok;
expect(unwrapped.f3._typeFn()).to.equal(GraphQLString);
});
});

describe('wrapFieldsType()', () => {
it('should set _typeFn to type', () => {
const unwrapped = {
f1: {
type: GraphQLString,
},
f2: {
type: new GraphQLObjectType({
name: 'MyType',
fields: {
f11: { type: GraphQLString },
},
}),
},
f3: {
type: GraphQLString,
_typeFn: () => GraphQLString,
},
};
const wrapped = wrapFieldsType(unwrapped);
expect(wrapped.f1.type).to.equal(GraphQLString);
expect(wrapped.f2.type).to.instanceOf(GraphQLObjectType);
expect(wrapped.f3.type).to.be.ok;
expect(wrapped.f3.type()).to.equal(GraphQLString);
});
});
});
36 changes: 36 additions & 0 deletions src/utils/typeAsFn.js
@@ -0,0 +1,36 @@
/* @flow */
/* eslint-disable no-param-reassign */

import type {
GraphQLInputFieldMap,
GraphQLFieldMap,
} from 'graphql/type/definition';
import { isFunction } from './is';

export type FieldMaps = GraphQLInputFieldMap | GraphQLFieldMap<*, *>;

export function unwrapFieldsType<T: FieldMaps>(
fieldMap: T
): T {
Object.keys(fieldMap).forEach(key => {
if (isFunction(fieldMap[key].type)) {
// $FlowFixMe
fieldMap[key]._typeFn = fieldMap[key].type;
// $FlowFixMe
fieldMap[key].type = fieldMap[key].type();
}
});
return fieldMap;
}

export function wrapFieldsType<T: FieldMaps>(
fieldMap: T
): T {
Object.keys(fieldMap).forEach(key => {
if (fieldMap[key]._typeFn) {
// $FlowFixMe
fieldMap[key].type = fieldMap[key]._typeFn;
}
});
return fieldMap;
}

0 comments on commit 3f9b98e

Please sign in to comment.