private static async Task SubmitBatchContent(IBaseClient client, Dictionary <string, Func <BatchRequestStep> > requests, bool ignoreNotFound, bool ignoreRefAlreadyExists, CancellationToken token, int attemptCount = 1) { BatchRequestContent content = GraphHelper.BuildBatchRequest(requests); BatchResponseContent response = await GraphHelper.ExecuteWithRetryAndRateLimit(async() => await client.Batch.Request().PostAsync(content, token), token, content.BatchRequestSteps.Count + 1); List <GraphBatchResult> results = await GetBatchResults(await response.GetResponsesAsync(), ignoreNotFound, ignoreRefAlreadyExists, attemptCount <= MaxRetry); GraphHelper.ThrowOnExceptions(results); int retryInterval = 8 * attemptCount; Dictionary <string, Func <BatchRequestStep> > stepsToRetry = new Dictionary <string, Func <BatchRequestStep> >(); foreach (var result in results.Where(t => t.IsRetryable)) { retryInterval = Math.Max(result.RetryInterval, retryInterval); stepsToRetry.Add(result.ID, requests[result.ID]); } if (stepsToRetry.Count > 0) { logger.Info($"Sleeping for {retryInterval} before retrying after attempt {attemptCount}"); await Task.Delay(TimeSpan.FromSeconds(retryInterval), token); await GraphHelper.SubmitBatchContent(client, stepsToRetry, ignoreNotFound, ignoreRefAlreadyExists, token, ++attemptCount); } }
/// <summary> /// Make a GraphAPI batch request, optionally passing in a function to process the returned Dictionary<string, HttpResponseMessage>. This method will dispose all HttpResponseMessage /// before returning /// </summary> /// <param name="httpClient"></param> /// <param name="content"></param> /// <param name="asyncresponsehandler"></param> /// <returns></returns> public async Task <BatchResponseContent> MakeBatchRequest(HttpClient httpClient, BatchRequestContent content, Func <Dictionary <string, HttpResponseMessage>, Task> asyncresponsehandler) { //make the batch request string requesturl = Connector.Instance.RootUrl + "/$batch"; BatchResponseContent batchResponseContent = null; while (string.IsNullOrWhiteSpace(requesturl) == false) { HttpResponseMessage response = await this.PostAsync(httpClient, requesturl, content); batchResponseContent = new BatchResponseContent(response); Dictionary <string, HttpResponseMessage> responses = await batchResponseContent.GetResponsesAsync(); if (asyncresponsehandler != null) { await asyncresponsehandler(responses); } foreach (HttpResponseMessage message in responses.Values) { message.Dispose(); } requesturl = await batchResponseContent.GetNextLinkAsync(); } return(batchResponseContent); }
private async Task <(User User, Presence Presence)> GetBatchContent(CancellationToken token) { _logger.LogInformation("Getting Graph Data: Profle, Image, Presence"); try { IUserRequest userRequest = _graphServiceClient.Me.Request(); IPresenceRequest presenceRequest = _graphServiceClient.Me.Presence.Request(); BatchRequestContent batchRequestContent = new BatchRequestContent(); var userRequestId = batchRequestContent.AddBatchRequestStep(userRequest); var presenceRequestId = batchRequestContent.AddBatchRequestStep(presenceRequest); BatchResponseContent returnedResponse = await _graphServiceClient.Batch.Request().PostAsync(batchRequestContent, token).ConfigureAwait(true); User user = await returnedResponse.GetResponseByIdAsync <User>(userRequestId).ConfigureAwait(true); Presence presence = await returnedResponse.GetResponseByIdAsync <Presence>(presenceRequestId).ConfigureAwait(true); return(User : user, Presence : presence); } catch (Exception e) { _logger.LogError(e, "Error Occured Getting Batch Content from Graph Api"); throw; } }
public async Task <(User User, Presence Presence)> GetBatchContent() { try { IUserRequest userRequest = _graphServiceClient.Me.Request(); IPresenceRequest presenceRequest = _graphServiceClient.Me.Presence.Request(); BatchRequestContent batchRequestContent = new BatchRequestContent(); var userRequestId = batchRequestContent.AddBatchRequestStep(userRequest); var presenceRequestId = batchRequestContent.AddBatchRequestStep(presenceRequest); BatchResponseContent returnedResponse = await _graphServiceClient.Batch.Request().PostAsync(batchRequestContent).ConfigureAwait(true); User user = await returnedResponse.GetResponseByIdAsync <User>(userRequestId).ConfigureAwait(true); Presence presence = await returnedResponse.GetResponseByIdAsync <Presence>(presenceRequestId).ConfigureAwait(true); return(User : user, Presence : presence); } catch (Exception ex) { _logger.LogError(ex, "Error Occured in GetBatchContent MainWindow"); _diagClient.TrackException(ex); throw; } }
public async Task BatchResponseContent_GetResponseByIdAsync() { string responseJSON = "{\"responses\":" + "[{" + "\"id\": \"1\"," + "\"status\":200," + "\"headers\":{\"Cache-Control\":\"no-cache\",\"OData-Version\":\"4.0\",\"Content-Type\":\"application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false;charset=utf-8\"}," + "\"body\":{\"@odata.context\":\"https://graph.microsoft.com/v1.0/$metadata#users/$entity\",\"displayName\":\"MOD Administrator\",\"jobTitle\":null,\"id\":\"9f4fe8ea-7e6e-486e-b8f4-VkHdanfIomf\"}" + "}," + "{" + "\"id\": \"2\"," + "\"status\":409," + "\"headers\" : {\"Cache-Control\":\"no-cache\"}," + "\"body\":{\"error\": {\"code\": \"20117\",\"message\": \"An item with this name already exists in this location.\",\"innerError\":{\"request-id\": \"nothing1b13-45cd-new-92be873c5781\",\"date\": \"2019-03-22T23:17:50\"}}}" + "}]}"; HttpContent content = new StringContent(responseJSON); HttpResponseMessage httpResponseMessage = new HttpResponseMessage(HttpStatusCode.OK); httpResponseMessage.Content = content; BatchResponseContent batchResponseContent = new BatchResponseContent(httpResponseMessage); HttpResponseMessage response = await batchResponseContent.GetResponseByIdAsync("2"); Assert.NotNull(response); Assert.Equal(HttpStatusCode.Conflict, response.StatusCode); Assert.True(response.Headers.CacheControl.NoCache); }
private static async Task TestBatch(HttpClient httpClient) { Console.WriteLine("Fetching Batch"); // Create http GET request. HttpRequestMessage httpRequestMessage1 = new HttpRequestMessage(HttpMethod.Get, "https://graph.microsoft.com/v1.0/me/"); // Create http POST request. String jsonContent = "{" + "\"displayName\": \"My Notebook2\"" + "}"; HttpRequestMessage httpRequestMessage2 = new HttpRequestMessage(HttpMethod.Post, "https://graph.microsoft.com/v1.0/me/onenote/notebooks") { Content = new StringContent(jsonContent, Encoding.UTF8, "application/json") }; // Create batch request steps with request ids. BatchRequestStep requestStep1 = new BatchRequestStep("1", httpRequestMessage1, null); BatchRequestStep requestStep2 = new BatchRequestStep("2", httpRequestMessage2, new List <string> { "1" }); // Add batch request steps to BatchRequestContent. BatchRequestContent batchRequestContent = new BatchRequestContent(); batchRequestContent.AddBatchRequestStep(requestStep1); batchRequestContent.AddBatchRequestStep(requestStep2); // Send batch request with BatchRequestContent. HttpResponseMessage response = await httpClient.PostAsync("https://graph.microsoft.com/v1.0/$batch", batchRequestContent); // Handle http responses using BatchResponseContent. BatchResponseContent batchResponseContent = new BatchResponseContent(response); Dictionary <string, HttpResponseMessage> responses = await batchResponseContent.GetResponsesAsync(); HttpResponseMessage httpResponse = await batchResponseContent.GetResponseByIdAsync("1"); string responseString = await httpResponse.Content.ReadAsStringAsync(); Console.WriteLine(responseString); HttpResponseMessage httpResponse2 = await batchResponseContent.GetResponseByIdAsync("2"); responseString = await httpResponse2.Content.ReadAsStringAsync(); Console.WriteLine(responseString); Console.WriteLine(); Console.WriteLine(); Console.WriteLine(); string nextLink = await batchResponseContent.GetNextLinkAsync(); Console.WriteLine(nextLink); Console.WriteLine(); Console.WriteLine(); Console.WriteLine(); }
public async Task BatchResponseContent_InitializeWithNoContentAsync() { HttpResponseMessage httpResponseMessage = new HttpResponseMessage(HttpStatusCode.BadRequest); BatchResponseContent batchResponseContent = new BatchResponseContent(httpResponseMessage); Dictionary <string, HttpResponseMessage> responses = await batchResponseContent.GetResponsesAsync(); HttpResponseMessage httpResponse = await batchResponseContent.GetResponseByIdAsync("1"); Assert.NotNull(responses); Assert.Null(httpResponse); Assert.True(responses.Count.Equals(0)); }
public async void GetUserIds_ExpectedInput_ReturnIds() { // Arrange Response response1 = new Response(); response1.Id = 1; response1.Status = 200; response1.Body = new Body(); response1.Body.Value = new object[1] { new User() { Id = "id1" } }; Response response2 = new Response(); response2.Id = 2; response2.Status = 200; response2.Body = new Body(); response2.Body.Value = new object[1] { new User() { Id = "id2" } }; BatchResponseBody batchResponseBody = new BatchResponseBody(); batchResponseBody.Responses = new Response[] { response1, response2 }; var httpResponse = new HttpResponseMessage(HttpStatusCode.OK); httpResponse.Content = new StreamContent(new MemoryStream(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(batchResponseBody)))); BatchResponseContent batchContent = new BatchResponseContent(httpResponse); var graphClientMock = new Mock <IGraphServiceClient>(); graphClientMock.Setup(g => g.Batch.Request(It.IsAny <List <HeaderOption> >()).PostAsync(It.IsAny <BatchRequestContent>())) .ReturnsAsync(batchContent); var userService = new UsersService(graphClientMock.Object); // Act var result = await userService.GetUserIdsFromEmailAsync(_emails, _accessToken); // Assert Assert.Equal(result, new string[] { "id1", "id2" }); }
public async static Task BatchRequestExample(GraphServiceClient graphServiceClient, int limit) { var watch = new System.Diagnostics.Stopwatch(); watch.Start(); var batchRequestContent = new BatchRequestContent(); var teams = DbOperations.GetTeams(limit); // 1. construct a Batch request for (int i = 0; i < teams.Count; i++) { var requestUrl1 = graphServiceClient.Groups[teams[i].TeamId].Drive.Root .Delta() .Request().RequestUrl; var request1 = new HttpRequestMessage(HttpMethod.Get, requestUrl1); var requestStep1 = new BatchRequestStep($"{i}", request1, null); batchRequestContent.AddBatchRequestStep(requestStep1); } //3. Submit request var batchRequest = new HttpRequestMessage(HttpMethod.Post, "https://graph.microsoft.com/v1.0/$batch"); batchRequest.Content = batchRequestContent; await graphServiceClient.AuthenticationProvider.AuthenticateRequestAsync(batchRequest); var httpClient = new HttpClient(); var batchResponse = await httpClient.SendAsync(batchRequest); // 3. Process response var batchResponseContent = new BatchResponseContent(batchResponse); var responses = await batchResponseContent.GetResponsesAsync(); foreach (var response in responses) { if (response.Value.IsSuccessStatusCode) { //Console.WriteLine(); //Console.WriteLine($"response {response.Key} - {await response.Value.Content.ReadAsStringAsync()}"); //Console.WriteLine(); Console.WriteLine($"response {response.Key}"); } } watch.Stop(); Console.WriteLine($"Checking Teams completed on {watch.ElapsedMilliseconds / 1000} seconds"); }
public async Task <(User User, Presence Presence)> GetBatchContent() { IUserRequest userRequest = _graphServiceClient.Me.Request(); IPresenceRequest presenceRequest = _graphServiceClient.Me.Presence.Request(); BatchRequestContent batchRequestContent = new BatchRequestContent(); var userRequestId = batchRequestContent.AddBatchRequestStep(userRequest); var presenceRequestId = batchRequestContent.AddBatchRequestStep(presenceRequest); BatchResponseContent returnedResponse = await _graphServiceClient.Batch.Request().PostAsync(batchRequestContent).ConfigureAwait(true); User user = await returnedResponse.GetResponseByIdAsync <User>(userRequestId).ConfigureAwait(true); Presence presence = await returnedResponse.GetResponseByIdAsync <Presence>(presenceRequestId).ConfigureAwait(true); return(User : user, Presence : presence); }
public async Task BatchResponseContent_InitializeWithEmptyResponseContentAsync() { string jsonResponse = "{ responses: [] }"; HttpContent content = new StringContent(jsonResponse); HttpResponseMessage httpResponseMessage = new HttpResponseMessage(HttpStatusCode.BadRequest); httpResponseMessage.Content = content; BatchResponseContent batchResponseContent = new BatchResponseContent(httpResponseMessage); Dictionary <string, HttpResponseMessage> responses = await batchResponseContent.GetResponsesAsync(); HttpResponseMessage httpResponse = await batchResponseContent.GetResponseByIdAsync("1"); Assert.NotNull(responses); Assert.Null(httpResponse); Assert.NotNull(batchResponseContent.Serializer); Assert.True(responses.Count.Equals(0)); }
public async Task JsonBatchRequest() { string token = await GetAccessTokenUsingPasswordGrant(); HttpClient httpClient = new HttpClient(); httpClient.DefaultRequestHeaders.Add("Authorization", "Bearer " + token); HttpRequestMessage httpRequestMessage1 = new HttpRequestMessage(HttpMethod.Get, "https://graph.microsoft.com/v1.0/me/"); String body = "{" + "\"displayName\": \"My Notebook\"" + "}"; HttpRequestMessage httpRequestMessage2 = new HttpRequestMessage(HttpMethod.Post, "https://graph.microsoft.com/v1.0/me/onenote/notebooks"); httpRequestMessage2.Content = new StringContent(body, Encoding.UTF8, "application/json"); BatchRequestStep requestStep1 = new BatchRequestStep("1", httpRequestMessage1, null); BatchRequestStep requestStep2 = new BatchRequestStep("2", httpRequestMessage2, new List <string> { "1" }); BatchRequestContent batchRequestContent = new BatchRequestContent(); batchRequestContent.AddBatchRequestStep(requestStep1); batchRequestContent.AddBatchRequestStep(requestStep2); HttpResponseMessage response = await httpClient.PostAsync("https://graph.microsoft.com/v1.0/$batch", batchRequestContent); BatchResponseContent batchResponseContent = new BatchResponseContent(response); Dictionary <string, HttpResponseMessage> responses = await batchResponseContent.GetResponsesAsync(); HttpResponseMessage httpResponse = await batchResponseContent.GetResponseByIdAsync("1"); string nextLink = await batchResponseContent.GetNextLinkAsync(); Assert.True(responses.Count.Equals(2)); }
public async Task BatchResponseContent_GetResponsesAsync() { string responseJSON = "{\"responses\":" + "[{" + "\"id\": \"1\"," + "\"status\":200," + "\"headers\":{\"Cache-Control\":\"no-cache\",\"OData-Version\":\"4.0\",\"Content-Type\":\"application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false;charset=utf-8\"}," + "\"body\":{\"@odata.context\":\"https://graph.microsoft.com/v1.0/$metadata#users/$entity\",\"displayName\":\"MOD Administrator\",\"jobTitle\":null,\"id\":\"9f4fe8ea-7e6e-486e-b8f4-VkHdanfIomf\"}" + "}," + "{" + "\"id\": \"2\"," + "\"status\":200," + "\"headers\":{\"Cache-Control\":\"no-store, no-cache\",\"OData-Version\":\"4.0\",\"Content-Type\":\"application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false;charset=utf-8\"}," + "\"body\":{\"@odata.context\":\"https://graph.microsoft.com/v1.0/$metadata#drives/$entity\",\"createdDateTime\":\"2019-01-12T09:05:38Z\",\"description\":\"\",\"id\":\"b!random-VkHdanfIomf\",\"lastModifiedDateTime\":\"2019-03-06T06:59:04Z\",\"name\":\"OneDrive\",\"webUrl\":\"https://m365x751487-my.sharepoint.com/personal/admin_m365x751487_onmicrosoft_com/Documents\",\"driveType\":\"business\",\"createdBy\":{\"user\":{\"displayName\":\"System Account\"}},\"lastModifiedBy\":{\"user\":{\"displayName\":\"System Account\"}},\"owner\":{\"user\":{\"email\":\"[email protected]\",\"id\":\"6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c\",\"displayName\":\"MOD Administrator\"}},\"quota\":{\"deleted\":0,\"remaining\":1099509670098,\"state\":\"normal\",\"total\":1099511627776,\"used\":30324}}" + "}," + "{" + "\"id\": \"3\"," + "\"status\":201," + "\"headers\":{\"Location\":\"https://graph.microsoft.com/v1.0/users/9f4fe8ea-7e6e-486e-a8f4-nothing-here/onenote/notebooks/1-zyz-a1c1-441a-8b41-9378jjdd2\",\"Preference-Applied\":\"odata.include-annotations=*\",\"Cache-Control\":\"no-cache\",\"OData-Version\":\"4.0\",\"Content-Type\":\"application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false;charset=utf-8\"}," + "\"body\":{\"@odata.context\":\"https://graph.microsoft.com/v1.0/$metadata#users('9f4fe8ea-7e6e-486e-a8f4-nothing-here')/onenote/notebooks/$entity\",\"id\":\"1-9f4fe8ea-7e6e-486e-a8f4-nothing-here\",\"self\":\"https://graph.microsoft.com/v1.0/users/9f4fe8ea-7e6e-486e-a8f4-nothing-here/onenote/notebooks/1-9f4fe8ea-7e6e-486e-a8f4-nothing-here\",\"createdDateTime\":\"2019-03-06T08:08:09Z\",\"displayName\":\"My Notebook -442293399\",\"lastModifiedDateTime\":\"2019-03-06T08:08:09Z\"}" + "}]}"; HttpContent content = new StringContent(responseJSON); HttpResponseMessage httpResponseMessage = new HttpResponseMessage(HttpStatusCode.OK); httpResponseMessage.Content = content; BatchResponseContent batchResponseContent = new BatchResponseContent(httpResponseMessage); Dictionary <string, HttpResponseMessage> responses = await batchResponseContent.GetResponsesAsync(); Assert.NotNull(responses); Assert.True(responses.Count.Equals(3)); Assert.False(responses["1"].Headers.CacheControl.NoStore); Assert.True(responses["2"].Headers.CacheControl.NoCache); Assert.True(responses["2"].Headers.CacheControl.NoStore); Assert.Equal(HttpStatusCode.Created, responses["3"].StatusCode); }
public async Task <IList <GraphProfileInformation> > GetAzureADObjectIdsAsync(IList <string> personnelNumbers, Guid?runId) { if (personnelNumbers.Count == 0) { return(new List <GraphProfileInformation>()); } HttpStatusCode[] httpsStatusCodesWithRetryAfterHeader = { HttpStatusCode.TooManyRequests, // 429 HttpStatusCode.ServiceUnavailable // 503 }; var retryAfterPolicy = Policy .HandleResult <HttpResponseMessage>(result => httpsStatusCodesWithRetryAfterHeader.Contains(result.StatusCode) && result.Headers?.RetryAfter != null) .WaitAndRetryAsync( _maxGraphServiceAttempts.MaxRetryAfterAttempts, sleepDurationProvider: GetSleepDuration, onRetryAsync: async(response, timeSpan, retryCount, context) => { await _loggingRepository.LogMessageAsync(new LogMessage { Message = $"Throttled by Graph for the timespan: {timeSpan}. The retry count is {retryCount}.", RunId = runId }); }); HttpStatusCode[] httpStatusCodesWorthRetryingExponentially = { HttpStatusCode.InternalServerError, // 500 HttpStatusCode.BadGateway, // 502 HttpStatusCode.ServiceUnavailable, // 503 HttpStatusCode.GatewayTimeout // 504 }; var exceptionHandlingPolicy = Policy .Handle <HttpRequestException>() .OrResult <HttpResponseMessage>(r => httpStatusCodesWorthRetryingExponentially.Contains(r.StatusCode)) .WaitAndRetryAsync( _maxGraphServiceAttempts.MaxExceptionHandlingAttempts, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), onRetryAsync: async(timeSpan, retryCount, context) => { await _loggingRepository.LogMessageAsync(new LogMessage { Message = $"Exponential backoff {retryCount}.", RunId = runId }); } ); var graphMemberIdPolicy = retryAfterPolicy.WrapAsync(exceptionHandlingPolicy); IList <string> unprocessedPersonnelNumbers = new List <string>(); var profiles = new List <GraphProfileInformation>(); if (_cache.Count > 0) { foreach (var personnelNumber in personnelNumbers) { if (_cache.ContainsKey(personnelNumber)) { profiles.Add(_cache[personnelNumber]); } else { unprocessedPersonnelNumbers.Add(personnelNumber); } } } else { unprocessedPersonnelNumbers = personnelNumbers; } await _loggingRepository.LogMessageAsync(new LogMessage { Message = $"{unprocessedPersonnelNumbers.Count} out of {personnelNumbers.Count} need to be retrieved from graph.", RunId = runId }); await _loggingRepository.LogMessageAsync(new LogMessage { Message = $"{_cache.Keys.Count} profiles exist in the cache.", RunId = runId }); var fields = string.Join(",", new[] { IdFieldName, PersonnelNumberFieldName, UserPrincipalNameFieldName }); // Graph currently limits batches to 10 requests per batch // Graph currently limits the number of conditions in a $filter expression to 20 var requestsCreated = 0; var pnQueue = new Queue <string>(unprocessedPersonnelNumbers); var totalBatches = Math.Ceiling(unprocessedPersonnelNumbers.Count / BATCH_REQUEST_LIMIT / FILTER_CONDITION_LIMIT * 1d); var batchCount = 0; var jobTimer = new Stopwatch(); var batchTimer = new Stopwatch(); var batchTimes = new List <TimeSpan>(); jobTimer.Start(); while (pnQueue.Count > 0) { using (var batchRequestContent = new BatchRequestContent()) { int limit = pnQueue.Count >= FILTER_CONDITION_LIMIT ? 10 : pnQueue.Count; // add request steps while (batchRequestContent.BatchRequestSteps.Count < BATCH_REQUEST_LIMIT && pnQueue.Count != 0) { var requestPersonnelNumbers = new List <string>(); limit = pnQueue.Count >= FILTER_CONDITION_LIMIT ? 10 : pnQueue.Count; for (var i = 0; i < limit; i++) { requestPersonnelNumbers.Add(pnQueue.Dequeue()); } // build filter expression var filter = $"(onPremisesImmutableId eq '{string.Join("' or onPremisesImmutableId eq '", requestPersonnelNumbers.ToArray())}')"; var requestMessage = _graphClient.Users.Request().Filter(filter).Select(fields).WithPerRequestAuthProvider().GetHttpRequestMessage(); requestsCreated++; batchRequestContent.AddBatchRequestStep(new BatchRequestStep($"{requestsCreated}", requestMessage, null)); } // every 20 requests send a batch or if there are no more requests to make batchCount++; batchTimer.Start(); HttpResponseMessage httpResponse = null; await graphMemberIdPolicy.ExecuteAsync(async() => { var batchRequest = new HttpRequestMessage(HttpMethod.Post, "https://graph.microsoft.com/v1.0/$batch") { Content = batchRequestContent }; await _graphClient.AuthenticationProvider.AuthenticateRequestAsync(batchRequest); httpResponse = await _graphClient.HttpProvider.SendAsync(batchRequest); await _loggingRepository.LogMessageAsync(new LogMessage { Message = $"Graph Response:" + $"\nStatusCode {httpResponse.StatusCode}", RunId = runId }); return(httpResponse); }); var batchResponse = new BatchResponseContent(httpResponse); // process each request in the batch foreach (var response in await batchResponse.GetResponsesAsync()) { // request was successful if (response.Value.IsSuccessStatusCode) { var content = await response.Value.Content.ReadAsStringAsync(); var oDataResponse = JsonConvert.DeserializeObject <ODataResponse <List <User> > >(content); // process each user foreach (var user in oDataResponse.Value) { var profile = new GraphProfileInformation { Id = user.Id, PersonnelNumber = user.OnPremisesImmutableId, UserPrincipalName = user.UserPrincipalName }; profiles.Add(profile); _cache.Add(profile.PersonnelNumber, profile); } } else { await _loggingRepository.LogMessageAsync(new LogMessage { Message = $"Graph Request failures:" + $"\nStatusCode {response.Value.StatusCode}" + $"\nReasonPhrase {response.Value.ReasonPhrase}" + $"\nRequestURI {batchRequestContent.BatchRequestSteps[response.Key].Request.RequestUri}", RunId = runId }); } } batchTimer.Stop(); batchTimes.Add(batchTimer.Elapsed); await _loggingRepository.LogMessageAsync(new LogMessage { Message = $"Graph Request: {batchCount} of {totalBatches}{Environment.NewLine}" + $" Batch Time Elapsed: {batchTimer.ElapsedMilliseconds} ms{Environment.NewLine}" + $" Total Time Elapsed: {jobTimer.Elapsed}{Environment.NewLine}" + $" Total Profile Count: {profiles.Count}{Environment.NewLine}" + $" Total Users Not Found: {(personnelNumbers.Count - pnQueue.Count) - profiles.Count}{Environment.NewLine}" + $" Total Queue Remaining: {pnQueue.Count}{Environment.NewLine}", RunId = runId }); batchTimer.Reset(); } } jobTimer.Stop(); return(profiles); }
public async Task PostAsyncReturnsBatchResponseContent() { using (HttpResponseMessage responseMessage = new HttpResponseMessage(HttpStatusCode.OK)) using (TestHttpMessageHandler testHttpMessageHandler = new TestHttpMessageHandler()) { /* Arrange */ // 1. create a mock response string requestUrl = "https://localhost/"; string responseJSON = "{\"responses\":" + "[{" + "\"id\": \"1\"," + "\"status\":200," + "\"headers\":{\"Cache-Control\":\"no-cache\",\"OData-Version\":\"4.0\",\"Content-Type\":\"application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false;charset=utf-8\"}," + "\"body\":{\"@odata.context\":\"https://graph.microsoft.com/v1.0/$metadata#users/$entity\",\"displayName\":\"MOD Administrator\",\"jobTitle\":null,\"id\":\"9f4fe8ea-7e6e-486e-b8f4-VkHdanfIomf\"}" + "}," + "{" + "\"id\": \"2\"," + "\"status\":409," + "\"headers\" : {\"Cache-Control\":\"no-cache\"}," + "\"body\":{\"error\": {\"code\": \"20117\",\"message\": \"An item with this name already exists in this location.\",\"innerError\":{\"request-id\": \"nothing1b13-45cd-new-92be873c5781\",\"date\": \"2019-03-22T23:17:50\"}}}" + "}]}"; HttpContent content = new StringContent(responseJSON); responseMessage.Content = content; // 2. Map the response testHttpMessageHandler.AddResponseMapping(requestUrl, responseMessage); // 3. Create a batch request object to be tested MockCustomHttpProvider customHttpProvider = new MockCustomHttpProvider(testHttpMessageHandler); BaseClient client = new BaseClient(requestUrl, authenticationProvider.Object, customHttpProvider); BatchRequest batchRequest = new BatchRequest(requestUrl, client); // 4. Create batch request content to be sent out // 4.1 Create HttpRequestMessages for the content HttpRequestMessage httpRequestMessage1 = new HttpRequestMessage(HttpMethod.Get, "https://graph.microsoft.com/v1.0/me/"); HttpRequestMessage httpRequestMessage2 = new HttpRequestMessage(HttpMethod.Post, "https://graph.microsoft.com/v1.0/me/onenote/notebooks"); // 4.2 Create batch request steps with request ids. BatchRequestStep requestStep1 = new BatchRequestStep("1", httpRequestMessage1); BatchRequestStep requestStep2 = new BatchRequestStep("2", httpRequestMessage2, new List <string> { "1" }); // 4.3 Add batch request steps to BatchRequestContent. BatchRequestContent batchRequestContent = new BatchRequestContent(requestStep1, requestStep2); /* Act */ BatchResponseContent returnedResponse = await batchRequest.PostAsync(batchRequestContent); HttpResponseMessage firstResponse = await returnedResponse.GetResponseByIdAsync("1"); HttpResponseMessage secondResponse = await returnedResponse.GetResponseByIdAsync("2"); /* Assert */ // validate the first response Assert.NotNull(firstResponse); Assert.Equal(HttpStatusCode.OK, firstResponse.StatusCode); Assert.True(firstResponse.Headers.CacheControl.NoCache); Assert.NotNull(firstResponse.Content); // validate the second response Assert.NotNull(secondResponse); Assert.Equal(HttpStatusCode.Conflict, secondResponse.StatusCode); Assert.True(secondResponse.Headers.CacheControl.NoCache); Assert.NotNull(secondResponse.Content); } }
private static async Task AddEventsInBatch(IConfigurationRoot config, GraphServiceClient client) { var events = new List <Event>(); int maxNoBatchItems = 20; for (int i = 0; i < maxNoBatchItems * 3; i++) { var @event = new Event { Subject = "Subject" + i, Body = new ItemBody { ContentType = BodyType.Html, Content = "Content" + i }, Start = new DateTimeTimeZone { DateTime = DateTime.UtcNow.AddHours(i).ToString("yyyy-MM-ddTHH:mm:ss", CultureInfo.InvariantCulture), TimeZone = TimeZoneInfo.Utc.Id }, End = new DateTimeTimeZone { DateTime = DateTime.UtcNow.AddHours(i).AddMinutes(30).ToString("yyyy-MM-ddTHH:mm:ss", CultureInfo.InvariantCulture), TimeZone = TimeZoneInfo.Utc.Id }, Location = new Location { DisplayName = "Dummy location" } }; events.Add(@event); } Console.WriteLine("Creating batches..."); List <BatchRequestContent> batches = new List <BatchRequestContent>(); var batchRequestContent = new BatchRequestContent(); foreach (Event e in events) { var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, $"https://graph.microsoft.com/v1.0/users/{config["CalendarEmail"]}/events") { Content = new StringContent(JsonConvert.SerializeObject(e), Encoding.UTF8, "application/json") }; BatchRequestStep requestStep = new BatchRequestStep(events.IndexOf(e).ToString(), httpRequestMessage, null); batchRequestContent.AddBatchRequestStep(requestStep); // Max number of 20 request per batch. So we need to send out multiple batches. if (events.IndexOf(e) > 0 && events.IndexOf(e) % maxNoBatchItems == 0) { batches.Add(batchRequestContent); batchRequestContent = new BatchRequestContent(); } } if (batchRequestContent.BatchRequestSteps.Count < maxNoBatchItems) { batches.Add(batchRequestContent); } if (batches.Count == 0 && batchRequestContent != null) { batches.Add(batchRequestContent); } Console.WriteLine("Batches created. Press enter to submit them."); Console.ReadLine(); Console.WriteLine("Submitting batches..."); List <string> createdEvents = new List <string>(); foreach (BatchRequestContent batch in batches) { BatchResponseContent response = null; try { response = await client.Batch.Request().PostAsync(batch); } catch (Microsoft.Graph.ClientException ex) { Console.WriteLine(ex.Message); } Dictionary <string, HttpResponseMessage> responses = await response.GetResponsesAsync(); foreach (string key in responses.Keys) { HttpResponseMessage httpResponse = await response.GetResponseByIdAsync(key); var responseContent = await httpResponse.Content.ReadAsStringAsync(); JObject eventResponse = JObject.Parse(responseContent); var eventId = (string)eventResponse["id"]; if (eventId != null) { createdEvents.Add(eventId); } Console.WriteLine($"Response code: {responses[key].StatusCode}-{responses[key].ReasonPhrase}-{eventId}"); } } Console.WriteLine($"{events.Count} events created. Press enter to remove them from the calendar."); Console.ReadLine(); Console.WriteLine($"Removing {createdEvents.Count} events..."); foreach (string eventId in createdEvents) { if (eventId != null) { await client.Users[config["CalendarEmail"]].Events[eventId] .Request() .DeleteAsync(); } } Console.WriteLine($"{createdEvents.Count} events where removed from calendar."); }
private async Task ParseBatchResponseAsync(IGraphServiceClient client, BatchRequestContent batchRequestContent, BatchResponseContent batchResponseContent, List <SignIn> signIns, int retryCount = 0) { AuditLogRootRestrictedSignInsCollectionResponse collection; Dictionary <string, HttpResponseMessage> responses; client.AssertNotNull(nameof(client)); batchRequestContent.AssertNotNull(nameof(batchRequestContent)); batchResponseContent.AssertNotNull(nameof(batchResponseContent)); signIns.AssertNotNull(nameof(signIns)); responses = await batchResponseContent.GetResponsesAsync().ConfigureAwait(false); foreach (KeyValuePair <string, HttpResponseMessage> item in responses.Where(item => item.Value.IsSuccessStatusCode)) { collection = await batchResponseContent .GetResponseByIdAsync <AuditLogRootRestrictedSignInsCollectionResponse>(item.Key).ConfigureAwait(false); collection.AdditionalData.TryGetValue("@odata.nextLink", out object nextPageLink); string nextPageLinkString = nextPageLink as string; if (!string.IsNullOrEmpty(nextPageLinkString)) { collection.Value.InitializeNextPageRequest(client, nextPageLinkString); } if (collection.Value.NextPageRequest != null) { PagedRequests.Enqueue(collection.Value.NextPageRequest); } signIns.AddRange(collection.Value); } if (PagedRequests.Count >= 5) { await ProcessPagedRequestsAsync(client, signIns).ConfigureAwait(false); } await RetryRequestWithTransientFaultAsync(client, batchRequestContent, responses, signIns, ++retryCount).ConfigureAwait(false); }
public async static Task WatchTeamSite(IList <string> keys) { try { //var watch = new System.Diagnostics.Stopwatch(); //watch.Start(); var batchRequestContent = new BatchRequestContent(); // 1. construct a Batch request foreach (var key in keys) { if (teamSitesDeltaLinks[key] == null) { teamSitesDeltaLinks[key] = new DeltaLinks() { LastSyncDate = DateTime.UtcNow.Ticks / 100000000, DeltaLink = graphClient.Groups[key].Drive.Root .Delta() .Request() .RequestUrl + "?$select=CreatedDateTime,Deleted,File,Folder,LastModifiedDateTime,Root,SharepointIds,Size,WebUrl" } } ; var request = new HttpRequestMessage(HttpMethod.Get, teamSitesDeltaLinks[key].DeltaLink); var requestStep = new BatchRequestStep($"{key}", request, null); batchRequestContent.AddBatchRequestStep(requestStep); } //3. Submit request var batchRequest = new HttpRequestMessage(HttpMethod.Post, "https://graph.microsoft.com/v1.0/$batch"); batchRequest.Content = batchRequestContent; await graphClient.AuthenticationProvider.AuthenticateRequestAsync(batchRequest); var httpClient = new HttpClient(); var batchResponse = await httpClient.SendAsync(batchRequest); // 3. Process response var batchResponseContent = new BatchResponseContent(batchResponse); var responses = await batchResponseContent.GetResponsesAsync(); foreach (var response in responses) { if (!response.Value.IsSuccessStatusCode) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine($"Issue on response {response.Value}"); Console.ResetColor(); continue; } //Console.WriteLine(response.Key); var deltaResponse = JsonConvert.DeserializeObject <DeltaResponse> (await response.Value.Content.ReadAsStringAsync()); teamSitesDeltaLinks[response.Key].DeltaLink = deltaResponse.DeltaLink; if (!firstCall) { foreach (var drive in deltaResponse.DriveItems) { await ProcessChangesAsync(drive, teamSitesDeltaLinks[response.Key].LastSyncDate); } } } //watch.Stop(); //Console.WriteLine($"Checking Teams completed on {watch.ElapsedMilliseconds / 1000} seconds"); libraryDeltaCalls++; } catch (Exception exc) { AddException(exc, "WatchTeamSite"); if (exc.InnerException.Message.Contains("Authentication failed")) { Console.WriteLine($"retry again due {exc.InnerException.Message}"); await Task.Delay(500); await WatchTeamSite(keys); } else { Console.WriteLine(exc.Message); Console.WriteLine(exc.InnerException.Message); Console.WriteLine(exc.StackTrace); } } }
public RemoteGraphServiceTests() { _logger = new Mock <ILogger <RemoteMicrosoftGraphService> >(); _graphServiceClient = new Mock <IGraphServiceClient>(); _remoteGraphService = new RemoteMicrosoftGraphService(_graphServiceClient.Object, _logger.Object); var inMemoryGraphService = new InMemoryGraphService(); _user = inMemoryGraphService.GetUserAsync(Guid.NewGuid().ToString()).Result; _users = inMemoryGraphService.FindActiveUserAsync(string.Empty).Result; _group = inMemoryGraphService.GetGroupAsync(Guid.NewGuid().ToString()).Result; _groups = inMemoryGraphService.FindGroupAsync(string.Empty).Result; _managerId = Guid.NewGuid().ToString(); _mockBatchRequest = new Mock <IBatchRequest>(); var httpResponseMessage = new HttpResponseMessage(); httpResponseMessage.StatusCode = HttpStatusCode.NotFound; httpResponseMessage.ReasonPhrase = "Not Found"; var batchResponseContent = new BatchResponseContent(httpResponseMessage); _mockBatchRequest.Setup(x => x.PostAsync(It.IsAny <BatchRequestContent>())) .ReturnsAsync(batchResponseContent); _mockBatchRequestBuilder = new Mock <IBatchRequestBuilder>(); _mockBatchRequestBuilder.Setup(x => x.Request()).Returns(_mockBatchRequest.Object); _mockBaseClient = new Mock <IBaseClient>(); _mockBaseClient.Setup(x => x.Batch).Returns(_mockBatchRequestBuilder.Object); _mockUserRequest = new Mock <IUserRequest>(); _mockUserRequest.Setup(x => x.Select(It.IsAny <Expression <Func <User, object> > >())).Returns(_mockUserRequest.Object); _mockUserRequest.Setup(x => x.GetAsync()).ReturnsAsync(_user); _mockUserRequest.Setup(x => x.GetHttpRequestMessage()).Returns(new HttpRequestMessage(new HttpMethod("GET"), new Uri("http://localhost/"))); _mockReferenceRequest = new Mock <IDirectoryObjectWithReferenceRequest>(); _mockReferenceRequest.Setup(x => x.Select(It.IsAny <Expression <Func <DirectoryObject, object> > >())).Returns(_mockReferenceRequest.Object); _mockReferenceRequest.Setup(x => x.GetAsync()).Returns(Task.FromResult(new DirectoryObject() { Id = _managerId })); _mockReferenceRequestBuilder = new Mock <IDirectoryObjectWithReferenceRequestBuilder>(); _mockReferenceRequestBuilder.Setup(x => x.Request()).Returns(_mockReferenceRequest.Object); _mockUserRequestBuilder = new Mock <IUserRequestBuilder>(); _mockUserRequestBuilder.Setup(x => x.Request()).Returns(_mockUserRequest.Object); _mockUserRequestBuilder.Setup(x => x.Manager).Returns(_mockReferenceRequestBuilder.Object); _mockUsersCollectionRequest = new Mock <IGraphServiceUsersCollectionRequest>(); _mockUsersCollectionRequest.Setup(x => x.Select(It.IsAny <Expression <Func <User, object> > >())).Returns(_mockUsersCollectionRequest.Object); _mockUsersCollectionRequest.Setup(x => x.Filter(It.IsAny <string>())).Returns(_mockUsersCollectionRequest.Object); _mockUsersCollectionRequest.Setup(x => x.GetAsync()).Returns(Task.FromResult(GetUsersCollectionPage())); _mockUsersCollectionRequestBuilder = new Mock <IGraphServiceUsersCollectionRequestBuilder>(); _mockUsersCollectionRequestBuilder.Setup(x => x.Request()).Returns(_mockUsersCollectionRequest.Object); _mockGroupsCollectionRequest = new Mock <IGraphServiceGroupsCollectionRequest>(); _mockGroupsCollectionRequest.Setup(x => x.Select(It.IsAny <Expression <Func <Group, object> > >())).Returns(_mockGroupsCollectionRequest.Object); _mockGroupsCollectionRequest.Setup(x => x.Filter(It.IsAny <string>())).Returns(_mockGroupsCollectionRequest.Object); _mockGroupsCollectionRequest.Setup(x => x.GetAsync()).Returns(Task.FromResult(GetGroupsCollectionPage())); _mockGroupsCollectionRequestBuilder = new Mock <IGraphServiceGroupsCollectionRequestBuilder>(); _mockGroupsCollectionRequestBuilder.Setup(x => x.Request()).Returns(_mockGroupsCollectionRequest.Object); _graphServiceClient.Setup(x => x.Users) .Returns(_mockUsersCollectionRequestBuilder.Object); _graphServiceClient.Setup(x => x.Users[It.IsAny <string>()]) .Returns(_mockUserRequestBuilder.Object); _graphServiceClient.Setup(x => x.Groups) .Returns(_mockGroupsCollectionRequestBuilder.Object); _graphServiceClient.Setup(x => x.Batch) .Returns(_mockBatchRequestBuilder.Object); }
public async Task BatchResponseContent_GetResponseByIdAsyncWithDeserializerWorksWithDateTimeOffsets() { // Arrange an example Event object with a few properties string responseJSON = "\n{\n" + " \"responses\": [\n" + " {\n" + " \"id\": \"3\",\n" + " \"status\": 200,\n" + " \"headers\": {\n" + " \"Cache-Control\": \"private\",\n" + " \"OData-Version\": \"4.0\",\n" + " \"Content-Type\": \"application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false;charset=utf-8\",\n" + " \"ETag\": \"W/\\\"h8TLt1Vki0W7hBZaqTqGTQAAQyxv+g==\\\"\"\n" + " },\n" + " \"body\": {\n" + " \"@odata.context\": \"https://graph.microsoft.com/v1.0/$metadata#users('d9f7c4f6-e1bb-4032-a86d-6e84722b983d')/events/$entity\",\n" + " \"@odata.etag\": \"W/\\\"h8TLt1Vki0W7hBZaqTqGTQAAQyxv+g==\\\"\",\n" + " \"id\": \"AQMkADcyMWRhMWZmAC0xZTI1LTRjZjEtYTRjMC04M\",\n" + " \"categories\": [],\n" + " \"originalStartTimeZone\": \"Pacific Standard Time\",\n" + " \"originalEndTimeZone\": \"Pacific Standard Time\",\n" + " \"iCalUId\": \"040000008200E00074C5B7101A82E0080000000053373A40E03ED5010000000000000000100000007C41056410E97C44B2A34798E719B862\",\n" + " \"reminderMinutesBeforeStart\": 15,\n" + " \"type\": \"singleInstance\",\n" + " \"webLink\": \"https://outlook.office365.com/owa/?itemid=AQMkADcyMWRhMWZmAC0xZTI1LTRjZjEtYTRjMC04MGY3OGEzNThiZDAARgAAA1AZwxLGN%2FJIv2Mj%2F0o8JqYHAIfEy7dVZItFu4QWWqk6hk0AAAIBDQAAAIfEy7dVZItFu4QWWqk6hk0AAAI4eQAAAA%3D%3D&exvsurl=1&path=/calendar/item\",\n" + " \"onlineMeetingUrl\": null,\n" + " \"recurrence\": null,\n" + " \"responseStatus\": {\n" + " \"response\": \"notResponded\",\n" + " \"time\": \"0001-01-01T00:00:00Z\"\n" + " },\n" + " \"body\": {\n" + " \"contentType\": \"html\",\n" + " \"content\": \"<html>\\r\\n<head>\\r\\n<meta http-\",\n" + " },\n" + " \"start\": {\n" + " \"dateTime\": \"2019-07-30T22:00:00.0000000\",\n" + " \"timeZone\": \"UTC\"\n" + " },\n" + " \"end\": {\n" + " \"dateTime\": \"2019-07-30T23:00:00.0000000\",\n" + " \"timeZone\": \"UTC\"\n" + " }" + " }\n" + " }\n" + " ]\n" + "}"; HttpContent content = new StringContent(responseJSON); HttpResponseMessage httpResponseMessage = new HttpResponseMessage(HttpStatusCode.OK) { Content = content }; BatchResponseContent batchResponseContent = new BatchResponseContent(httpResponseMessage); // Act Event eventItem = await batchResponseContent.GetResponseByIdAsync <Event>("3"); // Assert we have a valid datetime in the event Assert.Equal("2019-07-30T23:00:00.0000000", eventItem.End.DateTime); Assert.Equal("UTC", eventItem.End.TimeZone); Assert.Equal("2019-07-30T22:00:00.0000000", eventItem.Start.DateTime); Assert.Equal("UTC", eventItem.Start.TimeZone); }
public async Task BatchResponseContent_GetResponseByIdAsyncWithDeseirializer() { // Arrange string responseJSON = "{\"responses\":" + "[{" + "\"id\": \"1\"," + "\"status\":200," + "\"headers\":{\"Cache-Control\":\"no-cache\",\"OData-Version\":\"4.0\",\"Content-Type\":\"application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false;charset=utf-8\"}," + "\"body\":{\"@odata.context\":\"https://graph.microsoft.com/v1.0/$metadata#users/$entity\",\"displayName\":\"MOD Administrator\",\"jobTitle\":null,\"id\":\"9f4fe8ea-7e6e-486e-b8f4-VkHdanfIomf\"}" + "}," + "{" + "\"id\": \"2\"," + "\"status\":200," + "\"headers\":{\"Cache-Control\":\"no-store, no-cache\",\"OData-Version\":\"4.0\",\"Content-Type\":\"application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false;charset=utf-8\"}," + "\"body\":{\"@odata.context\":\"https://graph.microsoft.com/v1.0/$metadata#drives/$entity\",\"createdDateTime\":\"2019-01-12T09:05:38Z\",\"description\":\"\",\"id\":\"b!random-VkHdanfIomf\",\"lastModifiedDateTime\":\"2019-03-06T06:59:04Z\",\"name\":\"OneDrive\",\"webUrl\":\"https://m365x751487-my.sharepoint.com/personal/admin_m365x751487_onmicrosoft_com/Documents\",\"driveType\":\"business\",\"createdBy\":{\"user\":{\"displayName\":\"System Account\"}},\"lastModifiedBy\":{\"user\":{\"displayName\":\"System Account\"}},\"owner\":{\"user\":{\"email\":\"[email protected]\",\"id\":\"6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c\",\"displayName\":\"MOD Administrator\"}},\"quota\":{\"deleted\":0,\"remaining\":1099509670098,\"state\":\"normal\",\"total\":1099511627776,\"used\":30324}}" + "}," + "{" + "\"id\": \"3\"," + "\"status\":201," + "\"headers\":{\"Location\":\"https://graph.microsoft.com/v1.0/users/9f4fe8ea-7e6e-486e-a8f4-nothing-here/onenote/notebooks/1-zyz-a1c1-441a-8b41-9378jjdd2\",\"Preference-Applied\":\"odata.include-annotations=*\",\"Cache-Control\":\"no-cache\",\"OData-Version\":\"4.0\",\"Content-Type\":\"application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false;charset=utf-8\"}," + "\"body\":{\"@odata.context\":\"https://graph.microsoft.com/v1.0/$metadata#users('9f4fe8ea-7e6e-486e-a8f4-nothing-here')/onenote/notebooks/$entity\",\"id\":\"1-9f4fe8ea-7e6e-486e-a8f4-nothing-here\",\"self\":\"https://graph.microsoft.com/v1.0/users/9f4fe8ea-7e6e-486e-a8f4-nothing-here/onenote/notebooks/1-9f4fe8ea-7e6e-486e-a8f4-nothing-here\",\"createdDateTime\":\"2019-03-06T08:08:09Z\",\"displayName\":\"My Notebook -442293399\",\"lastModifiedDateTime\":\"2019-03-06T08:08:09Z\"}" + "}," + "{" + "\"id\": \"4\"," + "\"status\":409," + "\"headers\" : {\"Cache-Control\":\"no-cache\"}," + "\"body\":{\"error\": {\"code\": \"20117\",\"message\": \"An item with this name already exists in this location.\",\"innerError\":{\"request-id\": \"nothing1b13-45cd-new-92be873c5781\",\"date\": \"2019-03-22T23:17:50\"}}}" + "}" + "]}"; HttpContent content = new StringContent(responseJSON); HttpResponseMessage httpResponseMessage = new HttpResponseMessage(HttpStatusCode.OK); httpResponseMessage.Content = content; BatchResponseContent batchResponseContent = new BatchResponseContent(httpResponseMessage); // Act User user = await batchResponseContent.GetResponseByIdAsync <User>("1"); // Assert we have a valid user Assert.Equal("MOD Administrator", user.DisplayName); // Act Drive drive = await batchResponseContent.GetResponseByIdAsync <Drive>("2"); // Assert we have a valid drive object Assert.Equal("b!random-VkHdanfIomf", drive.Id); Assert.Equal("OneDrive", drive.Name); // Act Notebook notebook = await batchResponseContent.GetResponseByIdAsync <Notebook>("3"); // Assert we have a valid notebook object Assert.Equal("1-9f4fe8ea-7e6e-486e-a8f4-nothing-here", notebook.Id); Assert.Equal("My Notebook -442293399", notebook.DisplayName); // Act ServiceException serviceException = await Assert.ThrowsAsync <ServiceException>(() => batchResponseContent.GetResponseByIdAsync <DriveItem>("4")); // Assert we detect the incorrect response and give usable Service Exception Assert.Equal("20117", serviceException.Error.Code); Assert.Equal(HttpStatusCode.Conflict, serviceException.StatusCode);//status 409 Assert.NotNull(serviceException.RawResponseBody); }