< Summary

Information
Class: Services.SearchServices.OrderSearch.OrderSearchDiagnosticsService
Assembly: Services
File(s): /home/runner/work/Studio2Prod/Studio2Prod/AppRepository/SearchServices/OrderSearch/OrderSearchDiagnosticsService.cs
Line coverage
100%
Covered lines: 116
Uncovered lines: 0
Coverable lines: 116
Total lines: 184
Line coverage: 100%
Branch coverage
97%
Covered branches: 41
Total branches: 42
Branch coverage: 97.6%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
GetSnapshotAsync()100%1616100%
LookupAsync()100%88100%
LookupByShipmentNumberAsync()90%1010100%
BuildLookupItemAsync()100%88100%
get_Id()100%11100%

File(s)

/home/runner/work/Studio2Prod/Studio2Prod/AppRepository/SearchServices/OrderSearch/OrderSearchDiagnosticsService.cs

#LineLine coverage
 1using Microsoft.EntityFrameworkCore;
 2using Microsoft.Extensions.Configuration;
 3using OServices.FiltersServices.FilterModels;
 4using OzonRepositories.Context;
 5using Services.FiltersServices.DataFilterManagers;
 6
 7namespace Services.SearchServices.OrderSearch;
 8
 9public interface IOrderSearchDiagnosticsService
 10{
 11    Task<OrderSearchDiagnosticsSnapshot> GetSnapshotAsync(CancellationToken cancellationToken = default);
 12
 13    Task<OrderSearchLookupResponse> LookupAsync(int? orderId, string? shipmentNumber,
 14        CancellationToken cancellationToken = default);
 15}
 16
 17public sealed class OrderSearchDiagnosticsService : IOrderSearchDiagnosticsService
 18{
 19    private readonly IOrderSearchService _searchService;
 20    private readonly IOrderSearchDiagnosticsStore _store;
 21    private readonly ApplicationDbContext _context;
 22    private readonly IConfiguration _configuration;
 23
 2724    public OrderSearchDiagnosticsService(
 2725        IOrderSearchService searchService,
 2726        IOrderSearchDiagnosticsStore store,
 2727        ApplicationDbContext context,
 2728        IConfiguration configuration)
 29    {
 2730        _searchService = searchService;
 2731        _store = store;
 2732        _context = context;
 2733        _configuration = configuration;
 2734    }
 35
 36    public async Task<OrderSearchDiagnosticsSnapshot> GetSnapshotAsync(CancellationToken cancellationToken = default)
 37    {
 1038        var esEnabled = _configuration.GetValue<bool>("OrdersSearch:UseElasticsearch");
 1039        var (avgMs, maxMs, searchCount) = _store.GetSearchStats();
 40
 1041        long indexCount = 0;
 1042        var indexExists = false;
 1043        if (esEnabled)
 44        {
 945            indexExists = await _searchService.IndexExistsAsync();
 946            if (indexExists)
 47            {
 848                indexCount = await _searchService.GetTotalCountAsync();
 49            }
 50        }
 51
 1052        var dbOrderCount = await _context.Orders.AsNoTracking().CountAsync(cancellationToken);
 1053        var driftThreshold = OrderSearchDriftChecker.GetAlertThreshold(_configuration);
 1054        var countDrift = Math.Abs(indexCount - dbOrderCount);
 1055        var isDriftAlert = esEnabled && indexExists && countDrift > driftThreshold;
 1056        var lastDrift = _store.GetLastDriftEvent();
 1057        var syncState = await _context.OrderSearchSyncStates.AsNoTracking()
 1058            .FirstOrDefaultAsync(s => s.Id == 1, cancellationToken);
 59
 1060        return new OrderSearchDiagnosticsSnapshot
 1061        {
 1062            EsEnabled = esEnabled,
 1063            EsUrl = _configuration["OrdersSearch:Url"] ?? "http://localhost:9201",
 1064            IndexName = _configuration["OrdersSearch:IndexName"] ?? "orders",
 1065            IndexExists = indexExists,
 1066            IndexCount = indexCount,
 1067            DbOrderCount = dbOrderCount,
 1068            CountDrift = countDrift,
 1069            DriftAlertThreshold = driftThreshold,
 1070            IsDriftAlert = isDriftAlert,
 1071            LastDriftCheckAt = lastDrift?.TimestampUtc,
 1072            LastSyncedAt = syncState?.LastSyncedAt,
 1073            LastFullReindexAt = syncState?.LastFullReindexAt,
 1074            AvgSearchMs = Math.Round(avgMs, 1),
 1075            MaxSearchMs = maxMs,
 1076            SearchSampleCount = searchCount,
 1077            IndexEvents = _store.GetIndexEvents(),
 1078            SearchEvents = _store.GetSearchEvents(),
 1079            DriftEvents = _store.GetDriftEvents()
 1080        };
 1081    }
 82
 83    public async Task<OrderSearchLookupResponse> LookupAsync(int? orderId, string? shipmentNumber,
 84        CancellationToken cancellationToken = default)
 85    {
 1786        if (orderId is > 0)
 87        {
 588            var item = await BuildLookupItemAsync(orderId.Value, cancellationToken);
 589            return new OrderSearchLookupResponse
 590            {
 591                QueryLabel = $"ID {orderId.Value}",
 592                Items = new[] { item }
 593            };
 94        }
 95
 1296        var term = shipmentNumber?.Trim();
 1297        if (string.IsNullOrWhiteSpace(term))
 98        {
 799            return new OrderSearchLookupResponse();
 100        }
 101
 5102        return await LookupByShipmentNumberAsync(term, cancellationToken);
 17103    }
 104
 105    private async Task<OrderSearchLookupResponse> LookupByShipmentNumberAsync(string shipmentNumber,
 106        CancellationToken cancellationToken)
 107    {
 108        const int maxResults = 50;
 5109        var normalized = OrderDataFilterManager.NormalizeShipmentNumberForSearch(shipmentNumber);
 110
 5111        var dbOrders = string.IsNullOrEmpty(normalized)
 5112            ? new List<DbOrderRow>()
 5113            : await _context.Orders.AsNoTracking()
 5114                .Where(o => o.ShipmentNumber != null && o.ShipmentNumber.Contains(normalized))
 5115                .OrderByDescending(o => o.Id)
 5116                .Take(maxResults)
 5117                .Select(o => new DbOrderRow(o.Id, o.ShipmentNumber, o.Article, o.UpdatedAt))
 5118                .ToListAsync(cancellationToken);
 119
 5120        HashSet<int> indexIds = new();
 5121        if (_configuration.GetValue<bool>("OrdersSearch:UseElasticsearch"))
 122        {
 2123            var matched = await _searchService.SearchOrderIdsAsync(
 2124                new OrderFilterModel { ShipmentNumber = shipmentNumber },
 2125                maxResults: maxResults);
 2126            indexIds = matched.Ids.ToHashSet();
 127        }
 128
 5129        var items = new List<OrderSearchLookupResult>();
 122130        foreach (var row in dbOrders)
 131        {
 56132            OrderSearchDocument? doc = null;
 56133            var inIndex = indexIds.Contains(row.Id);
 56134            if (inIndex && _configuration.GetValue<bool>("OrdersSearch:UseElasticsearch"))
 135            {
 3136                doc = await _searchService.GetDocumentByIdAsync(row.Id);
 137            }
 138
 56139            items.Add(new OrderSearchLookupResult
 56140            {
 56141                OrderId = row.Id,
 56142                InIndex = inIndex,
 56143                InDatabase = true,
 56144                Document = doc,
 56145                DbShipmentNumber = row.ShipmentNumber,
 56146                DbArticle = row.Article,
 56147                DbUpdatedAt = row.UpdatedAt
 56148            });
 56149        }
 150
 5151        return new OrderSearchLookupResponse
 5152        {
 5153            QueryLabel = $"номер «{shipmentNumber}»",
 5154            Items = items
 5155        };
 5156    }
 157
 158    private async Task<OrderSearchLookupResult> BuildLookupItemAsync(int orderId, CancellationToken cancellationToken)
 159    {
 5160        var dbOrder = await _context.Orders.AsNoTracking()
 5161            .Where(o => o.Id == orderId)
 5162            .Select(o => new DbOrderRow(o.Id, o.ShipmentNumber, o.Article, o.UpdatedAt))
 5163            .FirstOrDefaultAsync(cancellationToken);
 164
 5165        OrderSearchDocument? doc = null;
 5166        if (_configuration.GetValue<bool>("OrdersSearch:UseElasticsearch"))
 167        {
 4168            doc = await _searchService.GetDocumentByIdAsync(orderId);
 169        }
 170
 5171        return new OrderSearchLookupResult
 5172        {
 5173            OrderId = orderId,
 5174            InIndex = doc != null,
 5175            InDatabase = dbOrder != null,
 5176            Document = doc,
 5177            DbShipmentNumber = dbOrder?.ShipmentNumber,
 5178            DbArticle = dbOrder?.Article,
 5179            DbUpdatedAt = dbOrder?.UpdatedAt
 5180        };
 5181    }
 182
 351183    private sealed record DbOrderRow(int Id, string? ShipmentNumber, string? Article, DateTime? UpdatedAt);
 184}