< Summary

Information
Class: OzonOrdersWeb.WebServices.Orders.Grid.OrdersGridScopedQuery
Assembly: OzonOrdersWeb
File(s): /home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/WebServices/Orders/Grid/OrdersGridScopedQuery.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 50
Coverable lines: 50
Total lines: 113
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 22
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%210%
ResolveUserAsync()0%4260%
BuildAsync(...)100%210%
BuildFilteredAsync(...)0%210140%
FilterAccessibleAsync()0%620%

File(s)

/home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/WebServices/Orders/Grid/OrdersGridScopedQuery.cs

#LineLine coverage
 1using System.Security.Claims;
 2using Microsoft.EntityFrameworkCore;
 3using OzonDomains.Models;
 4using OzonDomains.Models.AccessControl;
 5using OzonRepositories.Context;
 6using OzonRepositories.Context.Identity;
 7using Services.CacheServices.Cache.UserCacheService;
 8using Services.DataServices;
 9using Services.FiltersServices.SortModels;
 10
 11namespace OzonOrdersWeb.WebServices.Orders.Grid;
 12
 13/// <summary>Пользователь и его профиль доступа — то, от чего зависит RLS-скоуп запросов грида.</summary>
 14public sealed record OrdersGridUserContext(CustomIdentityUser User, AccessProfile? Profile);
 15
 16public interface IOrdersGridScopedQuery
 17{
 18    /// <summary>Пользователь из principal; null — не найден (запрос надо отклонить).</summary>
 19    Task<OrdersGridUserContext?> ResolveUserAsync(ClaimsPrincipal principal);
 20
 21    /// <summary>
 22    /// Базовый запрос заказов, доступных пользователю (профиль доступа + RLS-скоуп + архив). Все точечные чтения
 23    /// грида (карточка, история статусов, дельта автообновления, фасеты, счётчики пресетов) строятся поверх него.
 24    /// </summary>
 25    Task<IQueryable<Order>> BuildAsync(OrdersGridUserContext user, bool includeArchived, CancellationToken cancellationT
 26
 27    /// <summary>Тот же скоуп плюс фильтры грида (маппер, доп. фильтры, вычисляемые столбцы, onlyIds).</summary>
 28    Task<IQueryable<Order>> BuildFilteredAsync(OrdersGridUserContext user, AgGridRequestMapper.MappedRequest mapped,
 29        IReadOnlyList<(OrdersGridFormulaTranslator.Translation Translation, OrdersGridComputedFilters.ComputedFilter Fil
 30        IReadOnlyList<int>? onlyIds, CancellationToken cancellationToken);
 31
 32    /// <summary>Из запрошенных Id оставить только доступные пользователю (порядок сохраняется).</summary>
 33    Task<List<int>> FilterAccessibleAsync(OrdersGridUserContext user, IReadOnlyList<int> ids, bool includeArchived, Canc
 34}
 35
 36public sealed class OrdersGridScopedQuery : IOrdersGridScopedQuery
 37{
 38    private readonly IOrdersIndexQueryService _queryService;
 39    private readonly IUserCacheService _userCacheService;
 40    private readonly AccessProfileDataServices _accessProfileDataServices;
 41    private readonly ApplicationDbContext _context;
 42
 043    public OrdersGridScopedQuery(
 044        IOrdersIndexQueryService queryService,
 045        IUserCacheService userCacheService,
 046        AccessProfileDataServices accessProfileDataServices,
 047        ApplicationDbContext context)
 48    {
 049        _queryService = queryService;
 050        _userCacheService = userCacheService;
 051        _accessProfileDataServices = accessProfileDataServices;
 052        _context = context;
 053    }
 54
 55    public async Task<OrdersGridUserContext?> ResolveUserAsync(ClaimsPrincipal principal)
 56    {
 057        var user = await _userCacheService.GetCachedUserAsync(principal);
 058        if (user is null) return null;
 059        AccessProfile? profile = null;
 060        if (user.AccessProfileId is > 0)
 61        {
 062            profile = await _accessProfileDataServices.GetAccessProfileAsync(user.AccessProfileId.Value);
 63        }
 64
 065        return new OrdersGridUserContext(user, profile);
 066    }
 67
 68    public Task<IQueryable<Order>> BuildAsync(OrdersGridUserContext user, bool includeArchived, CancellationToken cancel
 69    {
 070        return _queryService.BuildFilteredQueryableAsync(new OrdersIndexQueryRequest
 071        {
 072            Filter = new OServices.FiltersServices.FilterModels.OrderFilterModel { IncludeArchived = includeArchived, Gr
 073            SortOrder = OrderSortState.StandardState,
 074            AccessProfile = user.Profile,
 075            HasFilters = true,
 076            UserId = user.User.Id,
 077            CancellationToken = cancellationToken
 078        });
 79    }
 80
 81    public Task<IQueryable<Order>> BuildFilteredAsync(OrdersGridUserContext user, AgGridRequestMapper.MappedRequest mapp
 82        IReadOnlyList<(OrdersGridFormulaTranslator.Translation Translation, OrdersGridComputedFilters.ComputedFilter Fil
 83        IReadOnlyList<int>? onlyIds, CancellationToken cancellationToken)
 84    {
 085        var hasPost = mapped.MultiValueFilters.Count > 0 || mapped.ExtraFilters.Count > 0 || computedFilters.Count > 0 |
 086        return _queryService.BuildFilteredQueryableAsync(new OrdersIndexQueryRequest
 087        {
 088            Filter = mapped.Filter,
 089            SortOrder = OrderSortState.StandardState,
 090            AccessProfile = user.Profile,
 091            HasFilters = true,
 092            UserId = user.User.Id,
 093            CancellationToken = cancellationToken,
 094            QueryPostProcessor = hasPost
 095                ? q =>
 096                {
 097                    var filtered = OrdersGridComputedFilters.Apply(OrdersGridExtraFilters.Apply(
 098                        OrdersGridMultiValueFilters.Apply(q, mapped.MultiValueFilters, _context), mapped.ExtraFilters), 
 099                    return onlyIds is { Count: > 0 } ? filtered.Where(o => onlyIds.Contains(o.Id)) : filtered;
 0100                }
 0101                : null
 0102        });
 103    }
 104
 105    public async Task<List<int>> FilterAccessibleAsync(OrdersGridUserContext user, IReadOnlyList<int> ids, bool includeA
 106    {
 0107        if (ids.Count == 0) return [];
 0108        var query = await BuildAsync(user, includeArchived, cancellationToken);
 0109        var list = ids.Distinct().ToList();
 0110        var allowed = new HashSet<int>(await query.Where(o => list.Contains(o.Id)).Select(o => o.Id).ToListAsync(cancell
 0111        return ids.Where(allowed.Contains).Distinct().ToList();
 0112    }
 113}