/// <summary> /// Generates a result representing the list items of this instance. This result /// is built in a manner that can be easily serialized. /// </summary> /// <param name="result">The result that was generated.</param> /// <returns><c>true</c> if the result that was generated is valid and should /// be included in a final result, <c>false</c> otherwise.</returns> private bool GenerateListItemResult(out IResponseItem result) { result = null; var includeResult = false; // this instance represents an array of children // create an output array of the generated data for each child if (_childListItems.Count == 0) { result = new ResponseList(); includeResult = true; } else { var list = new ResponseList(); foreach (var child in _childListItems) { var includeChildResult = child.GenerateResult(out var childResult); includeResult = includeResult || includeChildResult; if (includeChildResult) { list.Add(childResult); } } if (includeResult) { result = list; } } return(includeResult); }
private void WriteResponseItem(Utf8JsonWriter writer, IResponseItem dataItem) { if (dataItem == null) { this.WriteLeafValue(writer, dataItem); return; } switch (dataItem) { case IResponseFieldSet fieldSet: this.WriteObjectCollection(writer, fieldSet); break; case IResponseList list: this.WriteList(writer, list); break; case IResponseSingleValue singleValue: this.WriteLeafValue(writer, singleValue.Value); break; default: throw new ArgumentOutOfRangeException( $"Unknown {nameof(IResponseItem)} type. " + $"Default writer is unable to write type '{dataItem.GetType().FriendlyName()}' to the output stream."); } }
/// <summary> /// Generates the result for this data item. Will correctly populate a list or key/value pait object /// according to the rules of the field this data item represents. /// </summary> /// <param name="result">The result object that was generated.</param> /// <returns><c>true</c> if the result was generated and should be included in any up stream responses. <c>false</c> if this result should be ignored /// and not included.</returns> public bool GenerateResult(out IResponseItem result) { result = null; // no data resolved? no children can exist // return indicating if the "null" is acceptable or not. if (this.ResultData == null || _resultsDiscarded) { return(this.Status.IncludeInOutput()); } // total fail, womp womp if (!this.Status.IncludeInOutput()) { return(false); } // leafs have nothing underneath them, the resolved data IS the item value. if (this.FieldContext.Field.IsLeaf) { // List<SomeScalar> and List<SomeEnum> are leafs since there is no further // resolution to the data but its still must be projected // as a list response item when rendered // ---- return(this.IsListField ? this.GenerateListItemResult(out result) : this.GenerateSingleValueResult(out result)); } bool includeResult; if (_childFields != null) { includeResult = this.GenerateFieldListResult(out result); } else if (_childListItems != null) { includeResult = this.GenerateListItemResult(out result); } else { // no fields were resolved and no children containers were assigned // drop the item from the response. This can occur if a resolver returns // an object that is not scoped to the current query such as returning // Droids and Humans but the query is just a fragment spread for Humans // the droids would be dropped as they were not requested includeResult = false; result = null; } return(includeResult); }
/// <summary> /// Generates the single value result. /// </summary> /// <param name="result">The result.</param> /// <returns><c>true</c> if the leaf value was successfully rendered, <c>false</c> otherwise.</returns> private bool GenerateSingleValueResult(out IResponseItem result) { result = null; var resultData = this.ResultData; if (resultData != null) { var type = resultData.GetType(); if (GraphQLProviders.ScalarProvider.IsScalar(type)) { var graphType = GraphQLProviders.ScalarProvider.RetrieveScalar(type); resultData = graphType.Serializer.Serialize(resultData); } } result = new ResponseSingleValue(resultData); return(true); }
/// <summary> /// Generates a result representing the individual child fields of this instance. This result /// is built in a manner that can be easily serialized. /// </summary> /// <param name="result">The result that was generated.</param> /// <returns><c>true</c> if the result that was generated is valid and should /// be included in a final result, <c>false</c> otherwise.</returns> private bool GenerateFieldListResult(out IResponseItem result) { result = null; var includeResult = false; // this instance represents a set of key/value pair fields // create a dictionary of those kvps as the result var fieldSet = new ResponseFieldSet(); foreach (var field in _childFields) { var includeChildResult = field.GenerateResult(out var childResult); includeResult = includeResult || includeChildResult; if (includeChildResult) { if (fieldSet.Fields.ContainsKey(field.FieldContext.Name)) { throw new GraphExecutionException( $"Duplicate field name. The field '{field.Name}' at '{this.Origin.Path.DotString()}' was resolved " + $"more than once for a source object, unable to generate a valid output. " + $"Field collections require unique names. An attempt was made to add the field '{field.Name}', " + $"for target type '{field.FieldContext.ExpectedSourceType?.FriendlyName() ?? "-all-"}' when the field " + "name was already present in the output dictionary.", this.Origin, new InvalidOperationException($"The source object '{this.SourceData}' successfully resolved a field name of '{field.Name}' more than once when it shouldn't. This may occur if a source " + "object type is referenced to to multiple target graph types in fragment references. Ensure that your source data uniquely maps to one fragment per field collection " + "or that the fragments do not share property names.")); } fieldSet.Add(field.FieldContext.Name, childResult); } } if (includeResult) { result = fieldSet; } return(includeResult); }
/// <summary> /// Adds a new response item to this field set with the specified name. /// </summary> /// <param name="name">The name of the item.</param> /// <param name="item">The item.</param> public void Add(string name, IResponseItem item) { _dictionary.Add(name, item); }
/// <summary> /// Adds the specified item to the growing list. /// </summary> /// <param name="item">The item.</param> public void Add(IResponseItem item) { _list.Add(item); }