< Summary

Information
Class: OzonOrdersWeb.Areas.Studio2.Controllers.InterpartsPriceApiController
Assembly: OzonOrdersWeb
File(s): /home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/Areas/Studio2/Controllers/InterpartsPriceApiController.cs
Line coverage
19%
Covered lines: 13
Uncovered lines: 54
Coverable lines: 67
Total lines: 151
Line coverage: 19.4%
Branch coverage
13%
Covered branches: 4
Total branches: 30
Branch coverage: 13.3%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
Search()40%84109.67%
Details(...)0%7280%
ResolveInterpartsLoginAsync()0%156120%

File(s)

/home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/Areas/Studio2/Controllers/InterpartsPriceApiController.cs

#LineLine coverage
 1using Microsoft.AspNetCore.Authorization;
 2using Microsoft.EntityFrameworkCore;
 3using Microsoft.AspNetCore.Mvc;
 4using System.Text.Encodings.Web;
 5using OzonRepositories.Context;
 6using Services.ApiServices.InterpartsPriceApi;
 7using Services.ApiServices.InterpartsPriceApi.Models;
 8using Services.ApiServices.TradesoftApi.Models.Response;
 9
 10namespace OzonOrdersWeb.Areas.Studio2.Controllers;
 11
 12[Area("Studio2")]
 13[Authorize(Roles = "User,Admin")]
 14public class InterpartsPriceApiController : Controller
 15{
 16    private readonly InterpartsPriceApiDataManager _dataManager;
 17    private readonly ApplicationDbContext _context;
 18    private readonly InterpartsCurrencyRateService _currencyRateService;
 19    private readonly InterpartsPriceDiagnosticsStore _diagnostics;
 20
 521    public InterpartsPriceApiController(
 522        InterpartsPriceApiDataManager dataManager,
 523        ApplicationDbContext context,
 524        InterpartsCurrencyRateService currencyRateService,
 525        InterpartsPriceDiagnosticsStore diagnostics)
 26    {
 527        _dataManager = dataManager;
 528        _context = context;
 529        _currencyRateService = currencyRateService;
 530        _diagnostics = diagnostics;
 531    }
 32
 33    /// <summary>
 34    /// Поиск цен в `price-api.interparts.ru` по коду и производителю.
 35    /// </summary>
 36    /// <remarks>
 37    /// Минимальный пример:
 38    /// `GET /Studio2/InterpartsPriceApi/Search?code=B24022JC&producerId=912`
 39    ///
 40    /// Расширенный пример (переопределение необязательных параметров):
 41    /// `GET /Studio2/InterpartsPriceApi/Search?code=B24022JC&producerId=912&coef=1&useSets=true&calculateManagerPrice=f
 42    /// </remarks>
 43    /// <param name="code">Артикул (code) для поиска.</param>
 44    /// <param name="producerId">ID производителя (producerId) для поиска.</param>
 45    /// <param name="orderId">ID заказа. Если передан, login определяется по складу отгрузки заказа.</param>
 46    /// <param name="regionId">Регион (regionId). По умолчанию: 1.</param>
 47    /// <param name="login">Логин (login). Если не передан, выбирается из таблицы связей склада, иначе fallback admin.</
 48    /// <param name="coef">Коэффициент (coef). По умолчанию: 1.</param>
 49    /// <param name="destCurrencyId">Целевая валюта (destCurrencyId). По умолчанию: 2.</param>
 50    /// <param name="useCrosses">Флаг `useCrosses`. По умолчанию: false.</param>
 51    /// <param name="useSets">Флаг `useSets`. По умолчанию: true.</param>
 52    /// <param name="calculateManagerPrice">Флаг `calculateManagerPrice`. По умолчанию: true.</param>
 53    /// <param name="useCrossesSimple">Флаг `useCrossesSimple`. По умолчанию: false.</param>
 54    [HttpGet]
 55    public async Task<IActionResult> Search(
 56        string code,
 57        int producerId,
 58        int? orderId = null,
 59        int regionId = 1,
 60        string? login = null,
 61        double coef = 1,
 62        int destCurrencyId = 2,
 63        bool useCrosses = false,
 64        bool useSets = true,
 65        bool calculateManagerPrice = true,
 66        bool useCrossesSimple = false)
 67    {
 568        if (string.IsNullOrWhiteSpace(code) || producerId <= 0)
 69        {
 570            return Ok(Array.Empty<PreOrderItem>());
 71        }
 72
 073        var request = new InterpartsPriceSearchRequest
 074        {
 075            Code = code.Trim(),
 076            ProducerId = producerId,
 077            RegionId = regionId,
 078            Login = await ResolveInterpartsLoginAsync(orderId, login).ConfigureAwait(false),
 079            Coef = coef,
 080            DestCurrencyId = destCurrencyId,
 081            UseCrosses = useCrosses,
 082            UseSets = useSets,
 083            CalculateManagerPrice = calculateManagerPrice,
 084            UseCrossesSimple = useCrossesSimple
 085        };
 86
 87        try
 88        {
 089            var response = await _dataManager.SearchAsync(request).ConfigureAwait(false);
 090            if (!string.IsNullOrWhiteSpace(response.ErrorMessage))
 91            {
 092                var id = _diagnostics.Add(response.RequestUrl ?? "—", response.HttpStatusCode, response.ErrorMessage, re
 093                return Ok(new
 094                {
 095                    items = Array.Empty<PreOrderItem>(),
 096                    error = response.ErrorMessage,
 097                    upstreamStatusCode = response.HttpStatusCode,
 098                    diagnosticsUrl = Url.Action(nameof(Details), new { id })
 099                });
 100            }
 101
 0102            var currencyDirectory = await _currencyRateService.GetDirectoryAsync(HttpContext.RequestAborted).ConfigureAw
 0103            var items = response.Offers.ToPreOrderItems(request.Login, currencyDirectory.Rates, currencyDirectory.Names)
 0104            return Ok(items);
 105        }
 0106        catch (Exception ex) when (ex is HttpRequestException or TimeoutException or TaskCanceledException)
 107        {
 0108            return Ok(new { items = Array.Empty<PreOrderItem>(), error = "Сервис Interparts Price API временно недоступе
 109        }
 5110    }
 111
 112    [HttpGet]
 113    public IActionResult Details(Guid id)
 114    {
 0115        var entry = _diagnostics.Get(id);
 0116        if (entry is null) return NotFound("Детали ответа Interparts Price не найдены или срок их хранения истёк.");
 117
 0118        Func<string?, string> encode = value => HtmlEncoder.Default.Encode(value ?? string.Empty);
 0119        var status = entry.StatusCode?.ToString() ?? "нет HTTP-ответа";
 0120        var html = $$"""<!doctype html><html lang="ru"><head><meta charset="utf-8"><title>Interparts Price — детали отве
 0121        return Content(html, "text/html; charset=utf-8");
 122    }
 123
 124    private async Task<string> ResolveInterpartsLoginAsync(int? orderId, string? loginFromRequest)
 125    {
 0126        if (!string.IsNullOrWhiteSpace(loginFromRequest))
 0127            return loginFromRequest.Trim();
 128
 0129        if (!orderId.HasValue || orderId.Value <= 0)
 0130            return "admin";
 131
 0132        var warehouseId = await _context.Orders
 0133            .AsNoTracking()
 0134            .Where(o => o.Id == orderId.Value)
 0135            .Select(o => o.ShipmentWarehouseId)
 0136            .FirstOrDefaultAsync()
 0137            .ConfigureAwait(false);
 138
 0139        if (!warehouseId.HasValue || warehouseId.Value <= 0)
 0140            return "admin";
 141
 0142        var mappedLogin = await _context.InterpartsWarehouseLogins
 0143            .AsNoTracking()
 0144            .Where(x => x.WarehouseId == warehouseId.Value)
 0145            .Select(x => x.Login)
 0146            .FirstOrDefaultAsync()
 0147            .ConfigureAwait(false);
 148
 0149        return string.IsNullOrWhiteSpace(mappedLogin) ? "admin" : mappedLogin.Trim();
 0150    }
 151}