• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

loresoft / FluentRest / 9388947971

05 Jun 2024 05:56PM UTC coverage: 57.76% (+0.2%) from 57.587%
9388947971

push

github

pwelter34
fix flaky tests

277 of 614 branches covered (45.11%)

Branch coverage included in aggregate %.

847 of 1332 relevant lines covered (63.59%)

73.6 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

65.31
/src/FluentRest/HttpMessageExtensions.cs
1
// Ignore Spelling: Serializer Deserialize
2

3
namespace FluentRest;
4

5
/// <summary>
6
/// Extension method for <see cref="HttpRequestMessage"/>
7
/// </summary>
8
public static class HttpMessageExtensions
9
{
10
    public static TValue GetOrAddOption<TValue>(this HttpRequestMessage requestMessage, string key, Func<string, TValue> valueFactory)
11
    {
12
#if NET5_0_OR_GREATER
13
        var optionKey = new HttpRequestOptionsKey<TValue>(key);
1,470✔
14
        if (requestMessage.Options.TryGetValue(optionKey, out var value))
1,470✔
15
            return value;
1,176✔
16

17
        value = valueFactory(key);
294✔
18
        requestMessage.Options.Set(optionKey, value);
294✔
19
        return value;
294✔
20
#else
21
        if (requestMessage.Properties.TryGetValue(key, out var propertyValue))
22
            return (TValue)propertyValue;
23

24
        propertyValue = valueFactory(key);
25
        requestMessage.Properties.Add(key, propertyValue);
26

27
        return (TValue)propertyValue;
28
#endif
29
    }
30

31
    public static bool TryGetOption<TValue>(this HttpRequestMessage requestMessage, string key, out TValue value)
32
    {
33
#if NET5_0_OR_GREATER
34
        var optionKey = new HttpRequestOptionsKey<TValue>(key);
390✔
35
        return requestMessage.Options.TryGetValue(optionKey, out value);
390✔
36
#else
37
        var found = requestMessage.Properties.TryGetValue(key, out var propertyValue);
38
        value  = found ? (TValue)propertyValue : default;
39
        return found;
40
#endif
41
    }
42

43
    public static void SetOption<TValue>(this HttpRequestMessage requestMessage, string key, TValue value)
44
    {
45
#if NET5_0_OR_GREATER
46
        var optionKey = new HttpRequestOptionsKey<TValue>(key);
138✔
47
        requestMessage.Options.Set(optionKey, value);
138✔
48
#else
49
        requestMessage.Properties[key] = value;
50
#endif
51
    }
138✔
52

53
    /// <summary>
54
    /// Gets the <see cref="UrlBuilder"/> from the specified <paramref name="requestMessage" /> properties dictionary.
55
    /// </summary>
56
    /// <param name="requestMessage">The request message containing the property.</param>
57
    /// <returns>
58
    /// The <see cref="UrlBuilder"/> to modify the request message URI.
59
    /// </returns>
60
    /// <exception cref="ArgumentNullException"><paramref name="requestMessage"/> is <see langword="null"/></exception>
61
    public static UrlBuilder GetUrlBuilder(this HttpRequestMessage requestMessage)
62
    {
63
        if (requestMessage == null)
909!
64
            throw new ArgumentNullException(nameof(requestMessage));
×
65

66
        var propertyValue = requestMessage.GetOrAddOption(FluentProperties.RequestUrlBuilder, k =>
909✔
67
            requestMessage.RequestUri == null
162✔
68
                ? new UrlBuilder()
162✔
69
                : new UrlBuilder(requestMessage.RequestUri)
162✔
70
        );
909✔
71

72
        return propertyValue;
909✔
73
    }
74

75
    /// <summary>
76
    /// Sets the <see cref="UrlBuilder" /> on the specified <paramref name="requestMessage" /> properties dictionary.
77
    /// </summary>
78
    /// <param name="requestMessage">The request message containing the property.</param>
79
    /// <param name="urlBuilder">The URL bulder to set on the properties dictionary.</param>
80
    /// <exception cref="ArgumentNullException"><paramref name="requestMessage" /> is <see langword="null" /></exception>
81
    public static void SetUrlBuilder(this HttpRequestMessage requestMessage, UrlBuilder urlBuilder)
82
    {
83
        if (requestMessage == null)
27!
84
            throw new ArgumentNullException(nameof(requestMessage));
×
85

86
        requestMessage.SetOption(FluentProperties.RequestUrlBuilder, urlBuilder);
27✔
87
    }
27✔
88

89

90
    /// <summary>
91
    /// Gets the content data from the specified <paramref name="requestMessage" /> properties dictionary.
92
    /// </summary>
93
    /// <param name="requestMessage">The request message containing the property.</param>
94
    /// <returns>
95
    /// The content data to send for the request message.
96
    /// </returns>
97
    /// <exception cref="ArgumentNullException"><paramref name="requestMessage"/> is <see langword="null"/></exception>
98
    public static object GetContentData(this HttpRequestMessage requestMessage)
99
    {
100
        if (requestMessage == null)
78!
101
            throw new ArgumentNullException(nameof(requestMessage));
×
102

103
        requestMessage.TryGetOption<object>(FluentProperties.RequestContentData, out var propertyValue);
78✔
104
        return propertyValue;
78✔
105
    }
106

107
    /// <summary>
108
    /// Sets the content data on the specified <paramref name="requestMessage" /> properties dictionary.
109
    /// </summary>
110
    /// <param name="requestMessage">The request message containing the property.</param>
111
    /// <param name="contentData">The content data to send for the request message..</param>
112
    /// <exception cref="ArgumentNullException"><paramref name="requestMessage" /> is <see langword="null" /></exception>
113
    public static void SetContentData(this HttpRequestMessage requestMessage, object contentData)
114
    {
115
        if (requestMessage == null)
×
116
            throw new ArgumentNullException(nameof(requestMessage));
×
117

118
        requestMessage.SetOption(FluentProperties.RequestContentData, contentData);
×
119
    }
×
120

121

122
    /// <summary>
123
    /// Gets the form data property from the specified <paramref name="requestMessage" /> properties dictionary.
124
    /// </summary>
125
    /// <param name="requestMessage">The request message containing the property.</param>
126
    /// <returns>
127
    /// The dictionary of for data to send in the request message.
128
    /// </returns>
129
    /// <exception cref="ArgumentNullException"><paramref name="requestMessage"/> is <see langword="null"/></exception>
130
    public static Dictionary<string, ICollection<string>> GetFormData(this HttpRequestMessage requestMessage)
131
    {
132
        if (requestMessage == null)
180!
133
            throw new ArgumentNullException(nameof(requestMessage));
×
134

135
        var propertyValue = requestMessage.GetOrAddOption(FluentProperties.RequestFormData, k => new Dictionary<string, ICollection<string>>());
252✔
136
        return propertyValue;
180✔
137
    }
138

139

140
    /// <summary>
141
    /// Gets the completion option property from the specified <paramref name="requestMessage" /> properties dictionary.
142
    /// </summary>
143
    /// <param name="requestMessage">The request message containing the property.</param>
144
    /// <returns>
145
    /// The <see cref="HttpCompletionOption"/> to use when sending the request message.
146
    /// </returns>
147
    /// <exception cref="ArgumentNullException"><paramref name="requestMessage"/> is <see langword="null"/></exception>
148
    public static HttpCompletionOption GetCompletionOption(this HttpRequestMessage requestMessage)
149
    {
150
        if (requestMessage == null)
156!
151
            throw new ArgumentNullException(nameof(requestMessage));
×
152

153
        if (requestMessage.TryGetOption<HttpCompletionOption>(FluentProperties.HttpCompletionOption, out var value))
156!
154
            return value;
×
155

156
        return HttpCompletionOption.ResponseContentRead;
156✔
157
    }
158

159
    /// <summary>
160
    /// Sets the completion option property on the specified <paramref name="requestMessage" /> properties dictionary.
161
    /// </summary>
162
    /// <param name="requestMessage">The request message containing the property.</param>
163
    /// <param name="completionOption">The <see cref="HttpCompletionOption"/> to use when sending the request message.</param>
164
    /// <exception cref="ArgumentNullException"><paramref name="requestMessage"/> is <see langword="null"/></exception>
165
    public static void SetCompletionOption(this HttpRequestMessage requestMessage, HttpCompletionOption completionOption)
166
    {
167
        if (requestMessage == null)
×
168
            throw new ArgumentNullException(nameof(requestMessage));
×
169

170
        requestMessage.SetOption(FluentProperties.HttpCompletionOption, completionOption);
×
171
    }
×
172

173

174
    /// <summary>
175
    /// Gets the cancellation token property from the specified <paramref name="requestMessage" /> properties dictionary.
176
    /// </summary>
177
    /// <param name="requestMessage">The request message containing the property.</param>
178
    /// <returns>
179
    /// The <see cref="CancellationToken"/> to use when sending the request message.
180
    /// </returns>
181
    /// <exception cref="ArgumentNullException"><paramref name="requestMessage"/> is <see langword="null"/></exception>
182
    public static CancellationToken GetCancellationToken(this HttpRequestMessage requestMessage)
183
    {
184
        if (requestMessage == null)
156!
185
            throw new ArgumentNullException(nameof(requestMessage));
×
186

187
        if (requestMessage.TryGetOption<CancellationToken>(FluentProperties.CancellationToken, out var propertyValue))
156✔
188
            return propertyValue;
6✔
189

190
        return CancellationToken.None;
150✔
191
    }
192

193
    /// <summary>
194
    /// Sets the cancellation token property on the specified <paramref name="requestMessage" /> properties dictionary.
195
    /// </summary>
196
    /// <param name="requestMessage">The request message containing the property.</param>
197
    /// <param name="cancellationToken">The <see cref="CancellationToken"/> to use when sending the request message.</param>
198
    /// <exception cref="ArgumentNullException"><paramref name="requestMessage"/> is <see langword="null"/></exception>
199
    public static void SetCancellationToken(this HttpRequestMessage requestMessage, CancellationToken cancellationToken)
200
    {
201
        if (requestMessage == null)
6!
202
            throw new ArgumentNullException(nameof(requestMessage));
×
203

204
        requestMessage.SetOption(FluentProperties.CancellationToken, cancellationToken);
6✔
205
    }
6✔
206

207

208
    /// <summary>
209
    /// Gets the content serializer property from the specified <paramref name="requestMessage" /> properties dictionary.
210
    /// </summary>
211
    /// <param name="requestMessage">The request message containing the property.</param>
212
    /// <returns>
213
    /// The <see cref="IContentSerializer"/> to use when serializing content to send in the request message.
214
    /// </returns>
215
    /// <exception cref="ArgumentNullException"><paramref name="requestMessage"/> is <see langword="null"/></exception>
216
    public static IContentSerializer GetContentSerializer(this HttpRequestMessage requestMessage)
217
    {
218
        if (requestMessage == null)
381!
219
            throw new ArgumentNullException(nameof(requestMessage));
×
220

221
        var propertyValue = requestMessage.GetOrAddOption(FluentProperties.ContentSerializer, k => ContentSerializer.Current);
441✔
222
        return propertyValue;
381✔
223
    }
224

225
    /// <summary>
226
    /// Sets the content serializer property on the specified <paramref name="requestMessage" /> properties dictionary.
227
    /// </summary>
228
    /// <param name="requestMessage">The request message containing the property.</param>
229
    /// <param name="contentSerializer">The <see cref="IContentSerializer"/> to use when serializing content to send in the request message.</param>
230
    /// <exception cref="ArgumentNullException"><paramref name="requestMessage"/> is <see langword="null"/></exception>
231
    public static void SetContentSerializer(this HttpRequestMessage requestMessage, IContentSerializer contentSerializer)
232
    {
233
        if (requestMessage == null)
99!
234
            throw new ArgumentNullException(nameof(requestMessage));
×
235

236
        requestMessage.SetOption(FluentProperties.ContentSerializer, contentSerializer ?? ContentSerializer.Current);
99!
237
    }
99✔
238

239

240
    /// <summary>
241
    /// Synchronizes the specified request message with the fluent properties.
242
    /// </summary>
243
    /// <param name="requestMessage">The request message.</param>
244
    public static void Synchronize(this HttpRequestMessage requestMessage)
245
    {
246
        var urlBuilder = requestMessage.GetUrlBuilder();
378✔
247
        requestMessage.RequestUri = urlBuilder.ToUri();
378✔
248
    }
378✔
249

250

251
    /// <summary>
252
    /// Deserialize the HTTP response message asynchronously.
253
    /// </summary>
254
    /// <typeparam name="TData">The type of the data.</typeparam>
255
    /// <param name="responseMessage">The response message to deserialize.</param>
256
    /// <param name="ensureSuccess">Throw an exception if the HTTP response was unsuccessful.</param>
257
    /// <returns>
258
    /// The data object deserialized from the HTTP response message.
259
    /// </returns>
260
    /// <exception cref="HttpRequestException">Response status code does not indicate success.</exception>
261
    /// <exception cref="ArgumentNullException"><paramref name="responseMessage"/> is <see langword="null"/></exception>
262
    public static async Task<TData> DeserializeAsync<TData>(this HttpResponseMessage responseMessage, bool ensureSuccess = true)
263
    {
264
        if (responseMessage == null)
153!
265
            throw new ArgumentNullException(nameof(responseMessage));
×
266

267

268
        if (ensureSuccess)
153✔
269
            await responseMessage.EnsureSuccessStatusCode(true);
153✔
270

271
        var serializer = responseMessage.RequestMessage.GetContentSerializer();
147✔
272
        var data = await serializer
147✔
273
            .DeserializeAsync<TData>(responseMessage.Content)
147✔
274
            .ConfigureAwait(false);
147✔
275

276
        return data;
147✔
277
    }
147✔
278

279
    /// <summary>
280
    /// Throws an exception if the IsSuccessStatusCode property for the HTTP response is false.
281
    /// </summary>
282
    /// <param name="responseMessage">The response message.</param>
283
    /// <param name="includeContent">if set to <c>true</c> the response content is included in the exception.</param>
284
    /// <returns></returns>
285
    /// <exception cref="HttpRequestException">The HTTP response is unsuccessful.</exception>
286
    public static async Task EnsureSuccessStatusCode(this HttpResponseMessage responseMessage, bool includeContent)
287
    {
288
        if (responseMessage.IsSuccessStatusCode)
153✔
289
            return;
147✔
290

291
        // will throw if respose is a problem json
292
        await CheckResponseForProblem(responseMessage).ConfigureAwait(false);
6✔
293

294
        var message = $"Response status code does not indicate success: {responseMessage.StatusCode} ({responseMessage.ReasonPhrase});";
6✔
295

296
        if (!includeContent)
6!
297
            throw new HttpRequestException(message);
×
298

299
        var contentString = await responseMessage.Content.ReadAsStringAsync();
6✔
300
        if (string.IsNullOrEmpty(contentString))
6!
301
            throw new HttpRequestException(message);
6✔
302

303

304
        // add response content body to message for easier debugging
305
        message += Environment.NewLine + contentString;
×
306

307
        var exception = new HttpRequestException(message);
×
308
        exception.Data.Add("Response", contentString);
×
309

310
        throw exception;
×
311
    }
147✔
312

313
    private static async Task CheckResponseForProblem(HttpResponseMessage responseMessage)
314
    {
315
        string mediaType = responseMessage.Content?.Headers?.ContentType?.MediaType;
6!
316
        if (!string.Equals(mediaType, ProblemDetails.ContentType, StringComparison.OrdinalIgnoreCase))
6!
317
            return;
6✔
318

319
        var serializer = responseMessage.RequestMessage.GetContentSerializer();
×
320

321
        var problem = await serializer
×
322
            .DeserializeAsync<ProblemDetails>(responseMessage.Content)
×
323
            .ConfigureAwait(false);
×
324

325
        throw new ProblemException(problem);
×
326
    }
6✔
327
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc