Skip to content

Commit 96fecab

Browse files
authored
Refactor HTTP request handling for improved resource management and error handling (#5296)
* Refactor HTTP request handling for improved resource management and error handling * Improve error handling and retry logic in HTTP message sending * Enhance retry logic for HTTP requests to handle throttling more effectively * Refactor HTTP response handling to return URIs instead of headers for improved clarity and consistency
1 parent 7196c92 commit 96fecab

9 files changed

Lines changed: 284 additions & 203 deletions

File tree

src/Commands/Graph/InvokeGraphMethod.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -268,28 +268,28 @@ private void GetRequestWithoutPaging()
268268
private void PostRequest()
269269
{
270270
LogDebug($"Sending HTTP POST to {Url}");
271-
var response = GraphRequestHelper.PostHttpContent(Url, GetHttpContent(), AdditionalHeaders?.GetHeaders(ConsistencyLevelEventual.IsPresent));
271+
using var response = GraphRequestHelper.PostHttpContent(Url, GetHttpContent(), AdditionalHeaders?.GetHeaders(ConsistencyLevelEventual.IsPresent));
272272
HandleResponse(response);
273273
}
274274

275275
private void PutRequest()
276276
{
277277
LogDebug($"Sending HTTP PUT to {Url}");
278-
var response = GraphRequestHelper.PutHttpContent(Url, GetHttpContent(), AdditionalHeaders?.GetHeaders(ConsistencyLevelEventual.IsPresent));
278+
using var response = GraphRequestHelper.PutHttpContent(Url, GetHttpContent(), AdditionalHeaders?.GetHeaders(ConsistencyLevelEventual.IsPresent));
279279
HandleResponse(response);
280280
}
281281

282282
private void PatchRequest()
283283
{
284284
LogDebug($"Sending HTTP PATCH to {Url}");
285-
var response = GraphRequestHelper.Patch(GetHttpContent(), Url, AdditionalHeaders?.GetHeaders(ConsistencyLevelEventual.IsPresent));
285+
using var response = GraphRequestHelper.Patch(GetHttpContent(), Url, AdditionalHeaders?.GetHeaders(ConsistencyLevelEventual.IsPresent));
286286
HandleResponse(response);
287287
}
288288

289289
private void DeleteRequest()
290290
{
291291
LogDebug($"Sending HTTP DELETE to {Url}");
292-
var response = GraphRequestHelper.Delete(Url, AdditionalHeaders?.GetHeaders(ConsistencyLevelEventual.IsPresent));
292+
using var response = GraphRequestHelper.Delete(Url, AdditionalHeaders?.GetHeaders(ConsistencyLevelEventual.IsPresent));
293293
HandleResponse(response);
294294
}
295295

src/Commands/ManagementApi/GetUnifiedAuditLog.cs

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ protected override void ExecuteCmdlet()
8181
{
8282
EnsureSubscription(ContentTypeString);
8383

84-
var url = $"{ApiUrl}/subscriptions/content?contentType={ContentTypeString}&PublisherIdentifier=${TenantId}";
84+
var url = $"{ApiUrl}/subscriptions/content?contentType={ContentTypeString}&PublisherIdentifier={TenantId}";
8585
if (StartTime != DateTime.MinValue)
8686
{
8787
url += $"&startTime={StartTime:yyyy-MM-ddTHH:mm:ss}";
@@ -92,26 +92,40 @@ protected override void ExecuteCmdlet()
9292
}
9393

9494
List<ManagementApiSubscriptionContent> subscriptionContents = new();
95-
var subscriptionResponse = RequestHelper.GetResponse(url);
96-
var content = subscriptionResponse.Content.ReadAsStringAsync().GetAwaiter().GetResult();
95+
var serializerOptions = new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
96+
System.Net.Http.HttpResponseMessage subscriptionResponse = null;
9797

98-
if (subscriptionResponse.IsSuccessStatusCode)
98+
try
9999
{
100-
subscriptionContents.AddRange(collection: JsonSerializer.Deserialize<IEnumerable<ManagementApiSubscriptionContent>>(content, new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }));
101-
while (subscriptionResponse.Headers.Contains("NextPageUri"))
100+
subscriptionResponse = RequestHelper.GetResponse(url);
101+
var content = subscriptionResponse.Content.ReadAsStringAsync().GetAwaiter().GetResult();
102+
103+
if (subscriptionResponse.IsSuccessStatusCode)
102104
{
103-
subscriptionResponse = RequestHelper.GetResponse(subscriptionResponse.Headers.GetValues("NextPageUri").First());
104-
if (subscriptionResponse.IsSuccessStatusCode)
105+
subscriptionContents.AddRange(collection: JsonSerializer.Deserialize<IEnumerable<ManagementApiSubscriptionContent>>(content, serializerOptions) ?? []);
106+
while (subscriptionResponse.Headers.Contains("NextPageUri"))
105107
{
108+
var nextPageUri = subscriptionResponse.Headers.GetValues("NextPageUri").First();
109+
subscriptionResponse.Dispose();
110+
subscriptionResponse = RequestHelper.GetResponse(nextPageUri);
106111
content = subscriptionResponse.Content.ReadAsStringAsync().GetAwaiter().GetResult();
107-
subscriptionContents.AddRange(collection: JsonSerializer.Deserialize<IEnumerable<ManagementApiSubscriptionContent>>(content));
112+
if (!subscriptionResponse.IsSuccessStatusCode)
113+
{
114+
throw new PSInvalidOperationException($"Service responded with HTTP {(int)subscriptionResponse.StatusCode} {subscriptionResponse.ReasonPhrase}: {content}");
115+
}
116+
117+
subscriptionContents.AddRange(collection: JsonSerializer.Deserialize<IEnumerable<ManagementApiSubscriptionContent>>(content, serializerOptions) ?? []);
108118
}
109119
}
120+
else
121+
{
122+
// Request was not successful
123+
throw new PSInvalidOperationException($"Service responded with HTTP {(int)subscriptionResponse.StatusCode} {subscriptionResponse.ReasonPhrase}: {content}");
124+
}
110125
}
111-
else
126+
finally
112127
{
113-
// Request was not successful
114-
throw new PSInvalidOperationException($"Service responded with HTTP {(int)subscriptionResponse.StatusCode} {subscriptionResponse.ReasonPhrase}: {content}");
128+
subscriptionResponse?.Dispose();
115129
}
116130

117131
if (subscriptionContents.Any())

src/Commands/PowerPlatform/PowerApps/ExportPowerApp.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,10 @@ protected override void ExecuteCmdlet()
7878
creator = PackageCreatedBy,
7979
sourceEnvironment = PackageSourceEnvironment
8080
};
81-
var responseHeader = PowerAppsUtility.GetResponseHeader(Connection.HttpClient, environmentName, AccessToken, appName, wrapper, objectDetails);
81+
var responseLocation = PowerAppsUtility.GetResponseLocation(Connection.HttpClient, environmentName, AccessToken, appName, wrapper, objectDetails);
8282

