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

microsoft / botbuilder-dotnet / 333683

15 Dec 2022 06:25PM UTC coverage: 79.054% (-0.008%) from 79.062%
333683

Pull #6570

CI-PR build

GitHub
Merge 605095e6d into a908ca355
Pull Request #6570: Support bot sdk dot net for targeted meeting notification

25800 of 32636 relevant lines covered (79.05%)

0.79 hits per line

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

59.9
/libraries/Microsoft.Bot.Connector/Teams/TeamsOperations.cs
1
// Copyright (c) Microsoft Corporation. All rights reserved.
2
// Licensed under the MIT License.
3

4
namespace Microsoft.Bot.Connector.Teams
5
{
6
    using System.Collections.Generic;
7
    using System.Globalization;
8
    using System.Net;
9
    using System.Net.Http;
10
    using System.Threading;
11
    using System.Threading.Tasks;
12
    using Microsoft.Bot.Schema.Teams;
13
    using Microsoft.Rest;
14
    using Newtonsoft.Json;
15

16
    /// <summary>
17
    /// TeamsOperations operations.
18
    /// </summary>
19
    public partial class TeamsOperations : IServiceOperations<TeamsConnectorClient>, ITeamsOperations
20
    {
21
        /// <summary>
22
        /// Initializes a new instance of the <see cref="TeamsOperations"/> class.
23
        /// </summary>
24
        /// <param name='client'>
25
        /// Reference to the service client.
26
        /// </param>
27
        /// <exception cref="System.ArgumentNullException">
28
        /// Thrown when a required parameter is null.
29
        /// </exception>
30
        public TeamsOperations(TeamsConnectorClient client)
1✔
31
        {
32
            if (client == null)
1✔
33
            {
34
                throw new System.ArgumentNullException(nameof(client));
×
35
            }
36

37
            Client = client;
1✔
38
        }
1✔
39

40
        /// <summary>
41
        /// Gets a reference to the TeamsConnectorClient.
42
        /// </summary>
43
        /// <value>The TeamsConnectorClient.</value>
44
        public TeamsConnectorClient Client { get; private set; }
1✔
45

46
        /// <summary>
47
        /// Fetches channel list for a given team.
48
        /// </summary>
49
        /// <remarks>
50
        /// Fetch the channel list.
51
        /// </remarks>
52
        /// <param name='teamId'>
53
        /// Team Id.
54
        /// </param>
55
        /// <param name='customHeaders'>
56
        /// Headers that will be added to request.
57
        /// </param>
58
        /// <param name='cancellationToken'>
59
        /// The cancellation token.
60
        /// </param>
61
        /// <exception cref="HttpOperationException">
62
        /// Thrown when the operation returned an invalid status code.
63
        /// </exception>
64
        /// <exception cref="SerializationException">
65
        /// Thrown when unable to deserialize the response.
66
        /// </exception>
67
        /// <exception cref="ValidationException">
68
        /// Thrown when an input value does not match the expected data type, range or pattern.
69
        /// </exception>
70
        /// <exception cref="System.ArgumentNullException">
71
        /// Thrown when a required parameter is null.
72
        /// </exception>
73
        /// <returns>
74
        /// A response object containing the response body and response headers.
75
        /// </returns>
76
        public async Task<HttpOperationResponse<ConversationList>> FetchChannelListWithHttpMessagesAsync(string teamId, Dictionary<string, List<string>> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken))
77
        {
78
            if (teamId == null)
1✔
79
            {
80
                throw new ValidationException(ValidationRules.CannotBeNull, "teamId");
×
81
            }
82

83
            // Tracing
84
            bool shouldTrace = ServiceClientTracing.IsEnabled;
1✔
85
            string invocationId = null;
1✔
86
            if (shouldTrace)
1✔
87
            {
88
                invocationId = ServiceClientTracing.NextInvocationId.ToString(CultureInfo.InvariantCulture);
×
89
                Dictionary<string, object> tracingParameters = new Dictionary<string, object>();
×
90
                tracingParameters.Add("teamId", teamId);
×
91
                tracingParameters.Add("cancellationToken", cancellationToken);
×
92
                ServiceClientTracing.Enter(invocationId, this, "FetchChannelList", tracingParameters);
×
93
            }
94

95
            // Construct URL
96
            var baseUrl = Client.BaseUri.AbsoluteUri;
1✔
97
            var url = new System.Uri(new System.Uri(baseUrl + (baseUrl.EndsWith("/", System.StringComparison.InvariantCulture) ? string.Empty : "/")), "v3/teams/{teamId}/conversations").ToString();
×
98
            url = url.Replace("{teamId}", System.Uri.EscapeDataString(teamId));
1✔
99

100
            return await GetResponseAsync<ConversationList>(url, shouldTrace, invocationId, cancellationToken: cancellationToken).ConfigureAwait(false);
1✔
101
        }
1✔
102

103
        /// <summary>
104
        /// Fetches details related to a team.
105
        /// </summary>
106
        /// <param name='teamId'>
107
        /// Team Id.
108
        /// </param>
109
        /// <param name='customHeaders'>
110
        /// Headers that will be added to request.
111
        /// </param>
112
        /// <param name='cancellationToken'>
113
        /// The cancellation token.
114
        /// </param>
115
        /// <exception cref="HttpOperationException">
116
        /// Thrown when the operation returned an invalid status code.
117
        /// </exception>
118
        /// <exception cref="SerializationException">
119
        /// Thrown when unable to deserialize the response.
120
        /// </exception>
121
        /// <exception cref="ValidationException">
122
        /// Thrown when an input value does not match the expected data type, range or pattern.
123
        /// </exception>
124
        /// <exception cref="System.ArgumentNullException">
125
        /// Thrown when a required parameter is null.
126
        /// </exception>
127
        /// <returns>
128
        /// A response object containing the response body and response headers.
129
        /// </returns>
130
        public async Task<HttpOperationResponse<TeamDetails>> FetchTeamDetailsWithHttpMessagesAsync(string teamId, Dictionary<string, List<string>> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken))
