Example #1
0
        /// <summary>
        /// Returns an <see cref="List{TEntity}"/> based on a custom filtering function applied to the query, as well as
        /// optional select and expand arguments, checking the user READ permissions along the way
        /// </summary>
        /// <param name="filterFunc">Allows any kind of filtering on the query</param>
        /// <param name="expand">Optional expand argument</param>
        /// <param name="select">Optional select argument</param>
        /// <param name="orderby">Optional select argument</param>
        /// <param name="permissionsFilter">Optional filter argument, if null is passed the q</param>
        /// <param name="cancellation">Optional select argument</param>
        protected async Task <List <TEntity> > GetEntitiesByCustomQuery(
            Func <Query <TEntity>, Query <TEntity> > filterFunc,
            ExpressionExpand expand,
            ExpressionSelect select,
            ExpressionOrderBy orderby,
            ExpressionFilter permissionsFilter,
            CancellationToken cancellation)
        {
            // Prepare a query of the result, and clone it
            var repo  = GetRepository();
            var query = repo.Query <TEntity>();

            // Apply custom filter function
            query = filterFunc(query);

            // Apply read permissions
            permissionsFilter ??= await UserPermissionsFilter(Constants.Read, cancellation);

            query = query.Filter(permissionsFilter);

            // Expand, Select and Order the result as specified in the OData agruments
            var expandedQuery = query.Expand(expand).Select(select).OrderBy(orderby ?? ExpressionOrderBy.Parse("Id")); // Required

            // Load the result into memory
            var data = await expandedQuery.ToListAsync(cancellation); // this is potentially unordered, should that be a concern?

            // TODO: This is slow and unused
            // Apply the permission masks (setting restricted fields to null) and adjust the metadata accordingly
            // await ApplyReadPermissionsMask(data, query, permissions, GetDefaultMask(), cancellation);

            // Return
            return(data);
        }
        /// <summary>
        /// Returns a <see cref="TEntity"/> as per the Id and the specifications in the <see cref="GetByIdArguments"/>, after verifying the user's permissions
        /// </summary>
        public virtual async Task <(TEntity, Extras)> GetById(TKey id, GetByIdArguments args, CancellationToken cancellation)
        {
            // Parse the parameters
            var expand = ExpressionExpand.Parse(args?.Expand);
            var select = ParseSelect(args?.Select);

            // Load the data
            var data = await GetEntitiesByIds(new List <TKey> {
                id
            }, expand, select, null, cancellation);

            // Check that the entity exists, else return NotFound
            var entity = data.SingleOrDefault();

            if (entity == null)
            {
                throw new NotFoundException <TKey>(id);
            }

            // Load the extras
            var extras = await GetExtras(data, cancellation);

            // Return
            return(entity, extras);
        }
