/// <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();
 }
Esempio n. 3
0
 /// <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;
 }
Esempio n. 4
0
        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);
        }
Esempio n. 5
0
 /// <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;
 }
Esempio n. 6
0
        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);
                }
            }
        }
Esempio n. 7
0
        /// <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);
        }
Esempio n. 8
0
        /// <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&lt;GraphFieldExecutionContext&gt;.</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()})");
            }
        }