Exemplo n.º 1
0
        /// <summary>
        /// Returns a specialized instance of the <see cref="T:System.Net.Http.Formatting.MediaTypeFormatter" /> that can format a response for the given parameters.
        /// </summary>
        /// <param name="type">The type to format.</param>
        /// <param name="request">The request.</param>
        /// <param name="mediaType">The media type.</param>
        /// <returns>
        /// Returns <see cref="T:System.Net.Http.Formatting.MediaTypeFormatter" />.
        /// </returns>
        public override MediaTypeFormatter GetPerRequestFormatterInstance(Type type, System.Net.Http.HttpRequestMessage request, System.Net.Http.Headers.MediaTypeHeaderValue mediaType)
        {
            var    qryParams      = System.Web.HttpUtility.ParseQueryString(request.RequestUri.Query);
            string loadAttributes = qryParams["LoadAttributes"] ?? string.Empty;

            string[] limitToAttributeKeyList = qryParams["AttributeKeys"]?.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(a => a.Trim()).ToArray();

            // if "simple" or True is specified in the LoadAttributes param, tell the formatter to serialize in Simple mode
            bool serializeInSimpleMode = loadAttributes.Equals("simple", StringComparison.OrdinalIgnoreCase) || (loadAttributes.AsBooleanOrNull() ?? false);

            // if either "simple", "expanded", or True is specified in the LoadAttributes param, tell the formatter to load the attributes on the way out
            bool loadAttributesEnabled = serializeInSimpleMode || loadAttributes.Equals("expanded", StringComparison.OrdinalIgnoreCase);

            Rock.Model.Person person = null;

            // NOTE: request.Properties["Person"] gets set in Rock.Rest.Filters.SecurityAttribute.OnActionExecuting
            if (loadAttributesEnabled && request.Properties.ContainsKey("Person"))
            {
                person = request.Properties["Person"] as Rock.Model.Person;
            }

            // store the request options in HttpContext.Current.Items so they are thread safe, and only for this request
            var loadAttributesOptions = new LoadAttributesOptions(loadAttributesEnabled, serializeInSimpleMode, limitToAttributeKeyList, person);

            HttpContext.Current.Items[LoadAttributesOptions.ContextItemsKey] = loadAttributesOptions;

            return(base.GetPerRequestFormatterInstance(type, request, mediaType));
        }
Exemplo n.º 2
0
        /// <summary>
        /// Called during serialization to get the <see cref="T:Newtonsoft.Json.JsonWriter" />.
        /// </summary>
        /// <param name="type">The type of the object to write.</param>
        /// <param name="writeStream">The stream to write to.</param>
        /// <param name="effectiveEncoding">The encoding to use when writing.</param>
        /// <returns>
        /// The writer to use during serialization.
        /// </returns>
        public override Newtonsoft.Json.JsonWriter CreateJsonWriter(Type type, System.IO.Stream writeStream, Encoding effectiveEncoding)
        {
            var loadAttributesOptions = HttpContext.Current.Items[LoadAttributesOptions.ContextItemsKey] as LoadAttributesOptions;

            if (loadAttributesOptions == null)
            {
                // shouldn't happen, but just in case
                loadAttributesOptions = new LoadAttributesOptions(false, false, null, null);
            }

            if (loadAttributesOptions.LoadAttributesEnabled && loadAttributesOptions.SerializeInSimpleMode)
            {
                // Use the RockJsonTextWriter when we need to Serialize Model.AttributeValues and Model.Attributes in simple mode
                return(new RockJsonTextWriter(new System.IO.StreamWriter(writeStream), loadAttributesOptions.SerializeInSimpleMode));
            }
            else
            {
                return(base.CreateJsonWriter(type, writeStream, effectiveEncoding));
            }
        }
