Exemplo n.º 1
0
        private static IEnumerable <PropertyReference> CreatePropertyReferenceList(PropertyInfo destinationProperty, PropertyReference propertyReference)
        {
            // If the requestor didn't define any fields to include, check if the property has any default fields
            IEnumerable <PropertyReference> propertySubReferences = propertyReference.Children;

            if (!propertySubReferences.Any())
            {
                // check if there's a property attribute
                var includes = destinationProperty.GetCustomAttribute <SubPropertyIncludeByDefault>();
                if (includes != null)
                {
                    propertySubReferences = PropertyReference.Parse(includes.Includes);
                }
            }

            return(propertySubReferences);
        }
Exemplo n.º 2
0
        public void OnActionExecuted(ActionExecutedContext context)
        {
            Exception exceptionResult = null;
            object    resultObject    = null;
            Type      destinationType = null;


            var filterDescriptor = context
                                   .ActionDescriptor
                                   .FilterDescriptors
                                   .SingleOrDefault(d => d.Filter.GetType() == typeof(ExpandResultAttribute));

            if (!_expandAllEndpoints)
            {
                //Cast the filter property to ExpandResultAttribute
                var attributeInstance = filterDescriptor?.Filter as ExpandResultAttribute;

                //If the attribute is null, i.e. not present, or false, it shouldn't expand and we return here
                if (!(attributeInstance?.ShouldExpand ?? false))
                {
                    return;
                }
            }

            if (filterDescriptor != null)
            {
                destinationType = ((ExpandResultAttribute)filterDescriptor.Filter).DestinationType;
            }

            var doNotExpandAttribute = context
                                       .ActionDescriptor
                                       .FilterDescriptors
                                       .SingleOrDefault(d => d.Filter.GetType() == typeof(DoNotExpandResultAttribute));

            if (doNotExpandAttribute != null)
            {
                return;
            }

            // Set the error out of the gate should something have gone wrong coming into Popcorn
            if (context.Exception != null)
            {
                exceptionResult = context.Exception;
                if (context.HttpContext.Response.StatusCode == 200)
                {
                    context.HttpContext.Response.StatusCode = 500;
                }
                context.ExceptionHandled = true;     // Setting this so the inspector is still respected
            }
            else if (context.Result is ObjectResult) // Disect the response if there is something to unfold and no exception
            {
                resultObject = ((ObjectResult)context.Result).Value;

                // Wrap the main work here in a try/catch that we can then pass to our inspector
                try
                {
                    if (_expander.WillExpand(resultObject))
                    {
                        // see if we can find some include statements
                        string includes = "[]";
                        if (context.HttpContext.Request.Query.ContainsKey("include"))
                        {
                            includes = context.HttpContext.Request.Query["include"];
                        }
                        else if (context.HttpContext.Request.Headers?.ContainsKey("API-INCLUDE") ?? false)
                        {
                            includes = context.HttpContext.Request.Headers["API-INCLUDE"];
                        }

                        // Use our expander and expand the object
                        resultObject = _expander.Expand(resultObject, _context, PropertyReference.Parse(includes), destinationTypeHint: destinationType);
                    }

                    // Sort should there be anything to sort
                    if (resultObject != null)
                    {
                        // Assign sortDirection where necessary, but default to Ascending if nothing passed in
                        SortDirection sortDirection = SortDirection.Ascending;
                        if (context.HttpContext.Request.Query.ContainsKey("sortDirection"))
                        {
                            // Assign the proper sort direction, but invalidate an invalid value
                            try
                            {
                                sortDirection = (SortDirection)Enum.Parse(typeof(SortDirection), context.HttpContext.Request.Query["sortDirection"]);
                            }
                            catch (ArgumentException)
                            {
                                throw new ArgumentException(context.HttpContext.Request.Query["sortDirection"]);
                            }
                        }

                        // Do any sorting as specified
                        if (context.HttpContext.Request.Query.ContainsKey("sort"))
                        {
                            resultObject = _expander.Sort(resultObject, context.HttpContext.Request.Query["sort"], sortDirection);
                        }
                    }
                }
                catch (Exception e)
                {
                    exceptionResult = e;
                    // Set the response code as appropriate for a caught error
                    context.HttpContext.Response.StatusCode = 500;
                }
            }
            else
            {
                return;
            }

            // Apply our inspector to the expanded content
            if (_inspector != null)
            {
                resultObject = _inspector(resultObject, _context, exceptionResult);
            }
            else if (exceptionResult != null) // Have to rethrow the error if there is no inspector set so as to not return false positives
            {
                throw exceptionResult;
            }

            context.Result = new JsonResult(resultObject, _jsonOptions);
        }
