예제 #1
0
        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);
            }
        }
예제 #2
0
        /// <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);
        }
예제 #3
0
        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;
            }
        }
예제 #4
0
        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);
        }
예제 #6
0
        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));
        }
예제 #8
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" });
        }
예제 #9
0
        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");
        }
예제 #10
0
        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));
        }
예제 #12
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);
        }
예제 #15
0
        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);
                }
        }
예제 #16
0
        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);
        }
예제 #18
0
        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);
                }
            }
        }
예제 #19
0
        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);
        }