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 typeCap = typeCap.stringTypeStrip(); 102 //pragma(msg, collectTypes!(T)); 103 static foreach(type; collectTypes!(T)) {{ 104 enum typeConst = typeToTypeName!(type); 105 if(typeCap == typeConst) { 106 ret["data"] = typeToJson!(type,T)(); 107 graphql.defaultResolverLog.logf("%s %s %s", typeCap, 108 typeConst, ret["data"] 109 ); 110 goto retLabel; 111 } else { 112 graphql.defaultResolverLog.logf("||||||||||| %s %s", 113 typeCap, typeConst 114 ); 115 } 116 }} 117 retLabel: 118 graphql.defaultResolverLog.logf("%s", ret.toPrettyString()); 119 return ret; 120 }; 121 122 QueryResolver!(Con) schemaResolver = delegate(string name, Json parent, 123 Json args, ref Con context) @safe 124 { 125 //logf("%s %s %s", name, args, parent); 126 Json ret = //returnTemplate(); 127 Json.emptyObject(); 128 ret["data"] = Json.emptyObject(); 129 ret["data"]["types"] = Json.emptyArray(); 130 alias AllTypes = collectTypes!(T); 131 alias NoListOrArray = staticMap!(stripArrayAndNullable, AllTypes); 132 alias FixUp = staticMap!(fixupBasicTypes, NoListOrArray); 133 static if(hasMember!(T, Constants.directives)) { 134 alias NoDirectives = EraseAll!( 135 typeof(__traits(getMember, T, Constants.directives)), 136 FixUp 137 ); 138 } else { 139 alias NoDirectives = FixUp; 140 } 141 alias NoDup = NoDuplicates!(EraseAll!(T, NoDirectives)); 142 static foreach(type; NoDup) {{ 143 Json tmp = typeToJsonImpl!(type,T,type)(); 144 ret["data"]["types"] ~= tmp; 145 }} 146 static if(hasMember!(T, Constants.directives)) { 147 ret["data"][Constants.directives] = 148 directivesToJson!(typeof( 149 __traits(getMember, T, Constants.directives) 150 )); 151 } 152 153 static foreach(tName; ["subscriptionType", 154 "queryType", "mutationType"]) 155 { 156 static if(hasMember!(T, tName)) { 157 ret["data"][tName] = 158 typeToJsonImpl!(typeof(__traits(getMember, T, tName)), 159 T, typeof(__traits(getMember, T, tName))); 160 } 161 } 162 graphql.defaultResolverLog.logf("%s", ret.toPrettyString()); 163 return ret; 164 }; 165 166 graphql.setResolver("queryType", "__type", 167 delegate(string name, Json parent, Json args, ref Con context) @safe 168 { 169 graphql.defaultResolverLog.logf("%s %s %s", name, parent, args); 170 Json tr = typeResolver(name, parent, args, context); 171 Json ret = Json.emptyObject(); 172 ret["data"] = tr["data"]["ofType"]; 173 graphql.defaultResolverLog.logf("%s %s", tr.toPrettyString(), 174 ret.toPrettyString()); 175 return ret; 176 } 177 ); 178 179 graphql.setResolver("queryType", "__schema", schemaResolver); 180 181 graphql.setResolver("__Field", "type", 182 delegate(string name, Json parent, Json args, ref Con context) @safe 183 { 184 graphql.defaultResolverLog.logf("name %s, parent %s, args %s", 185 name, parent, args 186 ); 187 import std.string : capitalize; 188 Json ret = typeResolver(name, parent, args, context); 189 graphql.defaultResolverLog.logf("FIELDDDDD TYPPPPPE %s", 190 ret.toPrettyString() 191 ); 192 return ret; 193 } 194 ); 195 196 graphql.setResolver("__InputValue", "type", 197 delegate(string name, Json parent, Json args, ref Con context) @safe 198 { 199 graphql.defaultResolverLog.logf("%s %s %s", name, parent, args); 200 Json tr = typeResolver(name, parent, args, context); 201 Json ret = Json.emptyObject(); 202 ret["data"] = tr["data"]["ofType"]; 203 graphql.defaultResolverLog.logf("%s %s", tr.toPrettyString(), 204 ret.toPrettyString() 205 ); 206 return ret; 207 } 208 ); 209 210 graphql.setResolver("__Type", "ofType", 211 delegate(string name, Json parent, Json args, ref Con context) @safe 212 { 213 graphql.defaultResolverLog.logf("name %s, parent %s, args %s", 214 name, parent, args 215 ); 216 Json ret = Json.emptyObject(); 217 Json ofType; 218 if(parent.hasPathTo("ofType", ofType)) { 219 ret["data"] = ofType; 220 } 221 graphql.defaultResolverLog.logf("%s", ret); 222 return ret; 223 } 224 ); 225 226 graphql.setResolver("__Type", "interfaces", 227 delegate(string name, Json parent, Json args, ref Con context) @safe 228 { 229 graphql.defaultResolverLog.logf("name %s, parent %s, args %s", 230 name, parent, args 231 ); 232 Json ret = Json.emptyObject(); 233 ret["data"] = Json.emptyObject(); 234 if("kind" in parent 235 && parent["kind"].get!string() == "OBJECT") 236 { 237 ret["data"] = Json.emptyArray(); 238 } 239 if("interfacesNames" !in parent) { 240 return ret; 241 } 242 Json interNames = parent["interfacesNames"]; 243 if(interNames.type == Json.Type.array) { 244 if(interNames.length > 0) { 245 assert(ret["data"].type == Json.Type.array, 246 format("%s", parent.toPrettyString()) 247 ); 248 ret["data"] = Json.emptyArray(); 249 foreach(Json it; interNames.byValue()) { 250 string typeName = it.get!string(); 251 string typeCap = capitalize(typeName); 252 l: switch(typeCap) { 253 static foreach(type; collectTypes!(T)) {{ 254 case typeToTypeName!(type): { 255 //if(typeCap == typeToTypeName!(type)) { 256 alias striped = 257 stripArrayAndNullable!type; 258 graphql.defaultResolverLog.logf("%s %s", 259 typeCap, striped.stringof 260 ); 261 ret["data"] ~= 262 typeToJsonImpl!(striped,T,type)(); 263 break l; 264 } 265 }} 266 default: break; 267 } 268 } 269 } 270 } 271 graphql.defaultResolverLog.logf("__Type.interfaces result %s", 272 ret 273 ); 274 return ret; 275 } 276 ); 277 278 graphql.setResolver("__Type", "possibleTypes", 279 delegate(string name, Json parent, Json args, ref Con context) @safe 280 { 281 graphql.defaultResolverLog.logf("name %s, parent %s, args %s", 282 name, parent, args 283 ); 284 Json ret = Json.emptyObject(); 285 if("possibleTypesNames" !in parent) { 286 ret["data"] = Json(null); 287 return ret; 288 } 289 Json pTypesNames = parent["possibleTypesNames"]; 290 if(pTypesNames.type == Json.Type.array) { 291 graphql.defaultResolverLog.log(); 292 ret["data"] = Json.emptyArray(); 293 foreach(Json it; pTypesNames.byValue()) { 294 string typeName = it.get!string(); 295 string typeCap = capitalize(typeName); 296 l: switch(typeCap) { 297 static foreach(type; collectTypes!(T)) { 298 //if(typeCap == typeToTypeName!(type)) { 299 case typeToTypeName!(type): { 300 alias striped = stripArrayAndNullable!type; 301 graphql.defaultResolverLog.logf("%s %s", 302 typeCap, striped.stringof 303 ); 304 ret["data"] ~= 305 typeToJsonImpl!(striped,T,type)(); 306 break l; 307 } 308 } 309 default: { 310 break; 311 } 312 } 313 } 314 } else { 315 graphql.defaultResolverLog.log(); 316 ret["data"] = Json(null); 317 } 318 return ret; 319 } 320 ); 321 }