< Summary

Information
Class: Services.ApiServices.BergApi.BergHttpClient
Assembly: Services
File(s): /home/runner/work/Studio2Prod/Studio2Prod/AppRepository/ApiServices/BergApi/BergHttpClient.cs
Line coverage
97%
Covered lines: 39
Uncovered lines: 1
Coverable lines: 40
Total lines: 86
Line coverage: 97.5%
Branch coverage
94%
Covered branches: 17
Total branches: 18
Branch coverage: 94.4%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
GetAsync(...)100%11100%
PostAsync(...)100%11100%
SendAsync()93.75%161696.42%
BuildUri(...)100%22100%

File(s)

/home/runner/work/Studio2Prod/Studio2Prod/AppRepository/ApiServices/BergApi/BergHttpClient.cs

#LineLine coverage
 1using Newtonsoft.Json.Linq;
 2using Services.ApiServices.ApiSupplierRequestLog;
 3
 4namespace Services.ApiServices.BergApi;
 5
 6/// <summary>Клиент read-only API Berg v1.0.</summary>
 7public sealed class BergHttpClient
 8{
 9    private readonly IHttpClientFactory _factory;
 10    private readonly BergConfig _config;
 11    private readonly ApiSupplierRequestLogStore _logs;
 12
 4213    public BergHttpClient(IHttpClientFactory factory, BergConfig config, ApiSupplierRequestLogStore logs)
 14    {
 4215        _factory = factory;
 4216        _config = config;
 4217        _logs = logs;
 4218    }
 19
 20    public Task<JToken> GetAsync(string path, IReadOnlyDictionary<string, string?>? query = null, CancellationToken ct =
 2421        SendAsync(HttpMethod.Get, path, query, body: null, ct);
 22
 23    /// <summary>
 24    /// Запись в Berg (сейчас это только <c>ordering/place_order.json</c>). Berg принимает строгий
 25    /// JSON: ключи в кавычках, числа без кавычек — поэтому тело сериализуется, а не собирается строкой.
 26    /// </summary>
 27    public Task<JToken> PostAsync(string path, JToken body, CancellationToken ct = default) =>
 328        SendAsync(HttpMethod.Post, path, query: null, body, ct);
 29
 30    private async Task<JToken> SendAsync(HttpMethod method, string path, IReadOnlyDictionary<string, string?>? query,
 31        JToken? body, CancellationToken ct)
 32    {
 2733        if (string.IsNullOrWhiteSpace(_config.ApiKey))
 134            throw new BergApiException("Не настроен Berg:ApiKey.");
 35
 36        // Ключ уходит в лог под именем заголовка: ApiSupplierRequestLogStore.Redact знает
 37        // x-berg-api-key и заменяет значение звёздочками.
 2638        var loggedRequest = "X-Berg-API-Key=" + _config.ApiKey;
 2639        var payload = body?.ToString(Newtonsoft.Json.Formatting.None);
 2640        if (payload != null)
 341            loggedRequest += " " + payload;
 42
 2643        var timer = System.Diagnostics.Stopwatch.StartNew();
 44        try
 45        {
 2646            using var request = new HttpRequestMessage(method, BuildUri(path, query));
 2647            request.Headers.TryAddWithoutValidation("X-Berg-API-Key", _config.ApiKey);
 2648            if (payload != null)
 349                request.Content = new StringContent(payload, System.Text.Encoding.UTF8, "application/json");
 50
 2651            using var response = await _factory.CreateClient("BergApi").SendAsync(request, ct);
 2452            var text = await response.Content.ReadAsStringAsync(ct);
 2453            _logs.Add("Berg", method.Method, path, (int)response.StatusCode, timer, loggedRequest, text);
 54
 55            // Ошибки размещения заказа Berg отдаёт телом {"errors":[…]} вместе с кодом 4xx.
 56            // Тело важнее статуса: без него пользователь не узнает, какая строка не прошла.
 2457            JToken? parsed = null;
 4758            try { parsed = JToken.Parse(text); }
 159            catch (Exception ex)
 60            {
 161                if (response.IsSuccessStatusCode)
 062                    throw new BergApiException("Berg вернул невалидный JSON.", requestPath: path, innerException: ex);
 163            }
 64
 2465            if (!response.IsSuccessStatusCode && parsed?["errors"] is null)
 366                throw new BergApiException($"Berg вернул HTTP {(int)response.StatusCode}.", (int)response.StatusCode, pa
 67
 2168            return parsed!;
 69        }
 670        catch (BergApiException) { throw; }
 271        catch (Exception ex)
 72        {
 273            _logs.Add("Berg", method.Method, path, null, timer, loggedRequest, ex.Message);
 274            throw new BergApiException("Не удалось выполнить запрос к Berg.", requestPath: path, innerException: ex);
 75        }
 2176    }
 77
 78    private Uri BuildUri(string path, IReadOnlyDictionary<string, string?>? query)
 79    {
 2680        var builder = new UriBuilder(_config.BaseUrl.TrimEnd('/') + "/v1.0/" + path.TrimStart('/'));
 2681        if (query != null)
 4382            builder.Query = string.Join("&", query.Where(x => !string.IsNullOrWhiteSpace(x.Value))
 4383                .Select(x => $"{Uri.EscapeDataString(x.Key)}={Uri.EscapeDataString(x.Value!)}"));
 2684        return builder.Uri;
 85    }
 86}