Example #3
0
        /// <summary>
        /// Returns a <see cref="List{TEntity}"/> as per the propName, values and the specifications in the <see cref="ExpressionExpand"/> and <see cref="ExpressionSelect"/>,
        /// after verifying the user's READ permissions, returns the entities in the same order as the supplied Ids
        /// </summary>
        public virtual async Task <(List <TEntity>, Extras)> GetByPropertyValues(string propName, IEnumerable <object> values, SelectExpandArguments args, CancellationToken cancellation)
        {
            if (propName is null)
            {
                throw new ArgumentNullException(nameof(propName));
            }

            // Load the data
            List <TEntity> data;

            if (values == null || !values.Any())
            {
                data = new List <TEntity>();
            }
            else
            {
                // Parse the parameters
                var expand = ExpressionExpand.Parse(args?.Expand);
                var select = ParseSelect(args?.Select);

                data = await GetEntitiesByCustomQuery(q => q.FilterByPropertyValues(propName, values), expand, select, null, null, cancellation);
            }

            // Load the extras
            var extras = await GetExtras(data, cancellation);

            // Return
            return(data, extras);
        }
        /// <summary>
        /// Returns an <see cref="List{TEntity}"/> based on a custom <paramref name="filterFunc"/> applied to the query, as well as
        /// optional <paramref name="expand"/> and <paramref name="select"/> arguments, checking the user's READ permissions along the way.
        /// </summary>
        /// <param name="filterFunc">Allows any kind of filtering on the query</param>
        /// <param name="expand">Optional expand argument.</param>
        /// <param name="select">Optional select argument.</param>
        /// <param name="orderby">Optional orderby argument.</param>
        /// <param name="permissionsFilter">Optional filter argument, if null is passed the query uses the read permissions filter of the current user.</param>
        /// <param name="cancellation">The cancellation instruction.</param>
        protected async Task <List <TEntity> > GetEntitiesByCustomQuery(
            Func <EntityQuery <TEntity>, EntityQuery <TEntity> > filterFunc,
            ExpressionExpand expand,
            ExpressionSelect select,
            ExpressionOrderBy orderby,
            ExpressionFilter permissionsFilter,
            CancellationToken cancellation)
        {
            // Prepare a query of the result, and clone it
            var factory = QueryFactory();
            var query   = factory.EntityQuery <TEntity>();

            // Apply custom filter function
            query = filterFunc(query);

            // Apply read permissions
            permissionsFilter ??= await UserPermissionsFilter(PermissionActions.Read, cancellation);

            query = query.Filter(permissionsFilter);

            // Expand, Select and Order the result as specified in the Queryex agruments
            var expandedQuery = query.Expand(expand).Select(select).OrderBy(orderby ?? ExpressionOrderBy.Parse("Id")); // Required

            // Load the result into memory
            var data = await expandedQuery.ToListAsync(QueryContext(), cancellation); // this is potentially unordered, should that be a concern?

            // Return
            return(data);
        }
Example #5
0
        /// <summary>
        /// Returns a <see cref="List{TEntity}"/> as per the Ids and the specifications in the <see cref="SelectExpandArguments"/>, after verifying the user's permissions
        /// </summary>
        public virtual async Task <(List <TEntity>, Extras)> GetByIds(List <TKey> ids, SelectExpandArguments args, string action, CancellationToken cancellation)
        {
            // Parse the parameters
            var expand = ExpressionExpand.Parse(args?.Expand);
            var select = ParseSelect(args?.Select);

            // Prepare the permissions filter
            var permissionsFilter = await UserPermissionsFilter(action, cancellation);

            // Load the data
            var data = await GetEntitiesByIds(ids, expand, select, permissionsFilter, cancellation);

            var extras = await GetExtras(data, cancellation);

            // Return result
            return(data, extras);
        }
Example #6
0
        /// <summary>
        /// Returns a list of entities as per the specifications in the <see cref="GetChildrenArguments{TKey}"/>
        /// </summary>
        public virtual async Task <(List <TEntity>, Extras)> GetChildrenOf(GetChildrenArguments <TKey> args, CancellationToken cancellation)
        {
            // Parse the parameters
            var expand  = ExpressionExpand.Parse(args.Expand);
            var select  = ParseSelect(args.Select);
            var filter  = ExpressionFilter.Parse(args.Filter);
            var orderby = ExpressionOrderBy.Parse("Node");
            var ids     = args.I ?? new List <TKey>();

            // Load the data
            var data = await GetEntitiesByCustomQuery(q => q.FilterByParentIds(ids, args.Roots).Filter(filter), expand, select, orderby, null, cancellation);

            var extras = await GetExtras(data, cancellation);

            // Transform and Return
            return(data, extras);
        }
        /// <summary>
        /// Returns a <see cref="List{TEntity}"/> as per the Ids and the specifications
        /// in <paramref name="args"/>, after verifying the user's permissions.
        /// </summary>
        /// <remarks>
        /// This function does not call <see cref="ServiceBase.Initialize"/>. That is the responsibility of the caller.
        /// </remarks>
        protected virtual async Task <TEntitiesResult> GetByIds(IList <TKey> ids, SelectExpandArguments args, string action, CancellationToken cancellation)
        {
            await Initialize(cancellation);

            // Parse the parameters
            var expand = ExpressionExpand.Parse(args?.Expand);
            var select = ParseSelect(args?.Select);

            // Prepare the permissions filter
            var permissionsFilter = await UserPermissionsFilter(action, cancellation);

            // Load the data
            var data = await GetEntitiesByIds(ids, expand, select, permissionsFilter, cancellation);

            // Return result
            return(await ToEntitiesResult(data, data.Count, cancellation));
        }