131
        {
132
            if (teamId == null)
1✔
133
            {
134
                throw new ValidationException(ValidationRules.CannotBeNull, "teamId");
×
135
            }
136

137
            // Tracing
138
            bool shouldTrace = ServiceClientTracing.IsEnabled;
1✔
139
            string invocationId = null;
1✔
140
            if (shouldTrace)
1✔
141
            {
142
                invocationId = ServiceClientTracing.NextInvocationId.ToString(CultureInfo.InvariantCulture);
×
143
                Dictionary<string, object> tracingParameters = new Dictionary<string, object>();
×
144
                tracingParameters.Add("teamId", teamId);
×
145
                tracingParameters.Add("cancellationToken", cancellationToken);
×
146
                ServiceClientTracing.Enter(invocationId, this, "FetchTeamDetails", tracingParameters);
×
147
            }
148

149
            // Construct URL
150
            var baseUrl = Client.BaseUri.AbsoluteUri;
1✔
151
            var url = new System.Uri(new System.Uri(baseUrl + (baseUrl.EndsWith("/", System.StringComparison.InvariantCulture) ? string.Empty : "/")), "v3/teams/{teamId}").ToString();
×
152
            url = url.Replace("{teamId}", System.Uri.EscapeDataString(teamId));
1✔
153

154
            return await GetResponseAsync<TeamDetails>(url, shouldTrace, invocationId, cancellationToken: cancellationToken).ConfigureAwait(false);
1✔
155
        }
1✔
156

157
        /// <summary>
158
        /// Fetches details related to a meeting.
159
        /// </summary>
160
        /// <param name='meetingId'>
161
        /// Meeting Id, encoded as a BASE64 string.
162
        /// </param>
163
        /// <param name='customHeaders'>
164
        /// Headers that will be added to request.
165
        /// </param>
166
        /// <param name='cancellationToken'>
167
        /// The cancellation token.
168
        /// </param>
169
        /// <exception cref="HttpOperationException">
170
        /// Thrown when the operation returned an invalid status code.
171
        /// </exception>
172
        /// <exception cref="SerializationException">
173
        /// Thrown when unable to deserialize the response.
174
        /// </exception>
175
        /// <exception cref="ValidationException">
176
        /// Thrown when an input value does not match the expected data type, range or pattern.
177
        /// </exception>
178
        /// <exception cref="System.ArgumentNullException">
179
        /// Thrown when a required parameter is null.
180
        /// </exception>
181
        /// <returns>
182
        /// A response object containing the response body and response headers.
183
        /// </returns>
184
        public async Task<HttpOperationResponse<MeetingInfo>> FetchMeetingInfoWithHttpMessagesAsync(string meetingId, Dictionary<string, List<string>> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken))
