-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathtransform.js
153 lines (136 loc) · 3.74 KB
/
transform.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/**
* Mapping between GQL primitive types and JSON Schema property types
*
* @type {<type>}
*/
const PRIMITIVES = {
Int: 'integer',
Float: 'number',
String: 'string',
Boolean: 'boolean',
ID: 'string'
};
/**
* returns a JSON schema property type for a given GQL field type
*
* @param {object} type The GQL type object
* @return {Object} the property type object or a reference to a type definition
*/
const getPropertyType = type => {
switch (type.kind) {
case 'NonNullType':
return Object.assign(getPropertyType(type.type), { required: true });
case 'ListType':
return {
type: 'array',
items: {
type: getPropertyType(type.type)
}
}
default:
if (type.name.value in PRIMITIVES) {
return {
type: PRIMITIVES[type.name.value],
required: false
};
}
else {
return { $ref: `#/definitions/${type.name.value}` };
}
}
}
/**
* converts the GQL arguments array into a plain JSON schema array
*
* @param {Array} _arguments The GQL arguments
* @return {Object} a plain JSON array
*/
const toFieldArguments = _arguments => {
return _arguments.map(a => {
return {
title: a.name.value,
type: getPropertyType(a.type),
defaultValue: a.defaultValue
};
});
}
/**
* maps a GQL type field onto a JSON Schema property
*
* @param {object} field The GQL field object
* @return {Object} a plain JS object containing the property schema or a reference to another definition
*/
const toSchemaProperty = field => {
let propertyType = getPropertyType(field.type);
if ('$ref' in propertyType) propertyType = { allOf: [propertyType, { title: field.name.value }] };
return Object.assign(
propertyType,
{ title: field.name.value },
field.arguments ? { arguments: toFieldArguments(field.arguments) } : {}
);
}
/**
* Converts a single GQL definition into a plain JS schema object
*
* @param {Object} definition The GQL definition object
* @return {Object} A plain JS schema object
*/
const toSchemaObject = definition => {
if (definition.kind === 'ScalarTypeDefinition') {
return {
title: definition.name.value,
type: 'GRAPHQL_SCALAR'
}
}
else if (definition.kind === 'UnionTypeDefinition') {
return {
title: definition.name.value,
type: 'GRAPHQL_UNION',
oneOf: definition.types.map(getPropertyType)
}
}
else if (definition.kind === 'EnumTypeDefinition') {
return {
title: definition.name.value,
type: 'GRAPHQL_ENUM',
enum: definition.values.map(v => v.name.value)
};
}
const fields = definition.fields.map(toSchemaProperty);
const properties = {};
for (let f of fields) properties[f.title] = f.allOf ? { allOf: f.allOf } : f;
const required = fields
.filter(f => f.required)
.map(f => f.title);
let schemaObject = {
title: definition.name.value,
type: 'object',
properties,
required,
};
if (definition.kind === 'InputObjectTypeDefinition') {
Object.assign(schemaObject, { input: true });
}
return schemaObject;
}
/**
* GQL -> JSON Schema transform
*
* @param {Document} document The GraphQL document returned by the parse function of graphql/language
* @return {object} A plain JavaScript object which conforms to JSON Schema
*/
const transform = document => {
// ignore directives
const definitions = document.definitions
.filter(d => d.kind !== 'DirectiveDefinition')
.map(toSchemaObject);
const schema = {
$schema: 'http://json-schema.org/draft-04/schema#',
definitions: {}
};
for (let def of definitions) {
schema.definitions[def.title] = def;
}
return schema;
};
module.exports = transform;