using System.Globalization; using System.Net.Http; using System.Net.Http.Headers; using System.Text.Json; using System.Text.Json.Serialization; namespace OpenNest.Data; /// Wire conventions shared by the nest server's HTTP clients. internal static class NestServerHttp { /// The server's JSON shape: camel-case names and enum names. public static readonly JsonSerializerOptions JsonOptions = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, PropertyNameCaseInsensitive = true, Converters = { new JsonStringEnumConverter(JsonNamingPolicy.CamelCase) }, }; /// Guards a write on the revision the caller last read. public static void AddIfMatch(HttpRequestMessage request, int expectedRevision) => request.Headers.IfMatch.Add(new EntityTagHeaderValue($"\"{expectedRevision.ToString(CultureInfo.InvariantCulture)}\"")); /// Throws an naming the action, status and the start of the body. public static async Task EnsureSuccessAsync(HttpResponseMessage response, string action, CancellationToken cancellationToken) { if (response.IsSuccessStatusCode) return; var detail = ""; try { detail = await response.Content.ReadAsStringAsync(cancellationToken); } catch (Exception ex) when (ex is IOException or InvalidOperationException) { // The status code is the useful part; the body is best-effort. } if (detail.Length > 200) detail = detail[..200] + "…"; throw new IOException( $"Could not {action} on the nest server: HTTP {(int)response.StatusCode} {response.ReasonPhrase}. {detail}".TrimEnd()); } }