185
        {
186
            if (meetingId == null)
1✔
187
            {
188
                throw new ValidationException(ValidationRules.CannotBeNull, "meetingId");
×
189
            }
190

191
            // Tracing
192
            bool shouldTrace = ServiceClientTracing.IsEnabled;
1✔
193
            string invocationId = null;
1✔
194
            if (shouldTrace)
1✔
195
            {
196
                invocationId = ServiceClientTracing.NextInvocationId.ToString(CultureInfo.InvariantCulture);
×
197
                Dictionary<string, object> tracingParameters = new Dictionary<string, object>();
×
198
                tracingParameters.Add("meetingId", meetingId);
×
199
                tracingParameters.Add("cancellationToken", cancellationToken);
×
200
                ServiceClientTracing.Enter(invocationId, this, "FetchMeetingInfo", tracingParameters);
×
201
            }
202

203
            // Construct URL
204
            var baseUrl = Client.BaseUri.AbsoluteUri;
1✔
205
            var url = new System.Uri(new System.Uri(baseUrl + (baseUrl.EndsWith("/", System.StringComparison.InvariantCulture) ? string.Empty : "/")), "v1/meetings/{meetingId}").ToString();
×
206
            url = url.Replace("{meetingId}", System.Uri.EscapeDataString(meetingId));
1✔
207

208
            return await GetResponseAsync<MeetingInfo>(url, shouldTrace, invocationId, customHeaders, cancellationToken: cancellationToken).ConfigureAwait(false);
1✔
209
        }
1✔
210

211
        /// <summary>
212
        /// Fetches Teams meeting participant details.
213
        /// </summary>
214
        /// <remarks>
215
        /// Fetches details for a meeting particpant.
216
        /// </remarks>
217
        /// <param name='meetingId'>
218
        /// Teams meeting id.
219
        /// </param>
220
        /// <param name='participantId'>
221
        /// Teams meeting participant id.
222
        /// </param>
223
        /// <param name='tenantId'>
224
        /// Teams meeting tenant id.
225
        /// </param>
226
        /// <param name='customHeaders'>
227
        /// Headers that will be added to request.
228
        /// </param>
229
        /// <param name='cancellationToken'>
230
        /// The cancellation token.
231
        /// </param>
232
        /// <exception cref="HttpOperationException">
233
        /// Thrown when the operation returned an invalid status code.
234
        /// </exception>
235
        /// <exception cref="SerializationException">
236
        /// Thrown when unable to deserialize the response.
237
        /// </exception>
238
        /// <exception cref="ValidationException">
239
        /// Thrown when an input value does not match the expected data type, range or pattern.
240
        /// </exception>
241
        /// <exception cref="System.ArgumentNullException">
242
        /// Thrown when a required parameter is null.
243
        /// </exception>
244
        /// <returns>
245
        /// A response object containing the response body and response headers.
246
        /// </returns>
247
#pragma warning disable CA1801 // Review unused parameters - cannot change without breaking backwards compat.
248
        public async Task<HttpOperationResponse<TeamsMeetingParticipant>> FetchParticipantWithHttpMessagesAsync(string meetingId, string participantId, string tenantId, Dictionary<string, List<string>> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken))
249
#pragma warning restore CA1801 // Review unused parameters
250
        {
251
            if (meetingId == null)
1✔
252
            {
253
                throw new ValidationException(ValidationRules.CannotBeNull, nameof(meetingId));
×
254
            }
255

256
            if (participantId == null)
1✔
257
            {
258
                throw new ValidationException(ValidationRules.CannotBeNull, nameof(participantId));
×
259
            }
260

261
            if (tenantId == null)
1✔
262
            {
263
                throw new ValidationException(ValidationRules.CannotBeNull, nameof(tenantId));
×
264
            }
265

266
            // Tracing
267
            bool shouldTrace = ServiceClientTracing.IsEnabled;
1✔
268
            string invocationId = null;
1✔
269
            if (shouldTrace)
1✔
270
            {
271
                invocationId = ServiceClientTracing.NextInvocationId.ToString(CultureInfo.InvariantCulture);
×
272
                Dictionary<string, object> tracingParameters = new Dictionary<string, object>();
×
273
                tracingParameters.Add("meetingId", meetingId);
×
274
                tracingParameters.Add("participantId", participantId);
×
275
                tracingParameters.Add("tenantId", tenantId);
×
276
                tracingParameters.Add("cancellationToken", cancellationToken);
×
277
                ServiceClientTracing.Enter(invocationId, this, "GetParticipant", tracingParameters);
×
278
            }
279

280
            // Construct URL
281
            var baseUrl = Client.BaseUri.AbsoluteUri;
1✔
282
            var url = new System.Uri(new System.Uri(baseUrl + (baseUrl.EndsWith("/", System.StringComparison.InvariantCulture) ? string.Empty : "/")), "v1/meetings/{meetingId}/participants/{participantId}?tenantId={tenantId}").ToString();
×
283
            url = url.Replace("{meetingId}", System.Uri.EscapeDataString(meetingId));
1✔
284
            url = url.Replace("{participantId}", System.Uri.EscapeDataString(participantId));
1✔
285
            url = url.Replace("{tenantId}", System.Uri.EscapeDataString(tenantId));
1✔
286

287
            return await GetResponseAsync<TeamsMeetingParticipant>(url, shouldTrace, invocationId, cancellationToken: cancellationToken).ConfigureAwait(false);
1✔
288
        }