Exemplo n.º 3
0
        /// <summary>
        /// Gets the select and expand dictionary object.
        /// </summary>
        /// <param name="type">The type.</param>
        /// <param name="selectAndExpandList">The select and expand list.</param>
        /// <param name="rockContext">The rock context.</param>
        /// <param name="loadAttributesOptions">The load attributes options.</param>
        /// <returns></returns>
        private static List <Dictionary <string, object> > GetSelectAndExpandDictionaryObject(Type type, List <object> selectAndExpandList, Rock.Data.RockContext rockContext, LoadAttributesOptions loadAttributesOptions)
        {
            // The normal SelectAllAndExpand converts stuff into dictionaries, but some stuff ends up missing
            // So, this will do all that manually using reflection and our own dictionary of each Entity
            var valueAsDictionary = new List <Dictionary <string, object> >();
            var isPersonModel     = type.IsGenericType && type.GenericTypeArguments[0] == typeof(Rock.Model.Person);
            Dictionary <int, Rock.Model.PersonAlias> personAliasLookup = null;

            if (isPersonModel)
            {
                var personIds = selectAndExpandList.Select(a => (a.GetPropertyValue("Instance") as Rock.Model.Person).Id).ToList();

                // NOTE: If this is a really long list of PersonIds (20000+ or so), this might time out or get an error,
                // so if it is more than 20000 just get *all* the PersonAlias records which would probably be much faster than a giant where clause
                var personAliasQry = new Rock.Model.PersonAliasService(rockContext).Queryable().AsNoTracking();
                if (personIds.Count < 20000)
                {
                    personAliasLookup = personAliasQry.Where(a => personIds.Contains(a.PersonId) && a.AliasPersonId == a.PersonId).ToDictionary(k => k.PersonId, v => v);
                }
                else
                {
                    personAliasLookup = personAliasQry.Where(a => a.AliasPersonId == a.PersonId).ToDictionary(k => k.PersonId, v => v);
                }
            }

            foreach (var selectExpandItem in selectAndExpandList)
            {
                var entityProperty = selectExpandItem.GetType().GetProperty("Instance");
                var entity         = entityProperty.GetValue(selectExpandItem) as Rock.Data.IEntity;
                if (entity is Rock.Model.Person)
                {
                    // if this is a SelectAndExpand of Person, we manually need to load Aliases so that the PrimaryAliasId is populated
                    var person = entity as Rock.Model.Person;
                    if (!person.Aliases.Any())
                    {
                        var primaryAlias = personAliasLookup.GetValueOrNull(person.Id);
                        if (primaryAlias != null)
                        {
                            person.Aliases.Add(personAliasLookup[person.Id]);
                        }
                    }
                }

                Dictionary <string, object> valueDictionary = new Dictionary <string, object>();

                // add the "Expanded" stuff first to emulate the default behavior
                var expandedStuff = selectExpandItem.GetPropertyValue("Container");
                while (expandedStuff != null)
                {
                    var expandedName  = expandedStuff.GetPropertyValue("Name") as string;
                    var expandedValue = expandedStuff.GetPropertyValue("Value");
                    valueDictionary.Add(expandedName, expandedValue);
                    expandedStuff = expandedStuff.GetPropertyValue("Next");
                }

                // add each of the Entity's properties
                foreach (var entityKeyValue in entity.ToDictionary())
                {
                    valueDictionary.Add(entityKeyValue.Key, entityKeyValue.Value);
                }

                // if LoadAttributes was specified, add those last
                if (loadAttributesOptions.LoadAttributesEnabled && (entity is Attribute.IHasAttributes))
                {
                    // Add Attributes and AttributeValues
                    valueDictionary.Add("Attributes", (entity as Attribute.IHasAttributes).Attributes);
                    valueDictionary.Add("AttributeValues", (entity as Attribute.IHasAttributes).AttributeValues);
                }

                valueAsDictionary.Add(valueDictionary);
            }

            return(valueAsDictionary);
        }
