1 module graphql.schema.typeconversions;
2 
3 import std.array : empty;
4 import std.algorithm.searching : canFind;
5 import std.conv : to;
6 import std.stdio;
7 import std.traits;
8 import std.meta;
9 import std.typecons : Nullable, nullable;
10 import std.range : ElementEncodingType;
11 
12 import vibe.data.json;
13 
14 import nullablestore;
15 
16 import graphql.schema.types;
17 import graphql.traits;
18 import graphql.uda;
19 import graphql.constants;
20 
21 private enum memsToIgnore = ["__ctor", "toString", "toHash", "opCmp",
22                              "opEquals", "Monitor", "factory", "opAssign"];
23 @safe:
24 
25 template typeToTypeEnum(Type) {
26 	static if(isAggregateType!Type && hasUDA!(Type, GQLDUdaData)
27 			&& getUDAs!(Type, GQLDUdaData)[0].typeKind != TypeKind.UNDEFINED)
28 	{
29 		enum udas = getUDAs!(Type, GQLDUdaData);
30 		static assert(udas.length == 1);
31 		enum GQLDUdaData u = udas[0];
32 		enum typeToTypeEnum = to!string(u.typeKind);
33 	} else static if(is(Type == enum)) {
34 		enum typeToTypeEnum = "ENUM";
35 	} else static if(is(Type == bool)) {
36 		enum typeToTypeEnum = "SCALAR";
37 	} else static if(is(Type : GQLDCustomLeaf!Fs, Fs...)) {
38 		enum typeToTypeEnum = "SCALAR";
39 	} else static if(isFloatingPoint!(Type)) {
40 		enum typeToTypeEnum = "SCALAR";
41 	} else static if(isIntegral!(Type)) {
42 		enum typeToTypeEnum = "SCALAR";
43 	} else static if(isSomeString!Type) {
44 		enum typeToTypeEnum = "SCALAR";
45 	} else static if(is(Type == void)) {
46 		enum typeToTypeEnum = "SCALAR";
47 	} else static if(is(Type == union)) {
48 		enum typeToTypeEnum = "UNION";
49 	} else static if(isAggregateType!Type) {
50 		enum typeToTypeEnum = "OBJECT";
51 	} else {
52 		static assert(false, Type.stringof ~ " not handled");
53 	}
54 }
55 
56 template typeToTypeName(Type) {
57 	import graphql.uda : GQLDCustomLeaf;
58 	static if(is(Type == enum)) {
59 		enum typeToTypeName = Type.stringof;
60 	} else static if(is(Type == bool)) {
61 		enum typeToTypeName = "Boolean";
62 	} else static if(is(Type == GQLDCustomLeaf!Fs, Fs...)) {
63 		enum typeToTypeName = Fs[0].stringof;
64 	} else static if(isFloatingPoint!(Type)) {
65 		enum typeToTypeName = "Float";
66 	} else static if(isIntegral!(Type)) {
67 		enum typeToTypeName = "Int";
68 	} else static if(isSomeString!Type) {
69 		enum typeToTypeName = "String";
70 	} else {
71 		enum typeToTypeName = Type.stringof;
72 	}
73 }
74 
75 unittest {
76 	import std.datetime : Date;
77 
78 	string tS(Date d) {
79 		return "";
80 	}
81 	Date fromS(string s) {
82 		return Date.init;
83 	}
84 	static assert(typeToTypeName!(GQLDCustomLeaf!(Date, tS, fromS)) == "Date");
85 }
86 
87 template typeToParameterTypeName(Type) {
88 	template level2(Type) {
89 		static if(is(Type : Nullable!F, F)) {
90 			enum level2 = typeToTypeName!F;
91 		} else {
92 			enum level2 = typeToTypeName!Type ~ "!";
93 		}
94 	}
95 
96 	template level1(Type) {
97 		static if(isArray!Type && !isSomeString!Type) {
98 			enum level1 = "[" ~ level2!(ElementEncodingType!Type) ~ "]";
99 		} else {
100 			enum level1 = typeToTypeName!Type;
101 		}
102 	}
103 
104 	template level0(Type) {
105 		static if(is(Type : Nullable!F, F)) {
106 			enum level0 = level1!F;
107 		} else {
108 			enum level0 = level1!Type ~ "!";
109 		}
110 	}
111 
112 	template levelM1(Type) {
113 		static if(is(Type : NullableStore!F, F)) {
114 			enum levelM1 = level0!F;
115 		} else {
116 			enum levelM1 = level0!Type;
117 		}
118 	}
119 
120 	enum typeToParameterTypeName = levelM1!Type;
121 }
122 
123 unittest {
124 	import std.datetime : Date;
125 	string tS(Date d) {
126 		return "";
127 	}
128 	Date fS(string s) {
129 		return Date.init;
130 	}
131 	static assert(typeToParameterTypeName!(int) == "Int!");
132 	static assert(typeToParameterTypeName!(Nullable!int) == "Int");
133 	static assert(typeToParameterTypeName!(double) == "Float!");
134 	static assert(typeToParameterTypeName!(Nullable!double) == "Float");
135 	static assert(typeToParameterTypeName!(double[]) == "[Float!]!");
136 	static assert(typeToParameterTypeName!(Nullable!(double)[]) == "[Float]!");
137 	static assert(typeToParameterTypeName!(Nullable!(Nullable!(double)[])) ==
138 			"[Float]");
139 	static assert(typeToParameterTypeName!(GQLDCustomLeaf!(Date,tS,fS))
140 			== "Date!");
141 }
142 
143 unittest {
144 	enum AEnum {
145 		one,
146 		two,
147 		three
148 	}
149 	static assert(typeToParameterTypeName!(AEnum) == "AEnum!");
150 	static assert(typeToParameterTypeName!(Nullable!AEnum) == "AEnum");
151 	static assert(typeToParameterTypeName!(Nullable!(AEnum)[]) == "[AEnum]!");
152 	static assert(typeToParameterTypeName!(Nullable!(Nullable!(AEnum)[])) ==
153 			"[AEnum]");
154 }
155 
156 template isScalarType(Type) {
157 	static if(is(Type == bool)) {
158 		enum isScalarType = true;
159 	} else static if(is(Type == GQLDCustomLeaf!Fs, Fs...)) {
160 		enum isScalarType = true;
161 	} else static if(isFloatingPoint!(Type)) {
162 		enum isScalarType = true;
163 	} else static if(isIntegral!(Type)) {
164 		enum isScalarType = true;
165 	} else static if(isSomeString!Type) {
166 		enum isScalarType = true;
167 	} else static if(is(Type == enum)) {
168 		enum isScalarType = true;
169 	} else {
170 		enum isScalarType = false;
171 	}
172 }
173 
174 template typeToFieldType(Type) {
175 	static if(isArray!Type && !isSomeString!Type) {
176 		enum typeToFieldType = "__listType";
177 	} else static if(is(Type : Nullable!F, F)) {
178 		enum typeToFieldType = F.stringof;
179 	} else static if(is(Type : NullableStore!F, F)) {
180 		enum typeToFieldType = Type.TypeValue.stringof;
181 	} else {
182 		enum typeToFieldType = "__nonNullType";
183 	}
184 }
185 
186 Json typeFields(T)() {
187 	import graphql.uda;
188 	Json ret = Json.emptyArray();
189 	bool[string] fieldsAlreadyIn;
190 	alias TplusParents = AliasSeq!(T, InheritedClasses!T);
191 	static foreach(Type; TplusParents) {{
192 		static foreach(mem; __traits(allMembers, Type)) {{
193 			enum GQLDUdaData udaData = getUdaData!(Type, mem);
194 			static if(!canFind(memsToIgnore, mem)
195 					&& udaData.ignore != Ignore.yes)
196 			{
197 				if(mem !in fieldsAlreadyIn) {
198 					fieldsAlreadyIn[mem] = true;
199 					Json tmp = Json.emptyObject();
200 					tmp[Constants.name] = mem;
201 					// needed for interfacesForType
202 					tmp[Constants.__typename] = "__Field";
203 					tmp[Constants.description] =
204 						udaData.description.getText().empty
205 							? Json(null)
206 							: Json(udaData.description.getText());
207 
208 					tmp[Constants.isDeprecated] =
209 						udaData.deprecationInfo.isDeprecated == IsDeprecated.yes
210 							? true
211 							: false;
212 
213 					tmp[Constants.deprecationReason] =
214 						udaData.deprecationInfo.isDeprecated == IsDeprecated.yes
215 							? Json(udaData.deprecationInfo.deprecationReason)
216 							: Json(null);
217 
218 					tmp[Constants.args] = Json.emptyArray();
219 					static if(isCallable!(__traits(getMember, Type, mem))) {
220 						alias RT = ReturnType!(__traits(getMember, Type, mem));
221 						alias RTS = stripArrayAndNullable!RT;
222 						//tmp[Constants.typenameOrig] = typeToTypeName!(RT);
223 						tmp[Constants.typenameOrig] = typeToParameterTypeName!(RT);
224 
225 						// InputValue
226 						alias paraNames = ParameterIdentifierTuple!(
227 								__traits(getMember, Type, mem)
228 							);
229 						alias paraTypes = Parameters!(
230 								__traits(getMember, Type, mem)
231 							);
232 						alias paraDefs = ParameterDefaults!(
233 								__traits(getMember, Type, mem)
234 							);
235 						static foreach(idx; 0 .. paraNames.length) {{
236 							Json iv = Json.emptyObject();
237 							iv[Constants.name] = paraNames[idx];
238 							// needed for interfacesForType
239 							iv[Constants.__typename] = Constants.__InputValue;
240 							iv[Constants.description] = Json(null);
241 							static if(__traits(compiles, __traits(getAttributes,
242 									Parameters!(__traits(getMember, Type,
243 										mem))[idx ..  idx + 1])))
244 							{
245 								iv[Constants.description] = Json(null);
246 								alias udad = __traits(getAttributes,
247 										Parameters!(__traits(getMember, Type,
248 											mem))[idx ..  idx + 1]);
249 								static if(udad.length == 1) {
250 									enum F = udad[0];
251 									static if(is(typeof(F) == GQLDUdaData)) {
252 										iv[Constants.description] =
253 											F.description.text;
254 									}
255 								}
256 							}
257 							iv[Constants.typenameOrig] =
258 								typeToParameterTypeName!(paraTypes[idx]);
259 							static if(!is(paraDefs[idx] == void)) {
260 								iv[Constants.defaultValue] = serializeToJson(
261 										paraDefs[idx]
262 									)
263 									.toString();
264 							} else {
265 								iv[Constants.defaultValue] = Json(null);
266 							}
267 							tmp[Constants.args] ~= iv;
268 						}}
269 					} else {
270 						tmp[Constants.typenameOrig] =
271 							typeToParameterTypeName!(
272 							//typeToTypeName!(
273 								typeof(__traits(getMember, Type, mem))
274 							);
275 					}
276 					ret ~= tmp;
277 				}
278 			}
279 		}}
280 	}}
281 	//writefln("%s %s", __LINE__, ret.toPrettyString());
282 	return ret;
283 }
284 
285 Json inputFields(Type)() {
286 	Json ret = Json.emptyArray();
287 	alias types = FieldTypeTuple!Type;
288 	alias names = FieldNameTuple!Type;
289 	static foreach(idx; 0 .. types.length) {{
290 		enum GQLDUdaData udaData = getUdaData!(types[idx]);
291 		Json tmp = Json.emptyObject();
292 		tmp[Constants.name] = names[idx];
293 		tmp[Constants.description] = udaData.description.getText().empty
294 				? Json(null)
295 				: Json(udaData.description.getText());
296 
297 		// needed for interfacesForType
298 		tmp[Constants.__typename] = Constants.__InputValue;
299 
300 		//tmp[Constants.typenameOrig] = typeToTypeName!(types[idx]);
301 		tmp[Constants.typenameOrig] = typeToParameterTypeName!(types[idx]);
302 		auto t = __traits(getMember, Type.init, names[idx]);
303 		tmp[Constants.defaultValue] = serializeToJson(t);
304 		ret ~= tmp;
305 	}}
306 	return ret;
307 }
308 
309 Json emptyType() {
310 	Json ret = Json.emptyObject();
311 	ret[Constants.name] = Json(null);
312 	ret[Constants.description] = Json(null);
313 	ret[Constants.fields] = Json(null);
314 	ret[Constants.interfacesNames] = Json(null);
315 	ret[Constants.possibleTypesNames] = Json(null);
316 	ret[Constants.enumValues] = Json(null);
317 	ret["ofType"] = Json(null);
318 	return ret;
319 }
320 
321 Json removeNonNullAndList(Json j) {
322 	immutable string t = j["kind"].get!string();
323 	if(t == "NON_NULL" || t == "LIST") {
324 		return removeNonNullAndList(j["ofType"]);
325 	} else {
326 		return j;
327 	}
328 }
329 
330 // remove the top nullable to find out if we have a NON_NULL or not
331 Json typeToJson(Type,Schema)() {
332 	static if(is(Type : Nullable!F, F)) {
333 		return typeToJson1!(F,Schema,Type)();
334 	} else static if(is(Type : NullableStore!F, F)) {
335 		return typeToJson1!(Type.TypeValue,Schema,Type)();
336 	} else {
337 		Json ret = emptyType();
338 		ret["kind"] = "NON_NULL";
339 		ret[Constants.__typename] = "__Type";
340 		ret["ofType"] = typeToJson1!(Type,Schema,Type)();
341 		return ret;
342 	}
343 }
344 
345 // remove the array is present
346 Json typeToJson1(Type,Schema,Orig)() {
347 	static if(isArray!Type && !isSomeString!Type && !is(Type == enum)) {
348 		Json ret = emptyType();
349 		ret["kind"] = "LIST";
350 		ret[Constants.__typename] = "__Type";
351 		ret["ofType"] = typeToJson2!(ElementEncodingType!Type, Schema, Orig)();
352 		return ret;
353 	} else {
354 		return typeToJsonImpl!(Type, Schema, Orig)();
355 	}
356 }
357 
358 // remove another nullable
359 Json typeToJson2(Type,Schema,Orig)() {
360 	static if(is(Type : Nullable!F, F)) {
361 		return typeToJsonImpl!(F, Schema, Orig)();
362 	} else static if(is(Type : NullableStore!F, F)) {
363 		return typeToJsonImpl!(Type.TypeValue, Schema, Orig)();
364 	} else {
365 		Json ret = emptyType();
366 		ret["kind"] = "NON_NULL";
367 		ret[Constants.__typename] = "__Type";
368 		ret["ofType"] = typeToJsonImpl!(Type, Schema, Orig)();
369 		return ret;
370 	}
371 }
372 
373 template notNullOrArray(T,S) {
374 	static if(is(Nullable!F : T, F)) {
375 		alias notNullOrArray = S;
376 	} else static if(isArray!T) {
377 		alias notNullOrArray = S;
378 	} else {
379 		alias notNullOrArray = T;
380 	}
381 }
382 
383 Json typeToJsonImpl(Type,Schema,Orig)() {
384 	Json ret = Json.emptyObject();
385 	enum string kind = typeToTypeEnum!(stripArrayAndNullable!Type);
386 	ret["kind"] = kind;
387 	ret[Constants.__typename] = "__Type";
388 	ret[Constants.name] = typeToTypeName!Type;
389 
390 	alias TypeOrig = notNullOrArray!(Type,Orig);
391 	enum GQLDUdaData udaData = getUdaData!(TypeOrig);
392 	enum des = udaData.description.text;
393 	ret[Constants.description] = des.empty
394 			? Json(null)
395 			: Json(des);
396 
397 	ret[Constants.isDeprecated] =
398 		udaData.deprecationInfo.isDeprecated == IsDeprecated.yes
399 			? true
400 			: false;
401 
402 	ret[Constants.deprecationReason] =
403 		udaData.deprecationInfo.isDeprecated == IsDeprecated.yes
404 			? Json(udaData.deprecationInfo.deprecationReason)
405 			: Json(null);
406 
407 	// fields
408 	static if((is(Type == class) || is(Type == interface) || is(Type == struct))
409 			&& !is(Type : Nullable!K, K) && !is(Type : NullableStore!K, K)
410 			&& !is(Type : GQLDCustomLeaf!Ks, Ks...))
411 	{
412 		ret[Constants.fields] = typeFields!Type();
413 	} else {
414 		ret[Constants.fields] = Json(null);
415 	}
416 
417 	// inputFields
418 	static if(kind == Constants.INPUT_OBJECT) {
419 		ret[Constants.inputFields] = inputFields!Type();
420 	} else {
421 		ret[Constants.inputFields] = Json(null);
422 	}
423 
424 	// needed to resolve interfaces
425 	static if(is(Type == class) || is(Type == interface)) {
426 		ret[Constants.interfacesNames] = Json.emptyArray();
427 		static foreach(interfaces; InheritedClasses!Type) {{
428 			ret[Constants.interfacesNames] ~= interfaces.stringof;
429 		}}
430 	} else {
431 		ret[Constants.interfacesNames] = Json(null);
432 	}
433 
434 	// needed to resolve possibleTypes
435 	static if(is(Type == class) || is(Type == union) || is(Type == interface)) {
436 		static if(is(Type == union)) {
437 			ret[Constants.possibleTypesNames] = Json.emptyArray();
438 			static foreach(pt; Filter!(isAggregateType, FieldTypeTuple!Type)) {
439 				ret[Constants.possibleTypesNames] ~= pt.stringof;
440 			}
441 		} else {
442 			import graphql.reflection;
443 			// need to search for all types that we support that are derived
444 			// from this type
445 			ret[Constants.possibleTypesNames] = Json.emptyArray();
446 			foreach(tname;
447 					SchemaReflection!Schema.instance.derivatives.get(typeid(Type),
448 																	 null))
449 			{
450 				ret[Constants.possibleTypesNames] ~= tname;
451 			}
452 		}
453 	} else {
454 		ret[Constants.possibleTypesNames] = Json(null);
455 	}
456 
457 	// enumValues
458 	static if(is(Type == enum)) {
459 		ret[Constants.enumValues] = Json.emptyArray();
460 		static foreach(mem; EnumMembers!Type) {{
461 			Json tmp = Json.emptyObject();
462 			tmp[Constants.__TypeKind] = Constants.__EnumValue;
463 			tmp[Constants.name] = Json(to!string(mem));
464 			tmp[Constants.description] = "ENUM_DESCRIPTION_TODO";
465 			tmp[Constants.isDeprecated] = false;
466 			tmp[Constants.deprecationReason] = "ENUM_DEPRECATIONREASON_TODO";
467 			ret[Constants.enumValues] ~= tmp;
468 		}}
469 	} else {
470 		ret[Constants.enumValues] = Json(null);
471 	}
472 
473 	// needed to resolve ofType
474 	static if(is(Type : Nullable!F, F)) {
475 		ret[Constants.ofTypeName] = F.stringof;
476 	} else static if(is(Type : NullableStore!F, F)) {
477 		ret[Constants.ofTypeName] = F.stringof;
478 	} else static if(is(Type : GQLDCustomLeaf!Fs, Fs...)) {
479 		ret[Constants.ofTypeName] = Fs[0].stringof;
480 	} else static if(isArray!Type) {
481 		ret[Constants.ofTypeName] = ElementEncodingType!(Type).stringof;
482 	}
483 
484 	return ret;
485 }
486 
487 @safe unittest {
488 	import std.format : format;
489 	Json r = typeToJson!(string,void)();
490 	Json e = parseJsonString(`
491 		{
492 			"__typename": "__Type",
493 			"possibleTypesNames": null,
494 			"enumValues": null,
495 			"interfacesNames": null,
496 			"kind": "NON_NULL",
497 			"name": null,
498 			"ofType": {
499 				"__typename": "__Type",
500 				"possibleTypesNames": null,
501 				"enumValues": null,
502 				"interfacesNames": null,
503 				"kind": "SCALAR",
504 				"isDeprecated": false,
505 				"deprecationReason": null,
506 				"name": "String",
507 				"description": null,
508 				"inputFields": null,
509 				"ofTypeName": "immutable(char)",
510 				"fields": null
511 			},
512 			"description": null,
513 			"fields": null
514 		}
515 		`);
516 	assert(r == e, format("exp:\n%s\ngot:\n%s", e.toPrettyString(),
517 				r.toPrettyString()));
518 }
519 
520 @safe unittest {
521 	enum FooBar {
522 		foo,
523 		bar
524 	}
525 
526 	import std.format : format;
527 	Json r = typeToJson!(FooBar,void)();
528 	Json e = parseJsonString(`
529 {
530 	"__typename": "__Type",
531 	"possibleTypesNames": null,
532 	"enumValues": null,
533 	"interfacesNames": null,
534 	"kind": "NON_NULL",
535 	"name": null,
536 	"ofType": {
537 		"__typename": "__Type",
538 		"possibleTypesNames": null,
539 		"enumValues": [
540 			{
541 				"description": "ENUM_DESCRIPTION_TODO",
542 				"deprecationReason": "ENUM_DEPRECATIONREASON_TODO",
543 				"__TypeKind": "__EnumValue",
544 				"isDeprecated": false,
545 				"name": "foo"
546 			},
547 			{
548 				"description": "ENUM_DESCRIPTION_TODO",
549 				"deprecationReason": "ENUM_DEPRECATIONREASON_TODO",
550 				"__TypeKind": "__EnumValue",
551 				"isDeprecated": false,
552 				"name": "bar"
553 			}
554 		],
555 		"interfacesNames": null,
556 		"kind": "ENUM",
557 		"isDeprecated": false,
558 		"deprecationReason": null,
559 		"name": "FooBar",
560 		"description": null,
561 		"inputFields": null,
562 		"fields": null
563 	},
564 	"description": null,
565 	"fields": null
566 }
567 		`);
568 	assert(r == e, format("exp:\n%s\ngot:\n%s", e.toPrettyString(),
569 				r.toPrettyString()));
570 }
571 @safe unittest {
572 	import std.format : format;
573 	Json r = typeToJson!(Nullable!string,void)();
574 	Json e = parseJsonString(`
575 		{
576 			"__typename": "__Type",
577 			"possibleTypesNames": null,
578 			"enumValues": null,
579 			"interfacesNames": null,
580 			"kind": "SCALAR",
581 			"isDeprecated": false,
582 			"deprecationReason": null,
583 			"name": "String",
584 			"description": null,
585 			"inputFields": null,
586 			"ofTypeName": "immutable(char)",
587 			"fields": null
588 		}
589 		`);
590 	assert(r == e, format("exp:\n%s\ngot:\n%s", e.toPrettyString(),
591 				r.toPrettyString()));
592 }
593 
594 @safe unittest {
595 	import std.format : format;
596 	Json r = typeToJson!(Nullable!string,void)();
597 	Json e = parseJsonString(`
598 		{
599 			"__typename": "__Type",
600 			"possibleTypesNames": null,
601 			"enumValues": null,
602 			"interfacesNames": null,
603 			"kind": "SCALAR",
604 			"isDeprecated": false,
605 			"deprecationReason": null,
606 			"name": "String",
607 			"description": null,
608 			"inputFields": null,
609 			"ofTypeName": "immutable(char)",
610 			"fields": null
611 		}
612 		`);
613 	assert(r == e, format("exp:\n%s\ngot:\n%s", e.toPrettyString(),
614 				r.toPrettyString()));
615 }
616 
617 Json directivesToJson(Directives)() {
618 	import std.string : stripLeft;
619 	Json ret = Json.emptyArray();
620 	alias TplusParents = AliasSeq!(Directives, InheritedClasses!Directives);
621 	static foreach(Type; TplusParents) {{
622 		static foreach(mem; __traits(allMembers, Type)) {{
623 			static if(!canFind(memsToIgnore, mem)) {
624 				Json tmp = Json.emptyObject();
625 				enum GQLDUdaData udaData = getUdaData!(Type, mem);
626 				tmp[Constants.name] = mem;
627 				// needed for interfacesForType
628 				tmp[Constants.__typename] = Constants.__Directive;
629 				tmp[Constants.description] = udaData.description.getText().empty
630 						? Json(null)
631 						: Json(udaData.description.text);
632 
633 				tmp[Constants.isDeprecated] =
634 					udaData.deprecationInfo.isDeprecated == IsDeprecated.yes
635 						? true
636 						: false;
637 
638 				tmp[Constants.deprecationReason] =
639 					udaData.deprecationInfo.isDeprecated == IsDeprecated.yes
640 						? Json(udaData.deprecationInfo.deprecationReason)
641 						: Json(null);
642 
643 				//tmp[Constants.description] = Json(null);
644 				tmp[Constants.locations] = Json.emptyArray();
645 				tmp[Constants.args] = Json.emptyArray();
646 				static if(isCallable!(__traits(getMember, Type, mem))) {
647 					// InputValue
648 					alias paraNames = ParameterIdentifierTuple!(
649 							__traits(getMember, Type, mem)
650 						);
651 					alias paraTypes = Parameters!(
652 							__traits(getMember, Type, mem)
653 						);
654 					alias paraDefs = ParameterDefaults!(
655 							__traits(getMember, Type, mem)
656 						);
657 					static foreach(idx; 0 .. paraNames.length) {{
658 						Json iv = Json.emptyObject();
659 						// TODO remove the strip left. Its in because the
660 						// two default directives of GraphQL skip and include
661 						// both have one parameter named "if".
662 						iv[Constants.name] = stripLeft(paraNames[idx], "_");
663 						iv[Constants.description] = Json(null);
664 						// needed for interfacesForType
665 						iv[Constants.__typename] = Constants.__InputValue;
666 						iv[Constants.typenameOrig] = typeToTypeName!(paraTypes[idx]);
667 						//iv[Constants.typenameOrig] = typeToParameterTypeName!(paraTypes[idx]);
668 						static if(!is(paraDefs[idx] == void)) {
669 							iv[Constants.defaultValue] = serializeToJson(paraDefs[idx])
670 								.toString();
671 						} else {
672 							iv[Constants.defaultValue] = Json(null);
673 						}
674 						tmp[Constants.args] ~= iv;
675 					}}
676 				}
677 				ret ~= tmp;
678 			}
679 		}}
680 	}}
681 	return ret;
682 }
683 
684 Json getField(Json j, string name) {
685 	import graphql.constants;
686 
687 	if(j.type != Json.Type.object || Constants.fields !in j
688 			|| j[Constants.fields].type != Json.Type.array)
689 	{
690 		return Json.init;
691 	}
692 
693 	foreach(it; j[Constants.fields].byValue) {
694 		immutable string itName = it[Constants.name].get!string();
695 		if(itName == name) {
696 			return it;
697 		}
698 	}
699 	return Json.init;
700 }
701 
702 Json getIntrospectionField(string name) {
703 	import std.format : format;
704 	Json ret = Json.emptyObject();
705 	ret[Constants.typenameOrig] = name == Constants.__typename
706 		? "String"
707 		: name == Constants.__schema
708 			? "__Schema"
709 			: name == Constants.__type
710 				? "__Type"
711 				: format("Not known introspection name '%s'", name);
712 	return ret;
713 }