Exemplo n.º 3
0
        /// <summary>
        /// Given a value, attempt to set it to a property on a destination object.  This may involve changing the object,
        /// such as converting an int to a double, or an int to an int?, or expanding an object into its projection
        /// </summary>
        /// <param name="originalValue"></param>
        /// <param name="destinationProperty"></param>
        /// <param name="destinationObject"></param>
        /// <param name="context"></param>
        /// <param name="propertyReference"></param>
        /// <param name="visited">todo: describe visited parameter on SetValueToProperty</param>
        /// <returns></returns>
        private bool SetValueToProperty(object originalValue, PropertyInfo destinationProperty, object destinationObject, ContextType context, PropertyReference propertyReference, HashSet <int> visited)
        {
            // If it is null then just do a direct assignment
            if (originalValue == null)
            {
                destinationProperty.SetValue(destinationObject, null);
                return(true);
            }

            // Try to do an assignment including any conversion needed
            if (destinationProperty.TrySetValueHandleConvert(destinationObject, originalValue))
            {
                return(true);
            }

            if (WillExpand(originalValue))
            {
                IEnumerable <PropertyReference> propertySubReferences = CreatePropertyReferenceList(destinationProperty, propertyReference);
                var expandedValue = Expand(originalValue, context, propertySubReferences, visited, destinationProperty.PropertyType);
                if (destinationProperty.TrySetValueHandleConvert(destinationObject, expandedValue))
                {
                    return(true);
                }
            }

            return(false);
        }