8383

84-
var packageLink = PowerAppsUtility.GetPackageLink(Connection.HttpClient, Convert.ToString(responseHeader.Location), AccessToken);
84+
var packageLink = PowerAppsUtility.GetPackageLink(Connection.HttpClient, Convert.ToString(responseLocation), AccessToken);
8585
var getFileByteArray = PowerAppsUtility.GetFileByteArray(Connection.HttpClient, packageLink, AccessToken);
8686
var fileName = string.Empty;
8787
if (ParameterSpecified(nameof(OutPath)))

src/Commands/Utilities/CmdletMessageWriter.cs

Lines changed: 52 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -28,42 +28,66 @@ public void Stop()
2828

2929
public void Start()
3030
{
31-
while (!Finished || Queue.Count > 0)
31+
while (!Finished || HasMessages)
3232
{
33-
while (Queue.Count > 0)
33+
if (!TryDequeue(out var message))
3434
{
35-
var message = Queue.Dequeue();
36-
if (message.Formatted)
35+
Thread.Sleep(100);
36+
continue;
37+
}
38+
39+
if (message.Formatted)
40+
{
41+
WriteFormattedMessage(Cmdlet, message);
42+
}
43+
else
44+
{
45+
switch (message.Type)
3746
{
38-
WriteFormattedMessage(Cmdlet, message);
47+
case MessageType.Message:
48+
{
49+
Cmdlet.Host.UI.WriteLine(message.Text);
50+
break;
51+
}
52+
case MessageType.Warning:
53+
{
54+
Cmdlet.Host.UI.WriteWarningLine(message.Text);
55+
break;
56+
}
57+
case MessageType.Verbose:
58+
{
59+
Cmdlet.Host.UI.WriteVerboseLine(message.Text);
60+
break;
61+
}
3962
}
40-
else
41-
{
42-
switch (message.Type)
43-
{
44-
case MessageType.Message:
45-
{
46-
Cmdlet.Host.UI.WriteLine(message.Text);
47-
break;
48-
}
49-
case MessageType.Warning:
50-
{
51-
Cmdlet.Host.UI.WriteWarningLine(message.Text);
52-
break;
53-
}
54-
case MessageType.Verbose:
55-
{
56-
Cmdlet.Host.UI.WriteVerboseLine(message.Text);
57-
break;
58-
}
59-
}
63+
}
64+
}
65+
}
6066

61-
}
62-
break;
67+
private bool HasMessages
68+
{
69+
get
70+
{
71+
lock (LockToken)
72+
{
73+
return Queue.Count > 0;
6374
}
6475
}
76+
}
77+
78+
private bool TryDequeue(out Message message)
79+
{
80+
lock (LockToken)
81+
{
82+
if (Queue.Count > 0)
83+
{
84+
message = Queue.Dequeue();
85+
return true;
86+
}
6587

66-
Thread.Sleep(100);
88+
message = null;
89+
return false;
90+
}
6791
}
6892

