private void CalcVariableDefaultValues(GraphQLOperation op) { foreach (var varDef in op.Variables) { if (varDef.ParsedDefaultValue == null) { continue; } var inpDef = varDef.InputDef; var typeRef = inpDef.TypeRef; var eval = GetInputValueEvaluator(inpDef, varDef.ParsedDefaultValue, typeRef); if (!eval.IsConst()) { // somewhere inside there's reference to variable, this is not allowed AddError($"Default value cannot reference variables.", varDef); continue; } var value = eval.GetValue(_requestContext); if (value != null && value.GetType() != typeRef.TypeDef.ClrType) { // TODO: add valid coersion rules; for ex, spec allows auto convert like int => int[] AddError($"Detected type mismatch for default value '{value}' of variable {varDef.Name} of type {typeRef.Name}", varDef); } inpDef.DefaultValue = value; inpDef.HasDefaultValue = true; } // foreach varDef }
private async Task ExecuteOperationAsync(GraphQLOperation op, OutputObjectScope topScope) { var opOutItemSet = op.SelectionSubset.MappedItemSets.FirstOrDefault(fi => fi.ObjectTypeDef == op.OperationTypeDef); var topFields = _requestContext.GetIncludedMappedFields(opOutItemSet); topScope.Init(op.OperationTypeDef, topFields); var parallel = _parallelQuery && op.OperationType == OperationType.Query && topFields.Count > 1; // Note: if we go parallel here, note that the topScope is safe for concurrent thread access; // it is only used to save op result value (SetValue method) var executers = new List <OperationFieldExecuter>(); for (int fieldIndex = 0; fieldIndex < topFields.Count; fieldIndex++) { var opExecuter = new OperationFieldExecuter(_requestContext, topScope, fieldIndex); executers.Add(opExecuter); } _requestContext.Metrics.ExecutionThreadCount = executers.Count; if (parallel) { await ExecuteAllParallel(executers); } else { await ExecuteAllNonParallel(executers); } }
private void CalcVariableDefaultValues(GraphQLOperation op) { foreach (var varDef in op.Variables) { if (varDef.ParsedDefaultValue == null) { continue; } var inpDef = varDef.InputDef; var typeRef = inpDef.TypeRef; var eval = GetInputValueEvaluator(inpDef, varDef.ParsedDefaultValue, typeRef); if (!eval.IsConst()) { // somewhere inside there's reference to variable, this is not allowed AddError($"Default value cannot reference variables.", varDef); continue; } var value = eval.GetValue(_requestContext); if (value != null && value.GetType() != typeRef.TypeDef.ClrType) { // TODO: fix that, add type conversion, for now throwing exception; or maybe it's not needed, value will be converted at time of use // but spec also allows auto casting like int => int[] AddError($"Detected type mismatch for default value '{value}' of variable {varDef.Name} of type {typeRef.Name}", varDef); } inpDef.DefaultValue = value; inpDef.HasDefaultValue = true; } // foreach varDef }
public void MapAndValidateRequest() { var fragmAnalyzer = new FragmentAnalyzer(_requestContext); fragmAnalyzer.Analyze(); if (_requestContext.Failed) { return; } foreach (var fragm in _requestContext.ParsedRequest.Fragments) { if (!fragm.IsInline) { MapFragment(fragm); } } foreach (var op in _requestContext.ParsedRequest.Operations) { if (!AssignOperationDef(op)) { continue; } MapOperation(op); CalcVariableDefaultValues(op); } _currentOp = null; }
private void MapOperation(GraphQLOperation op) { MapSelectionSubSet(op.SelectionSubset, op.OperationTypeDef, op.Directives); if (_pendingSelectionSets.Count > 0) { MapPendingSelectionSubsets(); } }
private void BuildDefaultQuery(Node qNode) { var query = new GraphQLOperation() { Name = null, OperationType = OperationType.Query }; CompleteBuildOperation(query, qNode); }
public void MapAndValidateRequest() { Fragments_MapValidate(); if (_requestContext.Failed) { return; } foreach (var op in _requestContext.ParsedRequest.Operations) { op.OperationTypeDef = _model.GetOperationDef(op.OperationType); _currentOp = op; CalcVariableDefaultValues(op); MapOperation(op); } _currentOp = null; }
private void BuildOperations(IList <Node> opNodes) { foreach (var opNode in opNodes) { var nameNode = opNode.FindChild(TermNames.Name); string name = nameNode?.GetText() ?? null; var opTypeNode = opNode.FindChild(TermNames.OpType); var opTypeStr = opTypeNode.GetText(); if (!Enum.TryParse <OperationType>(opTypeStr, true, out var opType)) { AddError($"Invalid operation type '{opTypeStr}'.", opTypeNode); continue; } var op = new GraphQLOperation() { Name = name, OperationType = opType }; CompleteBuildOperation(op, opNode); } }
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 void CompleteBuildOperation(GraphQLOperation op, Node opNode) { try { if (op.Name != null) { _path.Push(op.Name); } var varDefsNode = opNode.FindChild(TermNames.VarDefList); op.Variables = BuildOperationVariables(varDefsNode); var selSetNode = opNode.FindChild(TermNames.SelSet); if (selSetNode != null) { var selItems = BuildSelectionItemsList(selSetNode, op); op.SelectionSubset = new SelectionSubset(op, selItems, selSetNode.GetLocation()); } _requestContext.ParsedRequest.Operations.Add(op); } finally { // op.Name might have been assigned, so we don't check name, but just pop path _path.Clear(); } }
private async Task ExecuteOperationAsync(GraphQLOperation op, OutputObjectScope topScope) { var mappedTopSubset = op.SelectionSubset.MappedSubSets[0]; var topMappedItems = mappedTopSubset.MappedItems; var parallel = _parallelQueryEnabled && op.OperationType == OperationType.Query && op.SelectionSubset.Items.Count > 1; var executers = new List <OperationFieldExecuter>(); foreach (var mappedItem in topMappedItems) { switch (mappedItem) { case MappedFragmentSpread mfs: _requestContext.AddInputError($"Top selection items may not be fragments", mfs.Spread); return; case MappedSelectionField mappedField: var opExecuter = new OperationFieldExecuter(_requestContext, mappedField, topScope); executers.Add(opExecuter); break; } } if (parallel) { await ExecuteAllParallel(executers); } else { await ExecuteAllNonParallel(executers); } // Save results from op fields into top scope; we do it here, after all threads finished, to avoid concurrency issues // and preserve output field order foreach (var ex in executers) { topScope.SetValue(ex.ResultKey, ex.Result); } }
private void MapOperation(GraphQLOperation op) { _currentOp = op; MapSelectionSubSet(op.SelectionSubset, op.OperationTypeDef); }