Exemplo n.º 4
0
        /// <summary>
        /// Verify that we have the appropriate include list for a type, taking into account any requested,
        /// or otherwise defaults supplied.
        /// </summary>
        /// <param name="includes"></param>
        /// <param name="sourceType"></param>
        /// <param name="destType"></param>
        /// <returns></returns>
        private IEnumerable <PropertyReference> ConstructIncludes(IEnumerable <PropertyReference> includes, Type sourceType, Type destType)
        {
            // Out of the gate we want to first see if the only property to be included is a wildcard
            if ((includes.Count() == 1) && (includes.Any(i => i.PropertyName == "*")))
            {
                var wildCardIncludes = new List <PropertyReference> {
                };
                var mapDef           = new MappingDefinition();

                // Check to see if the object is to be blind expanded and make the destination the same as the source if it is
                if (destType == null)
                {
                    destType = sourceType;
                }
                else // in the case that the object isn't to be blind expanded get the proper mapping
                {
                    Mappings.TryGetValue(sourceType, out mapDef);
                }

                // Have all of the destination type properties set to be included
                foreach (PropertyInfo info in destType.GetTypeInfo().GetProperties())
                {
                    var matchingSourceProp = sourceType.GetTypeInfo().GetProperty(info.Name);

                    // Make sure that the property isn't marked as InternalOnly on the sourceType
                    // Which is only an issue if they marked the type to throw an error if it's requested
                    if (matchingSourceProp != null)
                    {
                        if (matchingSourceProp.GetCustomAttributes().Any(att => att.GetType() == typeof(InternalOnlyAttribute)))
                        {
                            // Only add the property if it isn't marked InternalOnly
                            continue;
                        }
                    }

                    // Also make sure that the property exists on the destination type in some capacity
                    // This will never be hit by a blindly expanded object as the source and destination type are identical
                    if (matchingSourceProp == null)
                    {
                        try
                        {
                            // Check to see if there are any translators that would apply the object to the projection ultimately
                            var transTest = mapDef.DefaultDestination().Translators[info.Name];
                        }
                        catch (Exception)
                        {
                            // This property isn't known to the projection at all and thus should not be included
                            continue;
                        }
                    }

                    wildCardIncludes.Add(new PropertyReference {
                        PropertyName = info.Name
                    });
                }

                return(wildCardIncludes);
            }

            if (includes.Any())
            {
                return(includes);
            }

            if (destType == null)
            {
                // in the case of a blind object, default to source properties.  This is a bit dangerous!
                includes = sourceType.GetTypeInfo().GetProperties(BindingFlags.Public | BindingFlags.Instance)
                           .Select(p => new PropertyReference()
                {
                    PropertyName = p.Name
                });
            }

            // if this doesn't have any includes specified, use the default
            if (!includes.Any())
            {
                includes = PropertyReference.Parse(Mappings[sourceType].DestinationForType(destType).DefaultIncludes);
            }

            // if this STILL doesn't have any includes, that means include everything
            if (!includes.Any())
            {
                includes = destType.GetTypeInfo().GetProperties(BindingFlags.Public | BindingFlags.Instance)
                           .Select(p => new PropertyReference()
                {
                    PropertyName = p.Name
                });
            }

            return(includes);
        }
        public override void OnActionExecuted(ActionExecutedContext context)
        {
            Exception exceptionResult = null;
            object    resultObject    = null;

            // Set the error out of the gate should something have gone wrong coming into Popcorn
            if (context.Exception != null)
            {
                exceptionResult = context.Exception;
                if (context.HttpContext.Response.StatusCode == 200)
                {
                    context.HttpContext.Response.StatusCode = 500;
                }
                context.ExceptionHandled = true;     // Setting this so the inspector is still respected
            }
            else if (context.Result is ObjectResult) // Disect the response if there is something to unfold and no exception
            {
                resultObject = ((ObjectResult)context.Result).Value;

                // Wrap the main work here in a try/catch that we can then pass to our inspector
                try
                {
                    if (_expander.WillExpand(resultObject))
                    {
                        // see if we can find some include statements
                        string includes = "[]";
                        if (context.HttpContext.Request.Query.ContainsKey("include"))
                        {
                            includes = context.HttpContext.Request.Query["include"];
                        }
                        else if (context.HttpContext.Request.Headers?.ContainsKey("API-INCLUDE") ?? false)
                        {
                            includes = context.HttpContext.Request.Headers["API-INCLUDE"];
                        }

                        // Use our expander and expand the object
                        resultObject = _expander.Expand(resultObject, _context, PropertyReference.Parse(includes));
                    }

                    // Sort should there be anything to sort
                    if (resultObject != null)
                    {
                        // Assign sortDirection where necessary, but default to Ascending if nothing passed in
                        SortDirection sortDirection = SortDirection.Ascending;
                        if (context.HttpContext.Request.Query.ContainsKey("sortDirection"))
                        {
                            // Assign the proper sort direction, but invalidate an invalid value
                            try
                            {
                                sortDirection = (SortDirection)Enum.Parse(typeof(SortDirection), context.HttpContext.Request.Query["sortDirection"]);
                            }
                            catch (ArgumentException)
                            {
                                throw new ArgumentException(context.HttpContext.Request.Query["sortDirection"]);
                            }
                        }

                        // Do any sorting as specified
                        if (context.HttpContext.Request.Query.ContainsKey("sort"))
                        {
                            resultObject = _expander.Sort(resultObject, context.HttpContext.Request.Query["sort"], sortDirection);
                        }
                    }
                } catch (Exception e)
                {
                    exceptionResult = e;
                    // Set the response code as appropriate for a caught error
                    context.HttpContext.Response.StatusCode = 500;
                }
            }

            // Apply our inspector to the expanded content
            if (_inspector != null)
            {
                resultObject = _inspector(resultObject, _context, exceptionResult);
            }
            else if (exceptionResult != null)   // Have to rethrow the error if there is no inspector set so as to not return false positives
            {
                throw exceptionResult;
            }

            context.Result = new JsonResult(resultObject,
                                            new JsonSerializerSettings
            {
                NullValueHandling = NullValueHandling.Ignore
            });
            base.OnActionExecuted(context);
        }
Exemplo n.º 6
0
 /// <summary>
 /// The entry point method for converting a type into its projection and selectively including data.
 /// This will work on either a Mapped Type or a collection of a Mapped Type.
 /// This version allows specification of the includes in string format
 /// </summary>
 public object Expand(object source, ContextType context, string includes, HashSet <int> visited = null, Type destinationTypeHint = null)
 {
     return(Expand(source, context, PropertyReference.Parse(includes), visited, destinationTypeHint));
 }
Exemplo n.º 7
0
 /// <summary>
 /// A generic overload that automatically provides the type hint.
 /// This accepts a string include list of the form "[Prop1,Prop2[SubProp1]]"
 /// </summary>
 /// <typeparam name="TDestType"></typeparam>
 /// <param name="source"></param>
 /// <param name="includes"></param>
 /// <param name="context"></param>
 /// <param name="visited"></param>
 /// <returns></returns>
 public TDestType Expand <TDestType>(object source, string includes, ContextType context = null, HashSet <int> visited = null)
 {
     return((TDestType)Expand(source, context, PropertyReference.Parse(includes), visited, typeof(TDestType)));
 }