6993
public void LogWarning(string message, bool formatted = true)

src/Commands/Utilities/ImportFlowUtility.cs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,17 @@ public static ImportFlowResult ExecuteImportFlow(HttpClient httpClient, string a
2121
var sasUrl = GenerateSasUrl(httpClient, accessToken, baseUrl, environmentName);
2222
var blobUri = BuildBlobUri(sasUrl, packagePath);
2323
UploadPackageToBlob(blobUri, packagePath);
24-
var importParametersResponse = GetImportParameters(httpClient, accessToken, baseUrl, environmentName, blobUri);
25-
var importOperationsData = GetImportOperations(httpClient, accessToken, importParametersResponse.Location.ToString(), maxRetries, delayMs);
24+
var importParametersLocation = GetImportParameters(httpClient, accessToken, baseUrl, environmentName, blobUri);
25+
var importOperationsData = GetImportOperations(httpClient, accessToken, importParametersLocation.ToString(), maxRetries, delayMs);
2626
var propertiesElement = GetPropertiesElement(importOperationsData);
2727
ValidateProperties(propertiesElement);
2828
var resourcesObject = ParseResources(propertiesElement);
2929
var resource = TransformResources(resourcesObject, name);
3030
var validatePackagePayload = CreateImportObject(propertiesElement, resourcesObject);
3131
var validateResponseData = ValidateImportPackage(httpClient, accessToken, baseUrl, environmentName, validatePackagePayload);
3232
var importPackagePayload = CreateImportObject(validateResponseData);
33-
var importResult = ImportPackage(httpClient, accessToken, baseUrl, environmentName, importPackagePayload);
34-
return WaitForImportCompletion(httpClient, accessToken, importResult.Location.ToString());
33+
var importResultLocation = ImportPackage(httpClient, accessToken, baseUrl, environmentName, importPackagePayload);
34+
return WaitForImportCompletion(httpClient, accessToken, importResultLocation.ToString());
3535
}
3636
public static string GenerateSasUrl(HttpClient httpClient, string accessToken, string baseUrl, string environmentName)
3737
{
@@ -73,7 +73,7 @@ public static void UploadPackageToBlob(UriBuilder blobUri, string PackagePath)
7373
}
7474
}
7575