1✔
289

290
        /// <summary>
291
        /// Send a teams meeting notification.
292
        /// </summary>
293
        /// <remarks>
294
        /// Send a notification to teams meeting particpants.
295
        /// </remarks>
296
        /// <param name='meetingId'>
297
        /// Teams meeting id.
298
        /// </param>
299
        /// <param name='notification'>
300
        /// Teams notification object.
301
        /// </param>
302
        /// <param name='customHeaders'>
303
        /// Headers that will be added to request.
304
        /// </param>
305
        /// <param name='cancellationToken'>
306
        /// The cancellation token.
307
        /// </param>
308
        /// <exception cref="HttpOperationException">
309
        /// Thrown when the operation returned an invalid status code.
310
        /// </exception>
311
        /// <exception cref="SerializationException">
312
        /// Thrown when unable to deserialize the response.
313
        /// </exception>
314
        /// <exception cref="ValidationException">
315
        /// Thrown when an input value does not match the expected data type, range or pattern.
316
        /// </exception>
317
        /// <exception cref="System.ArgumentNullException">
318
        /// Thrown when a required parameter is null.
319
        /// </exception>
320
        /// <returns>
321
        /// A response object containing the response body and response headers.
322
        /// </returns>
323
        public async Task<HttpOperationResponse<TeamsMeetingNotificationRecipientFailureInfos>> SendMeetingNotificationMessageAsync(string meetingId, TeamsMeetingNotification notification, Dictionary<string, List<string>> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken))
324
        {
325
            if (meetingId == null)
1✔
326
            {
327
                throw new ValidationException(ValidationRules.CannotBeNull, nameof(meetingId));
×
328
            }
329

330
            // Tracing
331
            bool shouldTrace = ServiceClientTracing.IsEnabled;
1✔
332
            string invocationId = null;
1✔
333
            if (shouldTrace)
1✔
334
            {
335
                invocationId = ServiceClientTracing.NextInvocationId.ToString(CultureInfo.InvariantCulture);
×
336
                Dictionary<string, object> tracingParameters = new Dictionary<string, object>();
×
337
                tracingParameters.Add("meetingId", meetingId);
×
338
                tracingParameters.Add("cancellationToken", cancellationToken);
×
339
                ServiceClientTracing.Enter(invocationId, this, "SendMeetingNotification", tracingParameters);
×
340
            }
341

342
            // Construct URL
343
            var baseUrl = Client.BaseUri.AbsoluteUri;
1✔
344
            var url = new System.Uri(new System.Uri(baseUrl + (baseUrl.EndsWith("/", System.StringComparison.InvariantCulture) ? string.Empty : "/")), "v1/meetings/{meetingId}/notification").ToString();
×
345
            url = url.Replace("{meetingId}", System.Uri.EscapeDataString(meetingId));
1✔
346
            using var httpRequest = new HttpRequestMessage();
1✔
347
            httpRequest.Method = new HttpMethod("POST");
1✔
348
            httpRequest.RequestUri = new System.Uri(url);
1✔
349

350
            HttpResponseMessage httpResponse = null;
1✔
351

352
            // Create HTTP transport objects
353
#pragma warning disable CA2000 // Dispose objects before losing scope
354
            var result = new HttpOperationResponse<TeamsMeetingNotificationRecipientFailureInfos>();
1✔
355
#pragma warning restore CA2000 // Dispose objects before losing scope
356
            try
357
            {
358
                // Set Headers
359
                if (customHeaders != null)
1✔
360
                {
361
                    foreach (var header in customHeaders)
×
362
                    {
363
                        if (httpRequest.Headers.Contains(header.Key))
×
364
                        {
365
                            httpRequest.Headers.Remove(header.Key);
×
366
                        }
367

368
                        httpRequest.Headers.TryAddWithoutValidation(header.Key, header.Value);
×
369
                    }
370
                }
371

372
                // Serialize Request
373
                string requestContent = null;
1✔
374
                if (notification != null)
1✔
375
                {
376
                    requestContent = Rest.Serialization.SafeJsonConvert.SerializeObject(notification, Client.SerializationSettings);
1✔
377
                    httpRequest.Content = new StringContent(requestContent, System.Text.Encoding.UTF8);
1✔
378
                    httpRequest.Content.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json; charset=utf-8");
1✔
379
                }
380

381
                // Set Credentials
382
                if (Client.Credentials != null)
1✔
383
                {
384
                    cancellationToken.ThrowIfCancellationRequested();
1✔
385
                    await Client.Credentials.ProcessHttpRequestAsync(httpRequest, cancellationToken).ConfigureAwait(false);
1✔
386
                }
387

388
                // Send Request
389
                if (shouldTrace)
1✔
390
                {
391
                    ServiceClientTracing.SendRequest(invocationId, httpRequest);
×
392
                }
393

394
                cancellationToken.ThrowIfCancellationRequested();
1✔
395
                httpResponse = await Client.HttpClient.SendAsync(httpRequest, cancellationToken).ConfigureAwait(false);
1✔
396
                if (shouldTrace)
1✔
397
                {
398
                    ServiceClientTracing.ReceiveResponse(invocationId, httpResponse);
×
399
                }
400

401
                HttpStatusCode statusCode = httpResponse.StatusCode;
1✔
402
                cancellationToken.ThrowIfCancellationRequested();
1✔
403
                string responseContent = null;
1✔
404

405
                // Create Result
406
                result.Request = httpRequest;
1✔
407
                result.Response = httpResponse;
1✔
408

409
                if ((int)statusCode == 207)
1✔
410
                {
411
                    /* 207: if the notifications are sent only to parital number of recipients because 
412
                            the validation on some recipients’ ids failed or some recipients were not found in the roster. 
413
                        • In this case, SMBA will return the user MRIs of those failed recipients in a format that was given to a bot 
414
                            (ex: if a bot sent encrypted user MRIs, return encrypted one).
415
                     */
416

417
                    responseContent = await httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false);
1✔
418
                    try
419
                    {
420
                        result.Body = Rest.Serialization.SafeJsonConvert.DeserializeObject<TeamsMeetingNotificationRecipientFailureInfos>(responseContent, Client.DeserializationSettings);
1✔
421
                    }
1✔
422
                    catch (JsonException ex)
×
423
                    {
424
                        if (shouldTrace)
×
425
                        {
426
                            ServiceClientTracing.Error(invocationId, ex);
×
427
                        }
428

429
                        throw new SerializationException("Unable to deserialize the response.", responseContent, ex);
×
430
                    }
431
                }
432
                else if ((int)statusCode != 202)
1✔
433
                {
434
                    /* 
435
                        400: when Meeting Notification request payload validation fails. For instance, 
436
                            • Recipients: # of recipients is greater than what the API allows || all of recipients’ user ids were invalid
437
                            • Surface: 
438
                                o Surface list is empty or null 
439
                                o Surface type is invalid 
440
                                o Duplicative surface type exists in one payload
441
                        401: if the bot token is invalid 
442
                        403: if the bot is not allowed to send the notification.
443
                            In this case, the payload should contain more detail error message.
444
                            There can be many reasons: bot disabled by tenant admin, blocked during live site mitigation,
445
                            the bot does not have a correct RSC permission for a specific surface type, etc
446
                        404: if a meeting chat is not found || None of the receipients were found in the roster. 
447
                     */
448

449
                    // invalid/unexpected status code
450
                    var ex = new HttpOperationException($"Operation returned an invalid status code '{statusCode}'");
1✔
451
                    if (httpResponse.Content != null)
1✔
452
                    {
453
                        responseContent = await httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false);
1✔
454
                    }
455
                    else
456
                    {
457
                        responseContent = string.Empty;
×
458
                    }
459

460
                    ex.Request = new HttpRequestMessageWrapper(httpRequest, requestContent);
1✔
461
                    ex.Response = new HttpResponseMessageWrapper(httpResponse, responseContent);
1✔
462
                    if (shouldTrace)
1✔
463
                    {
464
                        ServiceClientTracing.Error(invocationId, ex);
×
465
                    }
466

467
                    throw ex;
1✔
468
                }
469
            }