Exemplo n.º 4
0
        /// <summary>
        /// Called during serialization to write an object of the specified type to the specified stream.
        /// </summary>
        /// <param name="type">The type of the object to write.</param>
        /// <param name="value">The object to write.</param>
        /// <param name="writeStream">The stream to write to.</param>
        /// <param name="effectiveEncoding">The encoding to use when writing.</param>
        public override void WriteToStream(Type type, object value, System.IO.Stream writeStream, Encoding effectiveEncoding)
        {
            bool          isSelectAndExpand   = false;
            List <object> selectAndExpandList = null;

            if (value is IQueryable <object> && typeof(IQueryable <Rock.Data.IEntity>).IsAssignableFrom(type))
            {
                Type valueType = value.GetType();

                // if this is an OData 'SelectAndExpand', we have convert stuff to Dictionary manually to get the stuff to serialize the way we want
                if (valueType.IsGenericType && valueType.GenericTypeArguments[0].Name == "SelectAllAndExpand`1")
                {
                    isSelectAndExpand = true;

                    var selectExpandQry = value as IQueryable <object>;

                    selectAndExpandList = selectExpandQry.ToList();
                }
            }

            var loadAttributesOptions = HttpContext.Current.Items[LoadAttributesOptions.ContextItemsKey] as LoadAttributesOptions;

            if (loadAttributesOptions == null)
            {
                // shouldn't happen, but just in case
                loadAttributesOptions = new LoadAttributesOptions(false, false, null, null);
            }

            // query should be filtered by now, so iterate thru items and load attributes before the response is serialized
            if (loadAttributesOptions.LoadAttributesEnabled)
            {
                IEnumerable <Attribute.IHasAttributes> items = null;

                if (value is IEnumerable <Attribute.IHasAttributes> )
                {
                    // if the REST call specified that Attributes should be loaded and we are returning a list of IHasAttributes..
                    // Also, do a ToList() to fetch the query into a list (instead re-querying multiple times)
                    items = (value as IEnumerable <Attribute.IHasAttributes>).ToList();

                    // Assign the items list back to value
                    value = items;
                }
                else if (value is Attribute.IHasAttributes)
                {
                    // if the REST call specified that Attributes should be loaded and we are returning a single IHasAttributes..
                    items = new List <Attribute.IHasAttributes>(new Attribute.IHasAttributes[] { value as Attribute.IHasAttributes });
                }
                else if (isSelectAndExpand && selectAndExpandList != null)
                {
                    //// 'SelectAndExpand' buries the Entity in a private field called 'Instance',
                    //// so use reflection to get that and load the attributes for each

                    var itemsList = new List <Attribute.IHasAttributes>();
                    foreach (var selectExpandItem in selectAndExpandList)
                    {
                        var entityProperty = selectExpandItem.GetType().GetProperty("Instance");
                        var entity         = entityProperty.GetValue(selectExpandItem) as Attribute.IHasAttributes;
                        if (entity != null)
                        {
                            itemsList.Add(entity);
                        }

                        entityProperty.SetValue(selectExpandItem, entity);
                    }

                    items = itemsList;
                }

                List <AttributeCache> limitToAttributes = null;
                if (loadAttributesOptions.LimitToAttributeKeyList?.Any() == true && type.IsGenericType)
                {
                    var entityTypeType = type.GenericTypeArguments[0];
                    if (entityTypeType != null)
                    {
                        var entityType           = EntityTypeCache.Get(entityTypeType);
                        var entityAttributesList = AttributeCache.GetByEntity(entityType.Id)?.SelectMany(a => a.AttributeIds).ToList().Select(a => AttributeCache.Get(a)).Where(a => a != null).ToList();
                        limitToAttributes = entityAttributesList?.Where(a => loadAttributesOptions.LimitToAttributeKeyList.Contains(a.Key, StringComparer.OrdinalIgnoreCase)).ToList();
                    }
                }

                if (items != null)
                {
                    using (var rockContext = new Rock.Data.RockContext())
                    {
                        foreach (var item in items)
                        {
                            Rock.Attribute.Helper.LoadAttributes(item, rockContext, limitToAttributes);
                        }

                        FilterAttributes(rockContext, items, loadAttributesOptions.Person);
                    }
                }
            }

            // Special Code if an $expand clause is specified and a $select clause is NOT specified
            // This fixes a couple of issues:
            //  1) our special loadAttributes stuff didn't work if $expand is specified
            //  2) Only non-virtual,non-inherited fields were included (for example: Person.PrimaryAliasId, etc, wasn't getting included) if $expand was specified
            if (isSelectAndExpand)
            {
                using (var rockContext = new Rock.Data.RockContext())
                {
                    List <Dictionary <string, object> > valueAsDictionary = GetSelectAndExpandDictionaryObject(type, selectAndExpandList, rockContext, loadAttributesOptions);
                    base.WriteToStream(type, valueAsDictionary, writeStream, effectiveEncoding);
                }

                return;
            }

            base.WriteToStream(type, value, writeStream, effectiveEncoding);
        }