Skip to content

Commit dc9e44d

Browse files
committed
Merge branch 'issue-15-input-types' into dev
2 parents 8b71038 + 08f89e9 commit dc9e44d

File tree

6 files changed

+141
-13
lines changed

6 files changed

+141
-13
lines changed

examples/simple/src/index.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ const PORT = 3000;
1111
const typeDefs = `
1212
type Item @model {
1313
name: String
14+
subItem: SubItem
15+
}
16+
17+
type SubItem {
18+
name: String
1419
}
1520
1621
type Query {
@@ -43,5 +48,3 @@ app.use('/graphql', bodyParser.json(), graphqlExpress({ schema, context }));
4348
app.get('/graphiql', graphiqlExpress({ endpointURL: '/graphql' }));
4449

4550
app.listen(PORT);
46-
47-
console.log(printSchema(schema));

packages/graphql-model-directive/src/ModelDirective.ts

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,14 @@ import {
22
defaultFieldResolver,
33
GraphQLBoolean,
44
GraphQLID,
5-
GraphQLInputObjectType,
65
GraphQLList,
76
GraphQLObjectType,
87
} from 'graphql';
98
import { SchemaDirectiveVisitor } from 'graphql-tools';
109
import * as pluralize from 'pluralize';
1110
import {
11+
addInputTypesForObjectType,
1212
generateFieldNames,
13-
omitResolvers,
1413
Store,
1514
} from './';
1615

@@ -70,14 +69,10 @@ export class ModelDirective extends SchemaDirectiveVisitor {
7069
}
7170

7271
private addInputTypes(type: GraphQLObjectType) {
73-
const names = generateFieldNames(type.name);
74-
75-
// Create an input type with identical fields as the current type.
76-
// Since input type fields can't have resolvers we omit them.
77-
this.schema.getTypeMap()[names.input.type] = new GraphQLInputObjectType({
78-
name: names.input.type,
79-
fields: () => omitResolvers(type.getFields()),
80-
});
72+
// Generate corresponding input types for the given type.
73+
// Each field returning GraphQLObjectType in the given type will also
74+
// have input types generated recursively.
75+
addInputTypesForObjectType(type, this.schema);
8176
}
8277

8378
private addMutations(type: GraphQLObjectType) {
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import {
2+
getNamedType,
3+
GraphQLInputObjectType,
4+
GraphQLList,
5+
GraphQLObjectType,
6+
GraphQLSchema,
7+
} from 'graphql';
8+
import {
9+
omitResolvers,
10+
} from './';
11+
12+
export const toInputObjectName = (name: string): string => `${name}InputType`;
13+
14+
export const isValidInputType = (type, schema: GraphQLSchema): boolean => {
15+
if (type instanceof GraphQLList) {
16+
return isValidInputType(getNamedType(type), schema);
17+
}
18+
return !(type instanceof GraphQLObjectType);
19+
};
20+
21+
export const getInputType = (object: any, schema: GraphQLSchema): GraphQLInputObjectType => {
22+
const type = schema.getType(toInputObjectName(object.type.name));
23+
return type as GraphQLInputObjectType;
24+
};
25+
26+
export const addInputTypesForObjectType = (objectType: GraphQLObjectType, schema: GraphQLSchema) => {
27+
// Fields of an input type cannot have resolvers
28+
// console.log(objectType);
29+
const fields = omitResolvers(objectType.getFields());
30+
31+
// Create the corresponding input type.
32+
// For example, if given `type Foo` will create `input FooInputType`
33+
let inputObjectType = new GraphQLInputObjectType({
34+
name: toInputObjectName(objectType.name),
35+
fields,
36+
});
37+
38+
// Adds the newly created input type to the type map.
39+
//
40+
// Note: the GraphQLObjectType fields of the input type have not yet been replaced.
41+
// However weneed a reference to the input type added to the type map for lookups during recursion.
42+
schema.getTypeMap()[inputObjectType.name] = inputObjectType;
43+
44+
// Iterate over each field in the input type.
45+
// If the field's type is not a GraphQLObjectType or a GraphQLList then it is copied as is.
46+
// If the field's type is a GraphQLObjectType or a GraphQLList
47+
// Get the type which the GraphQLList contains
48+
// Find (or create if not found) the corresponding input type
49+
// Replace the field's type with the input type
50+
const inputObjectFields = Object
51+
.keys(fields)
52+
.reduce((res, key) => {
53+
let field = fields[key];
54+
55+
if (!isValidInputType(field.type, schema)) {
56+
// Check if the input type already exists
57+
const inputType = getInputType(field, schema);
58+
if (inputType) {
59+
field = {
60+
name: inputType.name,
61+
type: field.type instanceof GraphQLList ? new GraphQLList(inputType) : inputType,
62+
};
63+
} else {
64+
// Input type does not exist so we need to create it
65+
const fieldType = schema.getType(field.type.ofType || field.type.name); // `field.type.ofType` is used in case of a list type
66+
const newInputType = addInputTypesForObjectType(fieldType as GraphQLObjectType, schema);
67+
field = {
68+
name: newInputType.name,
69+
type: field.type instanceof GraphQLList ? new GraphQLList(newInputType) : newInputType,
70+
};
71+
}
72+
}
73+
74+
return {
75+
...res,
76+
[key]: field,
77+
};
78+
}, {});
79+
80+
// Replace our original inputObjectType with new one containing the modified fields
81+
82+
inputObjectType = new GraphQLInputObjectType({
83+
name: inputObjectType.name,
84+
fields: inputObjectFields,
85+
});
86+
87+
schema.getTypeMap()[inputObjectType.name] = inputObjectType;
88+
89+
return inputObjectType;
90+
};

packages/graphql-model-directive/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export * from './UniqueDirective';
55
export * from './Store';
66
export * from './generateFieldNames';
77
export * from './omitResolvers';
8+
export * from './addInputTypesForObjectType';
89

910
import { DefaultDirective } from './DefaultDirective';
1011
import { ModelDirective } from './ModelDirective';

packages/graphql-model-directive/test/ModelDirective.test.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,19 @@ describe('ModelDirective', () => {
2121
const typeDefs = `
2222
type Foo @model {
2323
name: String
24+
children: Foo
25+
parent: Bar
26+
}
27+
28+
type Bar {
29+
names: [String]
30+
children: [Dummy]
31+
parent: Foo
32+
}
33+
34+
type Dummy {
35+
names: [String]
36+
parent: Bar
2437
}
2538
${baseTypeDefs}
2639
`;

packages/graphql-model-directive/test/__snapshots__/ModelDirective.test.ts.snap

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,41 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

33
exports[`ModelDirective produces the expected schema 1`] = `
4-
"type Foo {
4+
"type Bar {
5+
names: [String]
6+
children: [Dummy]
7+
parent: Foo
8+
}
9+
10+
input BarInputType {
11+
names: [String]
12+
children: [DummyInputType]
13+
parent: FooInputType
14+
}
15+
16+
type Dummy {
17+
names: [String]
18+
parent: Bar
19+
}
20+
21+
input DummyInputType {
22+
names: [String]
23+
parent: BarInputType
24+
}
25+
26+
type Foo {
527
name: String
28+
children: Foo
29+
parent: Bar
630
731
\\"\\"\\"Unique ID\\"\\"\\"
832
id: ID
933
}
1034
1135
input FooInputType {
1236
name: String
37+
children: FooInputType
38+
parent: BarInputType
1339
1440
\\"\\"\\"Unique ID\\"\\"\\"
1541
id: ID

0 commit comments

Comments
 (0)