// merge with prev method InvokeResolverAsync private object InvokeResolverFunc(FieldContext fieldContext) { var propReader = fieldContext.MappedField.Resolver.ResolverFunc; var entity = fieldContext.CurrentParentScope.Entity; var result = propReader(entity); return(result); }
internal static void ThrowRequestCancelled(this FieldContext fieldContext) { var reqCtx = (RequestContext)fieldContext.RequestContext; var err = new GraphQLError($"Request cancelled", fieldContext.GetFullRequestPath(), fieldContext.SelectionField.SourceLocation, type: "Cancel"); reqCtx.AddError(err); throw new AbortRequestException(); }
internal static void ThrowObjectCountExceededQuota(this FieldContext fieldContext) { var reqCtx = (RequestContext)fieldContext.RequestContext; var quota = reqCtx.Quota; var err = new GraphQLError($"Output object count exceeded maximum ({quota.MaxOutputObjects}) allowed by quota.", fieldContext.GetFullRequestPath(), fieldContext.SelectionField.SourceLocation, type: "Quota"); reqCtx.AddError(err); throw new AbortRequestException(); }
internal static void ThrowFieldDepthExceededQuota(this FieldContext fieldContext) { var reqCtx = (RequestContext)fieldContext.RequestContext; var quota = reqCtx.Quota; var sourceLoc = fieldContext.SourceLocation; var err = new GraphQLError($"Query depth exceeded maximum ({quota.MaxDepth}) allowed by quota.", fieldContext.GetFullRequestPath(), sourceLoc, type: "Quota"); reqCtx.AddError(err); throw new AbortRequestException(); }
private async Task ExecuteMappedSelectionSubsetAsync(MappedSelectionSubSet mappedSubSet, IList <ObjectTypeDef> possibleTypes, IList <OutputObjectScope> parentScopes) { foreach (var mappedItem in mappedSubSet.MappedItems) { if (mappedItem.Item.OnExecuting(_requestContext, out var args) && args.Skip) { continue; } // if it is a fragment spread, make recursive call to process fragment fields if (mappedItem.Item.Kind == SelectionItemKind.FragmentSpread) { var mappedSpread = (MappedFragmentSpread)mappedItem; var objTypeDef = mappedSubSet.Mapping.TypeDef; var fragmSelSubset = mappedSpread.Spread.Fragment.SelectionSubset; var entType = mappedSubSet.Mapping.EntityType; var mappedFragmSubset = GetMappedSubset(fragmSelSubset, possibleTypes, entType, mappedSpread.Spread); await ExecuteMappedSelectionSubsetAsync(mappedFragmSubset, possibleTypes, parentScopes); //call self recursively continue; } // It is a plain field var mappedField = (MappedSelectionField)mappedItem; var fieldContext = new FieldContext(_requestContext, this, mappedField, parentScopes); var selFieldKey = mappedField.Field.Key; // Process each scope for the field foreach (var scope in parentScopes) { if (fieldContext.BatchResultWasSet && scope.ContainsKey(selFieldKey)) { continue; } fieldContext.SetCurrentParentScope(scope); var fldDef = fieldContext.FieldDef; object result = await InvokeResolverAsync(fieldContext); // if batched result was not set, save value in scope if (!fieldContext.BatchResultWasSet) { scope.SetValue(selFieldKey, result); } } //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 (mappedField.Field.SelectionSubset != null && fieldContext.AllResultScopes.Count > 0) { _executedObjectFieldContexts.Add(fieldContext); } } //foreach mappedItem } //method
private object InvokeFieldReader(FieldContext fieldContext, object parent) { try { var reader = fieldContext.FieldDef.Reader; var result = reader(parent); return(result); } catch (TargetInvocationException tex) { // sync call goes here var origExc = tex.InnerException ?? tex; fieldContext.AddError(origExc, ErrorCodes.ResolverError); throw new AbortRequestException(); } }
public static void AddError(this FieldContext fieldContext, Exception exc, string errorType) { var reqCtx = (RequestContext)fieldContext.RequestContext; var path = fieldContext.GetFullRequestPath(); var err = new GraphQLError(exc.Message, path, fieldContext.SelectionField.SourceLocation, type: errorType); var withDet = reqCtx.Server.Settings.Options.IsSet(GraphQLServerOptions.ReturnExceptionDetails); if (withDet) { err.Extensions["Details"] = exc.ToText(); } reqCtx.AddError(err, exc); }
private async Task <object> InvokeResolverAsync(FieldContext fieldContext) { try { // Invoke resolver object result; object convResult; if (fieldContext.MappedField.Resolver.ResolverFunc != null) { result = InvokeResolverFunc(fieldContext); } else { result = await InvokeResolverMethodAsync(fieldContext); } // if it was batch result set, and result is null, lookup result from current scope // we still need to return real result if (result == null && fieldContext.BatchResultWasSet) { var fldKey = fieldContext.MappedField.Field.Key; fieldContext.CurrentParentScope.TryGetValue(fldKey, out convResult); // value already converted } else { convResult = fieldContext.ConvertToOutputValue(result); } // check for null in non-null field if (convResult == null && fieldContext.FieldDef.TypeRef.IsNotNull) { var selFld = fieldContext.MappedField.Field; _requestContext.AddError($"Server error: resolver for non-nullable field '{selFld.Key}' returned null.", selFld, ErrorCodes.ServerError); } return(convResult); } catch (TargetInvocationException tex) { // sync call goes here var origExc = tex.InnerException; if (origExc is AbortRequestException) { throw origExc; } AddError(fieldContext, origExc, ErrorCodes.ResolverError); Fail(); // throws return(null); //never happens } catch (AbortRequestException) { throw; } catch (Exception ex) { AddError(fieldContext, ex, ErrorCodes.ResolverError); Fail(); return(null); //never happens } }
public void AddError(FieldContext fieldContext, Exception ex, string errorType) { _failed = true; // fire event var eventArgs = new OperationErrorEventArgs(_requestContext, this._mappedOpField.Field, ex); _requestContext.Server.Events.OnOperationError(eventArgs); if (eventArgs.Exception == null) { return; // event handler cleared error } // add error fieldContext.AddError(ex, errorType); }
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
public async Task ExecuteOperationFieldAsync() { try { if (_mappedOpField.Field.OnExecuting(_requestContext, out var args) && args.Skip) { Result = DBNull.Value; // it's a signal to skip value in output return; } var opFieldContext = new FieldContext(_requestContext, this, _mappedOpField); opFieldContext.SetCurrentParentScope(_parentScope); // We do not save result in parent top-level context: we maybe executing in parallel with other top-level fields; // we need synchronization(lock), and also op fields might finish out of order. So we save result in a field, and // RequestHandler will save all results from executers in proper order. //_parentScope.SetValue(_mappedOpField.Field.Key, opOutValue); -- do not do this this.Result = await InvokeResolverAsync(opFieldContext); // for fields returning objects, save for further processing of results if (opFieldContext.MappedField.Field.SelectionSubset != null) { _executedObjectFieldContexts.Add(opFieldContext); } // process object field results until no more while (_executedObjectFieldContexts.Count > 0) { if (_requestContext.CancellationToken.IsCancellationRequested) { opFieldContext.ThrowRequestCancelled(); } // save current list, create new one in the field var oldFieldContexts = _executedObjectFieldContexts; _executedObjectFieldContexts = new List <FieldContext>(); foreach (var fldCtx in oldFieldContexts) { await ExecuteFieldSelectionSubsetAsync(fldCtx); } }//while } finally { // notify resolvers about end request if (_resolverInstances.Count > 0) { foreach (var resObj in _resolverInstances) { (resObj as IResolverClass)?.EndRequest(_requestContext); } } } }
private async Task <object> InvokeResolverAsync(FieldContext fieldContext) { try { if (fieldContext.ResolverClassInstance == null) { AssignResolverClassInstance(fieldContext); } if (fieldContext.ArgValues == null) { BuildResolverArguments(fieldContext); } // we might have encountered errors when evaluating args; if so, abort all this.AbortIfFailed(); // set current parentEntity arg if (fieldContext.Flags.IsSet(FieldFlags.HasParentArg)) { fieldContext.ArgValues[1] = fieldContext.CurrentScope.Entity; } var resolver = fieldContext.FieldDef.Resolver; var result = resolver.Method.Invoke(fieldContext.ResolverClassInstance, fieldContext.ArgValues); if (fieldContext.Flags.IsSet(FieldFlags.ReturnsTask)) { result = await UnwrapTaskResultAsync(fieldContext, (Task)result); } Interlocked.Increment(ref _requestContext.Metrics.ResolverCallCount); // Note: result might be null, but batched result might be set. return(result); } catch (TargetInvocationException tex) { // sync call goes here var origExc = tex.InnerException; if (origExc is AbortRequestException) { throw origExc; } fieldContext.AddError(origExc, ErrorCodes.ResolverError); Fail(); return(null); //never happens } catch (AbortRequestException) { throw; } catch (Exception ex) { fieldContext.AddError(ex, ErrorCodes.ResolverError); Fail(); return(null); //never happens } }
private object SafeEvaluateArg(FieldContext fieldContext, MappedArg arg) { try { var value = arg.Evaluator.GetValue(_requestContext); var convValue = _requestContext.ValidateConvert(value, arg.ArgDef.TypeRef, arg.Anchor); return(convValue); } catch (AbortRequestException) { return(null); } catch (InvalidInputException bvEx) { _requestContext.AddInputError(bvEx); _failed = true; return(null); //continue evaluating args; it will be aborted after all args are done } catch (Exception ex) { _requestContext.AddInputError($"Failed to evaluate argument {arg.ArgDef.Name}: {ex.Message}", arg.Anchor); _failed = true; return(null); // continue to next arg } }
private void BuildResolverArguments(FieldContext fieldContext) { var mappedArgs = fieldContext.MappedField.Field.MappedArgs; var argValues = new List <object>(); // special arguments: context, parent argValues.Add(fieldContext); if (!fieldContext.FieldDef.Flags.IsSet(FieldFlags.Static)) { argValues.Add(fieldContext.CurrentParentScope.Entity); } //regular arguments for (int i = 0; i < mappedArgs.Count; i++) { var arg = mappedArgs[i]; var argValue = SafeEvaluateArg(fieldContext, arg); argValues.Add(argValue); } fieldContext.ArgValues = argValues.ToArray(); }
public async Task ExecuteOperationFieldAsync() { try { var opFieldContext = new FieldContext(_requestContext, this, _operationField, _fieldIndex); opFieldContext.CurrentScope = _parentScope; var result = await InvokeResolverAsync(opFieldContext); var opOutValue = opFieldContext.ConvertToOuputValue(result); _parentScope.SetValue(_fieldIndex, opOutValue); // for fields returning objects, save for further processing of results if (opFieldContext.Flags.IsSet(FieldFlags.ReturnsComplexType)) { _executedObjectFieldContexts.Add(opFieldContext); } // process object field results until no more while (_executedObjectFieldContexts.Count > 0) { if (_requestContext.CancellationToken.IsCancellationRequested) { opFieldContext.ThrowRequestCancelled(); } // save current list, create new one in the field var oldFieldContexts = _executedObjectFieldContexts; _executedObjectFieldContexts = new List <FieldContext>(); foreach (var fldCtx in oldFieldContexts) { await ExecuteFieldSelectionSubsetAsync(fldCtx); } }//while } finally { // notify resolvers about end request if (_resolverInstances.Count > 0) { foreach (var resObj in _resolverInstances) { (resObj as IResolverClass)?.EndRequest(_requestContext); } } } }
// gets cached resolver class instance or creates new one private void AssignResolverClassInstance(FieldContext fieldCtx, FieldResolverInfo fieldResolver) { var resClassType = fieldResolver.ResolverMethod.ResolverClass.Type; object resInstance = null; if (_resolverInstances.Count == 1 && _resolverInstances[0].GetType() == resClassType) // fast track { resInstance = _resolverInstances[0]; } else { resInstance = _resolverInstances.FirstOrDefault(r => r.GetType() == resClassType); } if (resInstance == null) { resInstance = Activator.CreateInstance(resClassType); if (resInstance is IResolverClass iRes) { iRes.BeginRequest(_requestContext); } _resolverInstances.Add(resInstance); } fieldCtx.ResolverClassInstance = resInstance; }
internal static void ThrowFatal(this FieldContext fieldContext, string message) { throw new FatalServerException(message); }