Example #8
0
        /// <summary>
        /// Returns a <see cref="List{TEntity}"/> as per the Ids and the specifications in the <see cref="ExpressionExpand"/> and <see cref="ExpressionSelect"/>,
        /// after verifying the user's permissions, returns the entities in the same order as the supplied Ids.
        /// If null was supplied for permission filter, the function by default uses the filter for the read permissions filter
        /// </summary>
        protected virtual async Task <List <TEntity> > GetEntitiesByIds(
            List <TKey> ids,
            ExpressionExpand expand,
            ExpressionSelect select,
            ExpressionFilter permissionsFilter,
            CancellationToken cancellation)
        {
            if (ids == null || ids.Count == 0)
            {
                return(new List <TEntity>());
            }
            else
            {
                var data = await GetEntitiesByCustomQuery(q => q.FilterByIds(ids), expand, select, null, permissionsFilter, cancellation);

                // If the data is only
                if (ids.Count == 1 && data.Count == 1)
                {
                    // No need to sort
                    return(data);
                }
                else
                {
                    // Sort the entities according to the original Ids, as a good practice
                    TEntity[] dataSorted = new TEntity[ids.Count];
                    Dictionary <TKey, TEntity> dataDic = data.ToDictionary(e => e.Id);
                    for (int i = 0; i < ids.Count; i++)
                    {
                        var id = ids[i];
                        if (dataDic.TryGetValue(id, out TEntity entity))
                        {
                            dataSorted[i] = entity;
                        }
                    }

                    return(dataSorted.Where(e => e != null).ToList());
                }
            }
        }
Example #9
0
        /// <summary>
        /// Returns a list of entities and optionally their count as per the specifications in <paramref name="args"/>.
        /// </summary>
        public virtual async Task <TEntitiesResult> GetEntities(GetArguments args, CancellationToken cancellation)
        {
            await Initialize(cancellation);

            // Parse the parameters
            var filter  = ExpressionFilter.Parse(args.Filter);
            var orderby = ExpressionOrderBy.Parse(args.OrderBy);
            var expand  = ExpressionExpand.Parse(args.Expand);
            var select  = ParseSelect(args.Select);

            // Prepare the query
            var query = QueryFactory().EntityQuery <TEntity>();

            // Apply read permissions
            var permissionsFilter = await UserPermissionsFilter(PermissionActions.Read, cancellation);

            query = query.Filter(permissionsFilter);

            // Apply search
            query = await Search(query, args, cancellation);

            // Apply filter
            query = query.Filter(filter);

            // Apply orderby
            orderby ??= await DefaultOrderBy(cancellation);

            query = query.OrderBy(orderby);

            // Apply the paging (Protect against DOS attacks by enforcing a maximum page size)
            var top  = args.Top;
            var skip = args.Skip;

            top   = Math.Min(top, MaximumPageSize());
            query = query.Skip(skip).Top(top);

            // Apply the expand, which has the general format 'Expand=A,B.C,D'
            query = query.Expand(expand);

            // Apply the select, which has the general format 'Select=A,B.C,D'
            query = query.Select(select);

            // Load the data and count in memory
            List <TEntity> data;
            int?           count = null;

            if (args.CountEntities)
            {
                var output = await query.ToListAndCountAsync(MaximumCount, QueryContext, cancellation);

                data  = output.Entities;
                count = output.Count;
            }
            else
            {
                data = await query.ToListAsync(QueryContext, cancellation);
            }

            // Return
            return(await ToEntitiesResult(data, count, cancellation));
        }