76-
public static System.Net.Http.Headers.HttpResponseHeaders GetImportParameters(HttpClient httpClient, string accessToken, string baseUrl, string environmentName, UriBuilder blobUri)
76+
public static Uri GetImportParameters(HttpClient httpClient, string accessToken, string baseUrl, string environmentName, UriBuilder blobUri)
7777
{
7878
var importPayload = new
7979
{
@@ -82,7 +82,7 @@ public static System.Net.Http.Headers.HttpResponseHeaders GetImportParameters(Ht
8282
value = blobUri.Uri.ToString()
8383
}
8484
};
85-
var response = RestHelper.PostGetResponseHeader<JsonElement>(
85+
var responseLocation = RestHelper.PostGetResponseLocation<JsonElement>(
8686
httpClient,
8787
$"{baseUrl}/providers/Microsoft.BusinessAppPlatform/environments/{environmentName}/listImportParameters?api-version=2016-11-01",
8888
accessToken,
@@ -91,7 +91,7 @@ public static System.Net.Http.Headers.HttpResponseHeaders GetImportParameters(Ht
9191
);
9292
Log.Debug("ImportFlowUtility", "Import parameters retrieved");
9393
System.Threading.Thread.Sleep(2500);
94-
return response;
94+
return responseLocation;
9595
}
9696

9797
public static JsonElement GetImportOperations(HttpClient httpClient, string accessToken, string importOperationsUrl, int? maxRetries = null, int? delayMs = null)
@@ -224,17 +224,17 @@ public static JsonObject CreateImportObject(JsonElement importData, JsonObject r
224224
return resourcesObject;
225225
}
226226

227-
public static System.Net.Http.Headers.HttpResponseHeaders ImportPackage(HttpClient httpClient, string accessToken, string baseUrl, string environmentName, JsonObject importPackagePayload)
227+
public static Uri ImportPackage(HttpClient httpClient, string accessToken, string baseUrl, string environmentName, JsonObject importPackagePayload)
228228
{
229-
var importResult = RestHelper.PostGetResponseHeader<JsonElement>(
229+
var importResultLocation = RestHelper.PostGetResponseLocation<JsonElement>(
230230
httpClient,
231231
$"{baseUrl}/providers/Microsoft.BusinessAppPlatform/environments/{environmentName}/importPackage?api-version=2016-11-01",
232232
accessToken,
233233
payload: importPackagePayload,
234234
accept: "application/json"
235235
);
236236
Log.Debug("ImportFlowUtility", "Import package initiated");
237-
return importResult;
237+
return importResultLocation;
238238
}
239239

240240
public static ImportFlowResult WaitForImportCompletion(HttpClient httpClient,string accessToken,string importPackageResponseUrl)

src/Commands/Utilities/PowerAppsUtility.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using System;
22
using System.Net.Http;
3-
using System.Net.Http.Headers;
43
using System.Text.Json;
54
using System.Threading;
65
using PnP.Framework;
@@ -25,7 +24,7 @@ internal static Model.PowerPlatform.PowerApp.PowerAppPackageWrapper GetWrapper(H
2524
return wrapper;
2625
}
2726

28-
internal static HttpResponseHeaders GetResponseHeader(HttpClient connection, string environmentName, string accessToken, string appName, Model.PowerPlatform.PowerApp.PowerAppPackageWrapper wrapper, object details, AzureEnvironment azureEnvironment = AzureEnvironment.Production)
27+
internal static Uri GetResponseLocation(HttpClient connection, string environmentName, string accessToken, string appName, Model.PowerPlatform.PowerApp.PowerAppPackageWrapper wrapper, object details, AzureEnvironment azureEnvironment = AzureEnvironment.Production)
2928
{
3029
var exportPostData = new
3130
{
@@ -38,10 +37,10 @@ internal static HttpResponseHeaders GetResponseHeader(HttpClient connection, str
3837
};
3938

4039
string baseUrl = PowerPlatformUtility.GetBapEndpoint(azureEnvironment);
41-
var responseHeader = RestHelper.PostGetResponseHeader<string>(connection, $"{baseUrl}/providers/Microsoft.BusinessAppPlatform/environments/{environmentName}/exportPackage?api-version=2016-11-01", accessToken, payload: exportPostData);
40+
var responseLocation = RestHelper.PostGetResponseLocation<string>(connection, $"{baseUrl}/providers/Microsoft.BusinessAppPlatform/environments/{environmentName}/exportPackage?api-version=2016-11-01", accessToken, payload: exportPostData);
4241

4342

44-
return responseHeader;
43+
return responseLocation;
4544
}
4645

4746
internal static string GetPackageLink(HttpClient connection, string location, string accessToken)

0 commit comments

Comments
 (0)