} //method private bool SetupFieldResolverMethod(ObjectTypeDef typeDef, FieldDef field, MethodInfo resolverMethod, Attribute sourceAttr) { var retType = resolverMethod.ReturnType; var returnsTask = retType.IsGenericType && retType.GetGenericTypeDefinition() == typeof(Task <>); Func <object, object> taskResultReader = null; if (returnsTask) { retType = retType.GetGenericArguments()[0]; taskResultReader = ReflectionHelper.CompileTaskResultReader(retType); } // validate return type if (!CheckReturnTypeCompatible(retType, field, resolverMethod)) { return(false); } field.Resolver = new ResolverMethodInfo() { SourceAttribute = sourceAttr, Method = resolverMethod, ResolverClass = resolverMethod.DeclaringType, ReturnsTask = returnsTask, TaskResultReader = taskResultReader }; if (returnsTask) { field.Flags |= FieldFlags.ReturnsTask; } if (typeDef is ObjectTypeDef otd && otd.TypeRole == ObjectTypeRole.Data) { field.Flags |= FieldFlags.HasParentArg; } ValidateResolverMethodArguments(typeDef, field); return(!_model.HasErrors); }
public static ObjectTypeMapping FindObjectTypeMapping(this ObjectTypeDef typeDef, Type fromType) { var mapping = typeDef.Mappings.FirstOrDefault(m => m.EntityType == fromType || m.EntityType.IsAssignableFrom(fromType)); return(mapping); }
internal void Init(ObjectTypeDef objectTypeDef, IList <MappedField> fields) { MappedTypeDef = objectTypeDef; Fields = fields; _values = new object[fields.Count]; _valuesMask = BitSet.Create(fields.Count); }
private void ReassignFieldIndexes(ObjectTypeDef typeDef) { for (int i = 0; i < typeDef.Fields.Count; i++) { typeDef.Fields[i].Index = i; } }
// Might be called for ObjectType or Interface (for intf - just to check fields exist) private void MapObjectSelectionSubset(SelectionSubset selSubset, ObjectTypeDef objectTypeDef, IList <RequestDirective> directives, bool isForUnion = false) { var mappedFields = MapSelectionItems(selSubset.Items, objectTypeDef, directives, isForUnion); selSubset.MappedItemSets.Add(new MappedObjectItemSet() { ObjectTypeDef = objectTypeDef, Items = mappedFields }); }
} //method private void RegisterSpecialObjectTypeIfProvided(Type type, ObjectTypeRole typeRole, GraphQLModule module) { if (type == null) { return; } var typeName = $"{module.Name}_{type.Name}"; var typeDef = new ObjectTypeDef(typeName, type, GraphQLModelObject.EmptyAttributeList, module, typeRole); _model.Types.Add(typeDef); }
private async Task ExecuteObjectsSelectionSubsetAsync(IList <OutputObjectScope> parentScopes, ObjectTypeDef objTypeDef, SelectionSubset subSet) { var outItemSet = subSet.MappedItemSets.FirstOrDefault(fi => fi.ObjectTypeDef == objTypeDef); var mappedFields = _requestContext.GetIncludedMappedFields(outItemSet); // init scopes foreach (var scope in parentScopes) { scope.Init(objTypeDef, mappedFields); } for (int fldIndex = 0; fldIndex < mappedFields.Count; fldIndex++) { var mappedField = mappedFields[fldIndex]; var returnsComplexType = mappedField.FieldDef.Flags.IsSet(FieldFlags.ReturnsComplexType); var fieldContext = new FieldContext(_requestContext, this, mappedField, fldIndex, parentScopes); foreach (var scope in fieldContext.AllParentScopes) { if (fieldContext.BatchResultWasSet && scope.HasValue(fldIndex)) { continue; } fieldContext.CurrentScope = scope; object result = null; switch (mappedField.FieldDef.ExecutionType) { case FieldExecutionType.Reader: result = InvokeFieldReader(fieldContext, fieldContext.CurrentScope.Entity); break; case FieldExecutionType.Resolver: result = await InvokeResolverAsync(fieldContext); break; } var outValue = fieldContext.ConvertToOuputValue(result); if (!fieldContext.BatchResultWasSet) { scope.SetValue(fldIndex, outValue); } } //foreach scope // if there are any non-null object-type results, add this field context to this special list // to execute selection subsets in the next round. if (returnsComplexType && fieldContext.AllResultScopes.Count > 0) { _executedObjectFieldContexts.Add(fieldContext); } } //foreach fldIndex } //method
private ObjectTypeDef BuildRootSchemaObject(string name, ObjectTypeRole typeRole, ObjectTypeRole moduleTypeRole) { var allFields = _model.Types.OfType <ObjectTypeDef>() .Where(t => t.TypeRole == moduleTypeRole) .SelectMany(t => t.Fields).ToList(); if (allFields.Count == 0) { return(null); } // TODO: add check for name duplicates var rootObj = new ObjectTypeDef(name, null, GraphQLModelObject.EmptyAttributeList, null, typeRole); rootObj.Fields.AddRange(allFields); RegisterTypeDef(rootObj); rootObj.Hidden = false; return(rootObj); }
} //method private void AddTypeNameField(ObjectTypeDef objTypeDef) { var fld = new FieldDef(objTypeDef, "__typename", _stringNotNull); fld.Flags |= FieldFlags.Hidden; objTypeDef.Fields.Add(fld); // field mappings var typeName = objTypeDef.Name; Func <object, object> reader = obj => typeName; foreach (var mp in objTypeDef.Mappings) { mp.FieldResolvers.Add(new FieldResolverInfo() { TypeMapping = mp, Field = fld, ResolverFunc = reader }); } }
private bool AssignOperationDef(GraphQLOperation op) { ObjectTypeDef opDef = null; switch (op.OperationType) { case OperationType.Query: opDef = _model.QueryType; break; case OperationType.Mutation: opDef = _model.MutationType; break; case OperationType.Subscription: opDef = _model.SubscriptionType; break; } if (opDef == null) { AddError($"Operation '{op.OperationType}' is not defined in schema. Operation: '{op.Name}'.", op); return(false); } op.OperationTypeDef = opDef; return(true); }
private ObjectTypeDef BuildRootSchemaObject(string name, TypeRole typeRole, TypeRole moduleTypeRole) { var allModuleAggrTypes = _model.Types.OfType <ObjectTypeDef>() .Where(t => t.TypeRole == moduleTypeRole) .ToList(); if (allModuleAggrTypes.Count == 0) { return(null); } // create root object (ex: Query type) var rootObjTypeDef = new ObjectTypeDef(name, null, GraphQLModelObject.EmptyAttributeList, null, typeRole); RegisterTypeDef(rootObjTypeDef); var mapping = new ObjectTypeMapping(rootObjTypeDef, null); rootObjTypeDef.Mappings.Add(mapping); // copy resolvers foreach (var aggrType in allModuleAggrTypes) { mapping.FieldResolvers.AddRange(aggrType.Mappings[0].FieldResolvers); } // collect all fields from collected resolvers var allFields = mapping.FieldResolvers.Select(fr => fr.Field).ToList(); rootObjTypeDef.Fields.AddRange(allFields); // check for name duplicates var fieldNameDupes = rootObjTypeDef.Fields.Select(f => f.Name).GroupBy(fn => fn).Where(g => g.Count() > 1).ToList(); if (fieldNameDupes.Count > 0) { string dupesAll = string.Join(",", fieldNameDupes.Select(g => g.Key)); AddError($"Duplicate fields defined at top-level type {typeRole}, field names: {dupesAll}"); } // important: re-assign Index value for all fields; we moved fields to aggregate Query, Mutation // objects, so their indexes changed ReassignFieldIndexes(rootObjTypeDef); return(rootObjTypeDef); }
private MappedFragmentSpread MapFragmentSpread(FragmentSpread fs, ObjectTypeDef objectTypeDef, bool isForUnion) { // if it is not inline fragment, it might need to map to FragmentDef; inline fragments are auto-mapped at construction if (fs.Fragment == null) { fs.Fragment = GetFragmentDef(fs.Name); } if (fs.Fragment == null) { AddError($"Fragment {fs.Name} not defined.", fs); return(null); } // inline fragments are mapped in-place, here. // we need to map them here, once we know the target type if (fs.IsInline) { var onTypeRef = fs.Fragment.OnTypeRef; var skip = onTypeRef != null && onTypeRef.TypeDef != objectTypeDef; if (skip) { return(null); } MapObjectSelectionSubset(fs.Fragment.SelectionSubset, objectTypeDef, fs.Directives, isForUnion); } // there must be mapped field set now var mappedFragmItemSet = fs.Fragment.SelectionSubset.MappedItemSets.FirstOrDefault(fset => fset.ObjectTypeDef == objectTypeDef); if (mappedFragmItemSet == null) { AddError($"FATAL: Could not retrieve mapped item list for fragment spread {fs.Name}", fs, ErrorCodes.ServerError); return(null); } var mappedSpread = new MappedFragmentSpread(fs, mappedFragmItemSet.Items); return(mappedSpread); }
// Might be called for ObjectType or Interface (for intf - just to check fields exist) private void MapObjectSelectionSubset(SelectionSubset selSubset, ObjectTypeDef objectTypeDef, bool isForUnion = false) { // Map arguments on fields, add directives, map fragments foreach (var item in selSubset.Items) { AddRuntimeRequestDirectives(item); switch (item) { case SelectionField selFld: var fldDef = objectTypeDef.Fields[selFld.Name]; if (fldDef == null) { // if field not found, the behavior depends if it is a union; it is error for a union if (!isForUnion) { AddError($"Field '{selFld.Name}' not found on type '{objectTypeDef.Name}'.", selFld); } continue; } selFld.MappedArgs = MapArguments(selFld.Args, fldDef.Args, selFld); AddRuntimeModelDirectives(fldDef); MapSelectionFieldSubsetIfPresent(selFld, fldDef.TypeRef.TypeDef); break; case FragmentSpread fspread: // Named fragment refs are NOT set by parser; parser sets only Inline fragmDefs; we need to match named fragms here if (!fspread.IsInline && fspread.Fragment == null) { fspread.Fragment = GetFragmentDef(fspread.Name); if (fspread.Fragment == null) { AddError($"Fragment {fspread.Name} not defined.", fspread); } } break; } //switch } //foreach item if (_requestContext.Failed) { return; } // Now create mappings for all possible entity types foreach (var typeMapping in objectTypeDef.Mappings) { // It is possible mapped set already exists (with unions and especially fragments) var existing = selSubset.MappedSubSets.FirstOrDefault(ms => ms.Mapping == typeMapping); if (existing != null) { continue; } var mappedItems = new List <MappedSelectionItem>(); foreach (var item in selSubset.Items) { switch (item) { case SelectionField selFld: var fldDef = typeMapping.TypeDef.Fields[selFld.Name]; if (fldDef == null) { // it is not error, it should have been caught earlier; it is unmatch for union continue; } var fldResolver = typeMapping.GetResolver(fldDef); //.FirstOrDefault(fr => fr.Field.Name == selFld.Name); var mappedFld = new MappedSelectionField(selFld, fldResolver); mappedItems.Add(mappedFld); break; case FragmentSpread fs: var onType = fs.Fragment.OnTypeRef?.TypeDef; var skip = onType != null && onType.Kind == TypeKind.Object && onType != objectTypeDef; if (skip) { continue; } if (fs.IsInline) // only inline fragments should be mapped from here; named fragments are mapped separately, upfront { MapObjectSelectionSubset(fs.Fragment.SelectionSubset, objectTypeDef, isForUnion); } var mappedSpread = new MappedFragmentSpread(fs); mappedItems.Add(mappedSpread); break; } //switch } //foreach item selSubset.MappedSubSets.Add(new MappedSelectionSubSet() { Mapping = typeMapping, MappedItems = mappedItems }); } //foreach typeMapping }
// Might be called for ObjectType or Interface (for intf - just to check fields exist) private List <MappedSelectionItem> MapSelectionItems(IList <SelectionItem> selItems, ObjectTypeDef objectTypeDef, IList <RequestDirective> ownerDirectives = null, bool isForUnion = false) { var mappedItems = new List <MappedSelectionItem>(); foreach (var item in selItems) { switch (item) { case SelectionField selFld: var fldDef = objectTypeDef.Fields.FirstOrDefault(f => f.Name == selFld.Name); if (fldDef == null) { // if field not found, the behavior depends if it is a union; it is error for a union if (!isForUnion) { AddError($"Field '{selFld.Name}' not found on type '{objectTypeDef.Name}'.", selFld); } continue; } var mappedArgs = MapArguments(selFld.Args, fldDef.Args, selFld); var mappedFld = new MappedField(selFld, fldDef, mappedArgs); AddRuntimeModelDirectives(mappedFld); AddRuntimeRequestDirectives(mappedFld); mappedItems.Add(mappedFld); ValidateMappedFieldAndProcessSubset(mappedFld); break; case FragmentSpread fs: var mappedSpread = MapFragmentSpread(fs, objectTypeDef, isForUnion); if (mappedSpread != null)// null is indicator of error { AddRuntimeRequestDirectives(mappedSpread); mappedItems.Add(mappedSpread); } break; }//switch var allDirs = ownerDirectives.MergeLists(item.Directives); } //foreach item return(mappedItems); }