diff --git a/src/Microsoft.Graph.Core/Requests/Content/BatchRequestContentCollection.cs b/src/Microsoft.Graph.Core/Requests/Content/BatchRequestContentCollection.cs index e7f441790..23f70bb37 100644 --- a/src/Microsoft.Graph.Core/Requests/Content/BatchRequestContentCollection.cs +++ b/src/Microsoft.Graph.Core/Requests/Content/BatchRequestContentCollection.cs @@ -183,10 +183,69 @@ public BatchRequestContentCollection NewBatchWithFailedRequests(Dictionary + /// Creates a new with all that failed. + /// + /// A dictionary with response codes, get by executing batchResponseContentCollection.GetResponsesStatusCodesAsync() + /// Optional additional HTTP status codes to also treat as successful. Success is determined by OR membership in this set + /// new with all failed requests. + public BatchRequestContentCollection NewBatchWithFailedRequests(Dictionary responseStatusCodes, IEnumerable statusCodesToTreatAsSuccess) + { + var request = new BatchRequestContentCollection(this.requestAdapter, batchRequestLimit); + if (responseStatusCodes == null || responseStatusCodes.Count == 0) + { + return request; + } + + HashSet successSet = statusCodesToTreatAsSuccess != null + ? [.. statusCodesToTreatAsSuccess] + : null; + + bool IsSuccess(HttpStatusCode code) + { + if (BatchResponseContent.IsSuccessStatusCode(code)) + { + return true; + } + + if (successSet != null && successSet.Contains(code)) + { + return true; + } + + return false; + } + + var steps = this.BatchRequestSteps; + foreach (var kvp in responseStatusCodes) + { + if (steps.TryGetValue(kvp.Key, out var step)) + { + if (!IsSuccess(kvp.Value)) + { + var newStep = new BatchRequestStep( + requestId: step.RequestId, + httpRequestMessage: step.Request, + dependsOn: step.DependsOn?.ToList()); + + request.AddBatchRequestStep(newStep); + } + } + } + + return request; + } } } diff --git a/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/Content/BatchRequestContentTests.cs b/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/Content/BatchRequestContentTests.cs index 4ce0801fc..c5e5e87a6 100644 --- a/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/Content/BatchRequestContentTests.cs +++ b/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/Content/BatchRequestContentTests.cs @@ -692,5 +692,102 @@ public async Task BatchRequestContent_AddBatchRequestPutStepWithBaseRequestPrope // Assert we added successfully and contents are as expected and URI is not encoded Assert.Equal(expectedContent, System.Text.RegularExpressions.Regex.Unescape(requestContent)); } + + + [Fact] + public async Task BatchRequestContent_NewBatchWithFailedRequests_Files404_TreatedAsSuccessAsync() + { + var batchRequestContent = new BatchRequestContentCollection(client); + + var listChildren = new RequestInformation + { + HttpMethod = Method.GET, + UrlTemplate = "https://graph.microsoft.com/v1.0/me/drive/root/children" + }; + var listChildrenId = await batchRequestContent.AddBatchRequestStepAsync(listChildren); + + var getExistingFile = new RequestInformation + { + HttpMethod = Method.GET, + UrlTemplate = "https://graph.microsoft.com/v1.0/me/drive/items/existing" + }; + var okId = await batchRequestContent.AddBatchRequestStepAsync(getExistingFile); + + var responseStatusCodes = new Dictionary + { + { listChildrenId, HttpStatusCode.NotFound }, + { okId, HttpStatusCode.OK } + }; + + var successOverrides = new[] { HttpStatusCode.NotFound }; + + var retryBatch = batchRequestContent.NewBatchWithFailedRequests(responseStatusCodes, successOverrides); + + Assert.Empty(retryBatch.BatchRequestSteps); + } + + + [Fact] + public async Task BatchRequestContent_NewBatchWithFailedRequests_Files404_AndOk_MixesCorrectlyAsync() + { + var batchRequestContent = new BatchRequestContentCollection(client); + + var getMissingFile = new RequestInformation + { + HttpMethod = Method.GET, + UrlTemplate = "https://graph.microsoft.com/v1.0/me/drive/items/missing" + }; + var getExistingFile = new RequestInformation + { + HttpMethod = Method.GET, + UrlTemplate = "https://graph.microsoft.com/v1.0/me/drive/items/existing" + }; + + var missingId = await batchRequestContent.AddBatchRequestStepAsync(getMissingFile); + var okId = await batchRequestContent.AddBatchRequestStepAsync(getExistingFile); + + var responseStatusCodes = new Dictionary + { + { missingId, HttpStatusCode.NotFound }, + { okId, HttpStatusCode.OK } + }; + + var retryBatch = batchRequestContent.NewBatchWithFailedRequests(responseStatusCodes, null); + + Assert.Single(retryBatch.BatchRequestSteps); + Assert.True(retryBatch.BatchRequestSteps.ContainsKey(missingId)); + Assert.False(retryBatch.BatchRequestSteps.ContainsKey(okId)); + } + + [Fact] + public async Task BatchRequestContent_NewBatchWithFailedRequests_PreservesRequestIdsAsync() + { + var batchRequestContent = new BatchRequestContentCollection(client); + + var req1 = new RequestInformation { HttpMethod = Method.GET, UrlTemplate = REQUEST_URL }; + var req2 = new RequestInformation { HttpMethod = Method.GET, UrlTemplate = REQUEST_URL }; + var req3 = new RequestInformation { HttpMethod = Method.GET, UrlTemplate = REQUEST_URL }; + + var id1 = await batchRequestContent.AddBatchRequestStepAsync(req1); + var id2 = await batchRequestContent.AddBatchRequestStepAsync(req2); + var id3 = await batchRequestContent.AddBatchRequestStepAsync(req3); + + var responseStatusCodes = new Dictionary + { + { id1, HttpStatusCode.BadGateway }, + { id2, HttpStatusCode.OK }, + { id3, (HttpStatusCode)429 } + }; + + var retryBatch = batchRequestContent.NewBatchWithFailedRequests(responseStatusCodes); + + Assert.Equal(2, retryBatch.BatchRequestSteps.Count); + Assert.True(retryBatch.BatchRequestSteps.ContainsKey(id1)); + Assert.True(retryBatch.BatchRequestSteps.ContainsKey(id3)); + + Assert.Equal(id1, retryBatch.BatchRequestSteps[id1].RequestId); + Assert.Equal(id3, retryBatch.BatchRequestSteps[id3].RequestId); + } + } }