1✔
470
            finally
471
            {
472
                if (httpResponse != null)
1✔
473
                {
474
                    httpResponse.Dispose();
1✔
475
                }
476
            }
477

478
            if (shouldTrace)
1✔
479
            {
480
                ServiceClientTracing.Exit(invocationId, result);
×
481
            }
482

483
            return result;
1✔
484
        }
1✔
485

486
        private async Task<HttpOperationResponse<T>> GetResponseAsync<T>(string url, bool shouldTrace, string invocationId, Dictionary<string, List<string>> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken))
487
        {
488
            // Create HTTP transport objects
489
            var httpRequest = new HttpRequestMessage();
1✔
490
            HttpResponseMessage httpResponse = null;
1✔
491
            httpRequest.Method = new HttpMethod("GET");
1✔
492
            httpRequest.RequestUri = new System.Uri(url);
1✔
493

494
            // Set Headers
495
            if (customHeaders != null)
1✔
496
            {
497
                foreach (var header in customHeaders)
×
498
                {
499
                    if (httpRequest.Headers.Contains(header.Key))
×
500
                    {
501
                        httpRequest.Headers.Remove(header.Key);
×
502
                    }
503

504
                    httpRequest.Headers.TryAddWithoutValidation(header.Key, header.Value);
×
505
                }
506
            }
507

508
            // Serialize Request
509
            string requestContent = null;
1✔
510

511
            // Set Credentials
512
            if (Client.Credentials != null)
1✔
513
            {
514
                cancellationToken.ThrowIfCancellationRequested();
1✔
515
                await Client.Credentials.ProcessHttpRequestAsync(httpRequest, cancellationToken).ConfigureAwait(false);
1✔
516
            }
517

518
            // Send Request
519
            if (shouldTrace)
1✔
520
            {
521
                ServiceClientTracing.SendRequest(invocationId, httpRequest);
×
522
            }
523

524
            cancellationToken.ThrowIfCancellationRequested();
1✔
525
            httpResponse = await Client.HttpClient.SendAsync(httpRequest, cancellationToken).ConfigureAwait(false);
1✔
526
            if (shouldTrace)
1✔
527
            {
528
                ServiceClientTracing.ReceiveResponse(invocationId, httpResponse);
×
529
            }
530

531
            HttpStatusCode statusCode = httpResponse.StatusCode;
1✔
532
            cancellationToken.ThrowIfCancellationRequested();
1✔
533
            string responseContent = null;
1✔
534
            if ((int)statusCode != 200)
1✔
535
            {
536
                var ex = new HttpOperationException($"Operation returned an invalid status code '{statusCode}'");
×
537
                if (httpResponse.Content != null)
×
538
                {
539
                    responseContent = await httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false);
×
540
                }
541
                else
542
                {
543
                    responseContent = string.Empty;
×
544
                }
545

546
                ex.Request = new HttpRequestMessageWrapper(httpRequest, requestContent);
×
547
                ex.Response = new HttpResponseMessageWrapper(httpResponse, responseContent);
×
548
                if (shouldTrace)
×
549
                {
550
                    ServiceClientTracing.Error(invocationId, ex);
×
551
                }
552

553
                httpRequest.Dispose();
×
554
                if (httpResponse != null)
×
555
                {
556
                    httpResponse.Dispose();
×
557
                }
558

559
                throw ex;
×
560
            }
561

562
            // Create Result
563
            var result = new HttpOperationResponse<T>();
1✔
564
            result.Request = httpRequest;
1✔
565
            result.Response = httpResponse;
1✔
566

567
            // Deserialize Response
568
            if ((int)statusCode == 200)
1✔
569
            {
570
                responseContent = await httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false);
1✔
571
                try
572
                {
573
                    result.Body = Rest.Serialization.SafeJsonConvert.DeserializeObject<T>(responseContent, Client.DeserializationSettings);
1✔
574
                }
1✔
575
                catch (JsonException ex)
×
576
                {
577
                    httpRequest.Dispose();
×
578
                    if (httpResponse != null)
×
579
                    {
580
                        httpResponse.Dispose();
×
581
                    }
582

583
                    throw new SerializationException("Unable to deserialize the response.", responseContent, ex);
×
584
                }
585
            }
586

587
            if (shouldTrace)
1✔
588
            {
589
                ServiceClientTracing.Exit(invocationId, result);
×
590
            }
591

592
            return result;
1✔
593
        }
1✔
594
    }
595
}
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

© 2025 Coveralls, Inc