1 module graphql.schema.resolver; 2 3 import std.array : empty; 4 import std.format : format; 5 import std.meta; 6 import std.traits; 7 import std.typecons : Nullable; 8 import std.experimental.logger; 9 import std.stdio; 10 import std.string : capitalize; 11 12 import vibe.data.json; 13 14 import graphql.schema.types; 15 import graphql.schema.typeconversions; 16 import graphql.helper; 17 import graphql.traits; 18 import graphql.constants; 19 import graphql.graphql; 20 21 @safe: 22 23 alias QueryResolver(Con) = Json delegate(string name, Json parent, 24 Json args, ref Con context) @safe; 25 26 QueryResolver!(Con) buildSchemaResolver(Type, Con)() { 27 return ret; 28 } 29 30 QueryResolver!(Con) buildTypeResolver(Type, Con)() { 31 return ret; 32 } 33 34 GQLDSchema!(Type) toSchema(Type)() { 35 typeof(return) ret = new typeof(return)(); 36 37 static foreach(qms; ["queryType", "mutationType", "subscriptionType"]) {{ 38 GQLDMap cur = new GQLDMap(); 39 cur.name = qms; 40 ret.member[qms] = cur; 41 if(qms == "queryType") { 42 cur.member["__schema"] = ret.__schema; 43 cur.member["__type"] = ret.__nonNullType; 44 } 45 static if(__traits(hasMember, Type, qms)) { 46 alias QMSType = typeof(__traits(getMember, Type, qms)); 47 static foreach(mem; __traits(allMembers, QMSType)) {{ 48 alias MemType = typeof(__traits(getMember, QMSType, mem)); 49 static if(isCallable!(MemType)) {{ 50 GQLDOperation op = qms == "queryType" 51 ? new GQLDQuery() 52 : qms == "mutationType" ? new GQLDMutation() 53 : qms == "subscriptionType" ? new GQLDSubscription() 54 : null; 55 cur.member[mem] = op; 56 assert(op !is null); 57 op.returnType = typeToGQLDType!(ReturnType!(MemType))( 58 ret 59 ); 60 61 alias paraNames = ParameterIdentifierTuple!( 62 __traits(getMember, QMSType, mem) 63 ); 64 alias paraTypes = Parameters!( 65 __traits(getMember, QMSType, mem) 66 ); 67 static foreach(idx; 0 .. paraNames.length) { 68 op.parameters[paraNames[idx]] = 69 typeToGQLDType!(paraTypes[idx])(ret); 70 } 71 }} 72 }} 73 } 74 }} 75 return ret; 76 } 77 78 void setDefaultSchemaResolver(T, Con)(GraphQLD!(T,Con) graphql) { 79 auto typeResolver = delegate(string name, Json parent, 80 Json args, ref Con context) @safe 81 { 82 graphql.defaultResolverLog.logf("%s %s %s", name, args, parent); 83 Json ret = //returnTemplate(); 84 Json.emptyObject(); 85 string typeName; 86 if(Constants.name in args) { 87 typeName = args[Constants.name].get!string(); 88 } 89 if(Constants.typenameOrig in parent) { 90 typeName = parent[Constants.typenameOrig].get!string(); 91 } else if(Constants.name in parent) { 92 typeName = parent[Constants.name].get!string(); 93 } 94 string typeCap; 95 if(typeName.empty) { 96 ret.insertError("unknown type"); 97 goto retLabel; 98 } else { 99 typeCap = typeName; 100 } 101 static foreach(type; collectTypes!(T)) {{ 102 enum typeConst = typeToTypeName!(type); 103 if(typeCap == typeConst) { 104 ret["data"] = typeToJson!(type,T)(); 105 graphql.defaultResolverLog.logf("%s %s %s", typeCap, 106 typeConst, ret["data"] 107 ); 108 goto retLabel; 109 } else { 110 graphql.defaultResolverLog.logf("||||||||||| %s %s", 111 typeCap, typeConst 112 ); 113 } 114 }} 115 retLabel: 116 graphql.defaultResolverLog.logf("%s", ret.toPrettyString()); 117 return ret; 118 }; 119 120 QueryResolver!(Con) schemaResolver = delegate(string name, Json parent, 121 Json args, ref Con context) @safe 122 { 123 //logf("%s %s %s", name, args, parent); 124 Json ret = //returnTemplate(); 125 Json.emptyObject(); 126 ret["data"] = Json.emptyObject(); 127 ret["data"]["types"] = Json.emptyArray(); 128 alias AllTypes = collectTypes!(T); 129 alias NoListOrArray = staticMap!(stripArrayAndNullable, AllTypes); 130 alias FixUp = staticMap!(fixupBasicTypes, NoListOrArray); 131 static if(hasMember!(T, Constants.directives)) { 132 alias NoDirectives = EraseAll!( 133 typeof(__traits(getMember, T, Constants.directives)), 134 FixUp 135 ); 136 } else { 137 alias NoDirectives = FixUp; 138 } 139 alias NoDup = NoDuplicates!(EraseAll!(T, NoDirectives)); 140 static foreach(type; NoDup) {{ 141 Json tmp = typeToJsonImpl!(type,T,type)(); 142 ret["data"]["types"] ~= tmp; 143 }} 144 static if(hasMember!(T, Constants.directives)) { 145 ret["data"][Constants.directives] = 146 directivesToJson!(typeof( 147 __traits(getMember, T, Constants.directives) 148 )); 149 } 150 151 static foreach(tName; ["subscriptionType", 152 "queryType", "mutationType"]) 153 { 154 static if(hasMember!(T, tName)) { 155 ret["data"][tName] = 156 typeToJsonImpl!(typeof(__traits(getMember, T, tName)), 157 T, typeof(__traits(getMember, T, tName))); 158 } 159 } 160 graphql.defaultResolverLog.logf("%s", ret.toPrettyString()); 161 return ret; 162 }; 163 164 graphql.setResolver("queryType", "__type", 165 delegate(string name, Json parent, Json args, ref Con context) @safe 166 { 167 graphql.defaultResolverLog.logf("%s %s %s", name, parent, args); 168 Json tr = typeResolver(name, parent, args, context); 169 Json ret = Json.emptyObject(); 170 ret["data"] = tr["data"]["ofType"]; 171 graphql.defaultResolverLog.logf("%s %s", tr.toPrettyString(), 172 ret.toPrettyString()); 173 return ret; 174 } 175 ); 176 177 graphql.setResolver("queryType", "__schema", schemaResolver); 178 179 graphql.setResolver("__Field", "type", 180 delegate(string name, Json parent, Json args, ref Con context) @safe 181 { 182 graphql.defaultResolverLog.logf("name %s, parent %s, args %s", 183 name, parent, args 184 ); 185 import std.string : capitalize; 186 Json ret = typeResolver(name, parent, args, context); 187 graphql.defaultResolverLog.logf("FIELDDDDD TYPPPPPE %s", 188 ret.toPrettyString() 189 ); 190 return ret; 191 } 192 ); 193 194 graphql.setResolver("__InputValue", "type", 195 delegate(string name, Json parent, Json args, ref Con context) @safe 196 { 197 graphql.defaultResolverLog.logf("%s %s %s", name, parent, args); 198 Json tr = typeResolver(name, parent, args, context); 199 Json ret = Json.emptyObject(); 200 ret["data"] = tr["data"]["ofType"]; 201 graphql.defaultResolverLog.logf("%s %s", tr.toPrettyString(), 202 ret.toPrettyString() 203 ); 204 return ret; 205 } 206 ); 207 208 graphql.setResolver("__Type", "ofType", 209 delegate(string name, Json parent, Json args, ref Con context) @safe 210 { 211 graphql.defaultResolverLog.logf("name %s, parent %s, args %s", 212 name, parent, args 213 ); 214 Json ret = Json.emptyObject(); 215 Json ofType; 216 if(parent.hasPathTo("ofType", ofType)) { 217 ret["data"] = ofType; 218 } 219 graphql.defaultResolverLog.logf("%s", ret); 220 return ret; 221 } 222 ); 223 224 graphql.setResolver("__Type", "interfaces", 225 delegate(string name, Json parent, Json args, ref Con context) @safe 226 { 227 graphql.defaultResolverLog.logf("name %s, parent %s, args %s", 228 name, parent, args 229 ); 230 Json ret = Json.emptyObject(); 231 ret["data"] = Json.emptyObject(); 232 if("kind" in parent 233 && parent["kind"].get!string() == "OBJECT") 234 { 235 ret["data"] = Json.emptyArray(); 236 } 237 if("interfacesNames" !in parent) { 238 return ret; 239 } 240 Json interNames = parent["interfacesNames"]; 241 if(interNames.type == Json.Type.array) { 242 if(interNames.length > 0) { 243 assert(ret["data"].type == Json.Type.array, 244 format("%s", parent.toPrettyString()) 245 ); 246 ret["data"] = Json.emptyArray(); 247 foreach(Json it; interNames.byValue()) { 248 string typeName = it.get!string(); 249 string typeCap = capitalize(typeName); 250 l: switch(typeCap) { 251 static foreach(type; collectTypes!(T)) {{ 252 case typeToTypeName!(type): { 253 //if(typeCap == typeToTypeName!(type)) { 254 alias striped = 255 stripArrayAndNullable!type; 256 graphql.defaultResolverLog.logf("%s %s", 257 typeCap, striped.stringof 258 ); 259 ret["data"] ~= 260 typeToJsonImpl!(striped,T,type)(); 261 break l; 262 } 263 }} 264 default: break; 265 } 266 } 267 } 268 } 269 graphql.defaultResolverLog.logf("__Type.interfaces result %s", 270 ret 271 ); 272 return ret; 273 } 274 ); 275 276 graphql.setResolver("__Type", "possibleTypes", 277 delegate(string name, Json parent, Json args, ref Con context) @safe 278 { 279 graphql.defaultResolverLog.logf("name %s, parent %s, args %s", 280 name, parent, args 281 ); 282 Json ret = Json.emptyObject(); 283 if("possibleTypesNames" !in parent) { 284 ret["data"] = Json(null); 285 return ret; 286 } 287 Json pTypesNames = parent["possibleTypesNames"]; 288 if(pTypesNames.type == Json.Type.array) { 289 graphql.defaultResolverLog.log(); 290 ret["data"] = Json.emptyArray(); 291 foreach(Json it; pTypesNames.byValue()) { 292 string typeName = it.get!string(); 293 string typeCap = capitalize(typeName); 294 l: switch(typeCap) { 295 static foreach(type; collectTypes!(T)) { 296 //if(typeCap == typeToTypeName!(type)) { 297 case typeToTypeName!(type): { 298 alias striped = stripArrayAndNullable!type; 299 graphql.defaultResolverLog.logf("%s %s", 300 typeCap, striped.stringof 301 ); 302 ret["data"] ~= 303 typeToJsonImpl!(striped,T,type)(); 304 break l; 305 } 306 } 307 default: { 308 break; 309 } 310 } 311 } 312 } else { 313 graphql.defaultResolverLog.log(); 314 ret["data"] = Json(null); 315 } 316 return ret; 317 } 318 ); 319 }