< Summary

Information
Class: Chronicis.Client.Services.HttpClientExtensions
Assembly: Chronicis.Client
File(s): /home/runner/work/chronicis/chronicis/src/Chronicis.Client/Services/HttpClientExtensions.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 63
Coverable lines: 63
Total lines: 205
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 38
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
GetEntityAsync()0%2040%
GetListAsync()0%2040%
PostEntityAsync()0%4260%
PutEntityAsync()0%4260%
DeleteEntityAsync()0%4260%
PatchEntityAsync()0%4260%
PutBoolAsync()0%4260%

File(s)

/home/runner/work/chronicis/chronicis/src/Chronicis.Client/Services/HttpClientExtensions.cs

#LineLine coverage
 1using System.Net.Http.Json;
 2
 3namespace Chronicis.Client.Services;
 4
 5/// <summary>
 6/// Extension methods for HttpClient that encapsulate common API patterns.
 7/// These handle try/catch, logging, and response deserialization consistently.
 8///
 9/// Design note: These could later be moved into an ApiServiceBase class
 10/// if we decide to use inheritance for service classes.
 11/// </summary>
 12public static class HttpClientExtensions
 13{
 14    /// <summary>
 15    /// GET request that returns a single entity or null.
 16    /// Logs errors and returns null on failure (including 404).
 17    /// </summary>
 18    public static async Task<T?> GetEntityAsync<T>(
 19        this HttpClient http,
 20        string url,
 21        ILogger logger,
 22        string? entityDescription = null) where T : class
 23    {
 24        try
 25        {
 026            return await http.GetFromJsonAsync<T>(url);
 27        }
 028        catch (HttpRequestException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
 29        {
 030            logger.LogWarning("{Entity} not found at {Url}", entityDescription ?? typeof(T).Name, url);
 031            return null;
 32        }
 033        catch (Exception ex)
 34        {
 035            logger.LogError(ex, "Error fetching {Entity} from {Url}", entityDescription ?? typeof(T).Name, url);
 036            return null;
 37        }
 038    }
 39
 40    /// <summary>
 41    /// GET request that returns a list of entities.
 42    /// Logs errors and returns empty list on failure.
 43    /// </summary>
 44    public static async Task<List<T>> GetListAsync<T>(
 45        this HttpClient http,
 46        string url,
 47        ILogger logger,
 48        string? entityDescription = null)
 49    {
 50        try
 51        {
 052            var result = await http.GetFromJsonAsync<List<T>>(url);
 053            return result ?? new List<T>();
 54        }
 055        catch (Exception ex)
 56        {
 057            logger.LogError(ex, "Error fetching {Entity} list from {Url}", entityDescription ?? typeof(T).Name, url);
 058            return new List<T>();
 59        }
 060    }
 61
 62    /// <summary>
 63    /// POST request that creates an entity and returns it.
 64    /// Returns null on failure.
 65    /// </summary>
 66    public static async Task<T?> PostEntityAsync<T>(
 67        this HttpClient http,
 68        string url,
 69        object dto,
 70        ILogger logger,
 71        string? entityDescription = null) where T : class
 72    {
 73        try
 74        {
 075            var response = await http.PostAsJsonAsync(url, dto);
 076            if (response.IsSuccessStatusCode)
 77            {
 078                return await response.Content.ReadFromJsonAsync<T>();
 79            }
 80
 081            logger.LogWarning("Failed to create {Entity}: {StatusCode}",
 082                entityDescription ?? typeof(T).Name, response.StatusCode);
 083            return null;
 84        }
 085        catch (Exception ex)
 86        {
 087            logger.LogError(ex, "Error creating {Entity} at {Url}", entityDescription ?? typeof(T).Name, url);
 088            return null;
 89        }
 090    }
 91
 92    /// <summary>
 93    /// PUT request that updates an entity and returns it.
 94    /// Returns null on failure.
 95    /// </summary>
 96    public static async Task<T?> PutEntityAsync<T>(
 97        this HttpClient http,
 98        string url,
 99        object dto,
 100        ILogger logger,
 101        string? entityDescription = null) where T : class
 102    {
 103        try
 104        {
 0105            var response = await http.PutAsJsonAsync(url, dto);
 0106            if (response.IsSuccessStatusCode)
 107            {
 0108                return await response.Content.ReadFromJsonAsync<T>();
 109            }
 110
 0111            logger.LogWarning("Failed to update {Entity}: {StatusCode}",
 0112                entityDescription ?? typeof(T).Name, response.StatusCode);
 0113            return null;
 114        }
 0115        catch (Exception ex)
 116        {
 0117            logger.LogError(ex, "Error updating {Entity} at {Url}", entityDescription ?? typeof(T).Name, url);
 0118            return null;
 119        }
 0120    }
 121
 122    /// <summary>
 123    /// DELETE request that returns success/failure.
 124    /// </summary>
 125    public static async Task<bool> DeleteEntityAsync(
 126        this HttpClient http,
 127        string url,
 128        ILogger logger,
 129        string? entityDescription = null)
 130    {
 131        try
 132        {
 0133            var response = await http.DeleteAsync(url);
 0134            if (!response.IsSuccessStatusCode)
 135            {
 0136                logger.LogWarning("Failed to delete {Entity}: {StatusCode}",
 0137                    entityDescription ?? "entity", response.StatusCode);
 138            }
 0139            return response.IsSuccessStatusCode;
 140        }
 0141        catch (Exception ex)
 142        {
 0143            logger.LogError(ex, "Error deleting {Entity} at {Url}", entityDescription ?? "entity", url);
 0144            return false;
 145        }
 0146    }
 147
 148    /// <summary>
 149    /// PATCH request that returns success/failure.
 150    /// Useful for partial updates like moving articles.
 151    /// </summary>
 152    public static async Task<bool> PatchEntityAsync(
 153        this HttpClient http,
 154        string url,
 155        object dto,
 156        ILogger logger,
 157        string? entityDescription = null)
 158    {
 159        try
 160        {
 0161            var response = await http.PatchAsJsonAsync(url, dto);
 0162            if (!response.IsSuccessStatusCode)
 163            {
 0164                var errorContent = await response.Content.ReadAsStringAsync();
 0165                logger.LogWarning("Failed to patch {Entity}: {StatusCode} - {Error}",
 0166                    entityDescription ?? "entity", response.StatusCode, errorContent);
 167            }
 0168            return response.IsSuccessStatusCode;
 169        }
 0170        catch (Exception ex)
 171        {
 0172            logger.LogError(ex, "Error patching {Entity} at {Url}", entityDescription ?? "entity", url);
 0173            return false;
 174        }
 0175    }
 176
 177    /// <summary>
 178    /// PUT request that returns success/failure (no response body expected).
 179    /// Useful for operations like moving entities.
 180    /// </summary>
 181    public static async Task<bool> PutBoolAsync(
 182        this HttpClient http,
 183        string url,
 184        object dto,
 185        ILogger logger,
 186        string? entityDescription = null)
 187    {
 188        try
 189        {
 0190            var response = await http.PutAsJsonAsync(url, dto);
 0191            if (!response.IsSuccessStatusCode)
 192            {
 0193                var errorContent = await response.Content.ReadAsStringAsync();
 0194                logger.LogWarning("Failed to update {Entity}: {StatusCode} - {Error}",
 0195                    entityDescription ?? "entity", response.StatusCode, errorContent);
 196            }
 0197            return response.IsSuccessStatusCode;
 198        }
 0199        catch (Exception ex)
 200        {
 0201            logger.LogError(ex, "Error updating {Entity} at {Url}", entityDescription ?? "entity", url);
 0202            return false;
 203        }
 0204    }
 205}