/// <summary> /// Adds the specified context to the collection. /// </summary> /// <param name="context">The context.</param> public void Add(IGraphFieldInvocationContext context) { if (context == null) { return; } // as a collection is built its possible that // a field (with a given alias/name) is added more than once // for instance if field on an interface and as part of a fragment // has the same resultant name // these fields can be safely merged or in the case of this library // the secondary one just discarded // spec: https://graphql.github.io/graphql-spec/June2018/#sec-Field-Selection-Merging // // key (Alias Name, Field name, return graph type name, expected source type) var contextKey = Tuple.Create(context.Name, context.Field.Name, context.Field.TypeExpression.TypeName, context.ExpectedSourceType); if (_uniqueContexts.Contains(contextKey)) { return; } _uniqueContexts.Add(contextKey); _contexts.Add(context); if (context.ExpectedSourceType != null) { _acceptableTypes.Add(context.ExpectedSourceType); } if (context.Field.SecurityGroups.Any()) { _secureContexts.Add(context); } }
/// <summary> /// Initializes a new instance of the <see cref="GraphFieldAuthorizationRequest"/> class. /// </summary> /// <param name="invocationContext">The invocation context through which this authorization request /// is occuring.</param> public GraphFieldAuthorizationRequest(IGraphFieldInvocationContext invocationContext) { Validation.ThrowIfNull(invocationContext, nameof(invocationContext)); this.Id = Guid.NewGuid().ToString("N"); this.Field = invocationContext.Field; this.Origin = invocationContext.Origin; this.Items = new MetaDataCollection(); }
/// <summary> /// Initializes a new instance of the <see cref="GraphDataItem" /> class. /// </summary> /// <param name="context">The context.</param> /// <param name="sourceData">The source data.</param> /// <param name="path">The path.</param> public GraphDataItem(IGraphFieldInvocationContext context, object sourceData, SourcePath path) { Validation.ThrowIfNull(path, nameof(path)); this.SourceData = sourceData; this.FieldContext = Validation.ThrowIfNullOrReturn(context, nameof(context)); this.Status = FieldItemResolutionStatus.NotStarted; this.Origin = new SourceOrigin(context.Origin.Location, path); this.TypeExpression = context.Field.TypeExpression; }
private float CalculateFieldWeight(IGraphFieldInvocationContext field) { var fieldWeight = field.Field.Complexity ?? 1; // Weight of how the field will be processed switch (field.Field.Mode) { case FieldResolutionMode.PerSourceItem: fieldWeight *= EXECUTION_MODE_PER_SOURCE_WEIGHT; break; case FieldResolutionMode.Batch: fieldWeight *= EXECUTION_MODE_BATCH_WEIGHT; break; } // what of how the field data is sourced switch (field.Field.FieldSource) { case GraphFieldTemplateSource.Action: fieldWeight *= CONTROLLER_ACTION_WEIGHT; break; case GraphFieldTemplateSource.Method: fieldWeight *= OBJECT_METHOD_FIELD_WEIGHT; break; case GraphFieldTemplateSource.Property: fieldWeight *= OBJECT_PROPERTY_FIELD_WEIGHT; break; } // the total weight of the child fields (per source item) float childFieldsWeight = 1; foreach (var childField in field.ChildContexts) { childFieldsWeight += this.CalculateFieldWeight(childField); } // the combined weight of the child items against how those children // are executed (once for a single object orm perhaps many times for a list?) if (field.Field.TypeExpression.IsListOfItems) { childFieldsWeight *= RETURN_TYPE_LIST_WEIGHT; } else { childFieldsWeight *= RETURN_TYPE_SINGLE_VALUE_WEIGHT; } // the combined weight return(childFieldsWeight * fieldWeight); }
/// <summary> /// Initializes a new instance of the <see cref="GraphFieldRequest" /> class. /// </summary> /// <param name="invocationContext">The invocation context that defines how hte field /// should be processed according to the query plan.</param> /// <param name="dataSource">The data source containing the the source input data to the field as well as /// the graph items referenced by said input data.</param> /// <param name="origin">The location in the source query where this field request was generated.</param> /// <param name="items">A collection of meta data items to carry with this request.</param> public GraphFieldRequest( IGraphFieldInvocationContext invocationContext, GraphFieldDataSource dataSource, SourceOrigin origin, MetaDataCollection items = null) { this.Id = Guid.NewGuid().ToString("N"); this.InvocationContext = Validation.ThrowIfNullOrReturn(invocationContext, nameof(invocationContext)); this.Origin = Validation.ThrowIfNullOrReturn(origin, nameof(origin)); this.Items = items ?? new MetaDataCollection(); this.DataSource = dataSource; }
private IEnumerable <IGraphFieldInvocationContext> YieldSecureContexts(IGraphFieldInvocationContext context) { if (context.Field.SecurityGroups.Any()) { yield return(context); } foreach (var child in context.ChildContexts) { var found = this.YieldSecureContexts(child); foreach (var childContext in found) { yield return(childContext); } } }
/// <summary> /// Adds a new field context item as a child of this field. Throws an exception /// if this data item contains list items. /// </summary> /// <param name="childInvocationContext">The child invocation context.</param> /// <returns>GraphDataItem.</returns> public GraphDataItem AddChildField(IGraphFieldInvocationContext childInvocationContext) { if (this.IsListField) { throw new GraphExecutionException( $"The field {this.FieldContext.Field.Route.Path} represents " + "a list of items, a child field context cannot be directly added to it."); } _childFields = _childFields ?? new List <GraphDataItem>(); var path = this.Origin.Path.Clone(); path.AddFieldName(childInvocationContext.Field.Name); var childFieldItem = new GraphDataItem(childInvocationContext, this.ResultData, path); _childFields.Add(childFieldItem); return(childFieldItem); }
/// <summary> /// Using the child context being invoked, this method creates the execution contexts in a manner /// that is expected by the invocation context be that 1 per each item, or 1 for a collective set of items being batched. /// </summary> /// <param name="context">The context.</param> /// <param name="childInvocationContext">The child invocation context.</param> /// <param name="sourceItemsToInclude">The source items to include.</param> /// <returns>IEnumerable<GraphFieldExecutionContext>.</returns> private IEnumerable <GraphFieldExecutionContext> CreateChildExecutionContexts( GraphFieldExecutionContext context, IGraphFieldInvocationContext childInvocationContext, IEnumerable <GraphDataItem> sourceItemsToInclude) { if (childInvocationContext.Field.Mode == FieldResolutionMode.PerSourceItem) { foreach (var sourceItem in sourceItemsToInclude) { var child = sourceItem.AddChildField(childInvocationContext); var dataSource = new GraphFieldDataSource(sourceItem.ResultData, child.Origin.Path, child); var request = new GraphFieldRequest(childInvocationContext, dataSource, child.Origin, context.Request.Items); yield return(new GraphFieldExecutionContext( context, request, context.VariableData, context.DefaultFieldSources)); } } else if (childInvocationContext.Field.Mode == FieldResolutionMode.Batch) { // remove any potential indexers from the path to this batch operation // in general this will be acted on a collection of items, attempt to remove // the first found instance of an indexer in the chain to indicate the path to the batch // // items may be declared as: Top.Parent[0].BatchField, Top.Parent[1].BatchField // alter the canonical path to be: Top.Parent.BatchField var fieldPath = sourceItemsToInclude.First().Origin.Path.Clone(); while (fieldPath.IsIndexedItem) { fieldPath = fieldPath.MakeParent(); } fieldPath.AddFieldName(childInvocationContext.Field.Name); var batchOrigin = new SourceOrigin(context.Request.Origin.Location, fieldPath); // create a list to house the raw source data being passed for the batch // this is the IEnumerable<T> required as an input to any batch resolver var sourceArgumentType = childInvocationContext.Field.Arguments.SourceDataArgument?.ObjectType ?? typeof(object); var sourceListType = typeof(List <>).MakeGenericType(sourceArgumentType); var sourceDataList = InstanceFactory.CreateInstance(sourceListType) as IList; // create a list of all the GraphDataItems representing the field // being resolved per input item var sourceItemList = new List <GraphDataItem>(); foreach (var item in sourceItemsToInclude) { var childField = item.AddChildField(childInvocationContext); sourceDataList.Add(item.ResultData); sourceItemList.Add(childField); } var dataSource = new GraphFieldDataSource( sourceDataList, fieldPath, sourceItemList); var request = new GraphFieldRequest(childInvocationContext, dataSource, batchOrigin, context.Request.Items); yield return(new GraphFieldExecutionContext( context, request, context.VariableData, context.DefaultFieldSources)); } else { throw new ArgumentOutOfRangeException( nameof(childInvocationContext.Field.Mode), $"The execution mode for field '{childInvocationContext.Field.Route.Path}' cannot be processed " + $"by {nameof(ProcessChildFieldsMiddleware<TSchema>)}. (Mode: {childInvocationContext.Field.Mode.ToString()})"); } }