Skip to content

Commit 163c893

Browse files
Refactoring ImportFlow
1 parent ca5f73d commit 163c893

File tree

2 files changed

+274
-284
lines changed

2 files changed

+274
-284
lines changed

src/Commands/PowerPlatform/PowerAutomate/ImportFlow.cs

Lines changed: 16 additions & 284 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,8 @@
22
using PnP.PowerShell.Commands.Base;
33
using PnP.PowerShell.Commands.Base.PipeBinds;
44
using PnP.PowerShell.Commands.Utilities;
5-
using PnP.PowerShell.Commands.Utilities.REST;
6-
using System;
7-
using System.IO;
85
using System.Management.Automation;
9-
using System.Net.Http;
10-
using System.Text.Json;
11-
using System.Text.Json.Nodes;
6+
127

138
namespace PnP.PowerShell.Commands.PowerPlatform.PowerAutomate
149
{
@@ -35,32 +30,21 @@ protected override void ExecuteCmdlet()
3530
{
3631
var environmentName = GetEnvironmentName();
3732
string baseUrl = PowerPlatformUtility.GetBapEndpoint(Connection.AzureEnvironment);
38-
39-
//Get the SAS URL for the blob storage
40-
var sasUrl = GenerateSasUrl(baseUrl, environmentName);
41-
var blobUri = BuildBlobUri(sasUrl, PackagePath);
42-
// Step 1: Upload the package to the blob storage using the SAS URL
43-
UploadPackageToBlob(blobUri);
44-
//Step 2: this will list the import parameters
45-
var importParametersResponse = GetImportParameters(baseUrl, environmentName, blobUri);
46-
// Step 3: Get the list of import operations data
47-
var importOperationsData = GetImportOperations(importParametersResponse.Location.ToString());
48-
var propertiesElement = GetPropertiesElement(importOperationsData);
49-
50-
ValidateProperties(propertiesElement);
51-
var resourcesObject = ParseResources(propertiesElement);
52-
// Step 4: Transform the resources object
53-
var resource = TransformResources(resourcesObject);
54-
55-
var validatePackagePayload = CreateImportObject(propertiesElement, resourcesObject);
56-
//Step 5: Validate the import package
57-
var validateResponseData = ValidateImportPackage(baseUrl, environmentName, validatePackagePayload);
58-
59-
var importPackagePayload = CreateImportObject(validateResponseData);
60-
//Step 6: import package
61-
var importResult = ImportPackage(baseUrl, environmentName, importPackagePayload);
62-
//Step 7: Wait for the import to complete
63-
var importStatus = WaitForImportCompletion(importResult.Location.ToString());
33+
var sasUrl = ImportFlowUtility.GenerateSasUrl(Connection.HttpClient, AccessToken, baseUrl, environmentName);
34+
var blobUri = ImportFlowUtility.BuildBlobUri(sasUrl, PackagePath);
35+
ImportFlowUtility.UploadPackageToBlob(blobUri, PackagePath);
36+
var importParametersResponse = ImportFlowUtility.GetImportParameters(Connection.HttpClient, AccessToken, baseUrl, environmentName, blobUri);
37+
var importOperationsData = ImportFlowUtility.GetImportOperations(Connection.HttpClient, AccessToken, importParametersResponse.Location.ToString());
38+
var propertiesElement = ImportFlowUtility.GetPropertiesElement(importOperationsData);
39+
40+
ImportFlowUtility.ValidateProperties(propertiesElement);
41+
var resourcesObject = ImportFlowUtility.ParseResources(propertiesElement);
42+
var resource = ImportFlowUtility.TransformResources(resourcesObject, Name);
43+
var validatePackagePayload = ImportFlowUtility.CreateImportObject(propertiesElement, resourcesObject);
44+
var validateResponseData = ImportFlowUtility.ValidateImportPackage(Connection.HttpClient, AccessToken, baseUrl, environmentName, validatePackagePayload);
45+
var importPackagePayload = ImportFlowUtility.CreateImportObject(validateResponseData);
46+
var importResult = ImportFlowUtility.ImportPackage(Connection.HttpClient, AccessToken, baseUrl, environmentName, importPackagePayload);
47+
var importStatus = ImportFlowUtility.WaitForImportCompletion(Connection.HttpClient, AccessToken, importResult.Location.ToString());
6448

6549
WriteObject($"Import {importStatus}");
6650
}
@@ -71,257 +55,5 @@ private string GetEnvironmentName()
7155
? Environment.GetName()
7256
: PowerPlatformUtility.GetDefaultEnvironment(ArmRequestHelper, Connection.AzureEnvironment)?.Name;
7357
}
74-
75-
private string GenerateSasUrl(string baseUrl, string environmentName)
76-
{
77-
var response = RestHelper.Post(Connection.HttpClient, $"{baseUrl}/providers/Microsoft.BusinessAppPlatform/environments/{environmentName}/generateResourceStorage?api-version=2016-11-01", AccessToken);
78-
WriteVerbose($"Storage resource URL generated: {response}");
79-
var data = JsonSerializer.Deserialize<JsonElement>(response);
80-
return data.GetProperty("sharedAccessSignature").GetString();
81-
}
82-
83-
private UriBuilder BuildBlobUri(string sasUrl, string packagePath)
84-
{
85-
var fileName = Path.GetFileName(packagePath);
86-
var blobUri = new UriBuilder(sasUrl);
87-
blobUri.Path += $"/{fileName}";
88-
return blobUri;
89-
}
90-
91-
private void UploadPackageToBlob(UriBuilder blobUri)
92-
{
93-
// Step 2: Upload the package to the blob storage using the SAS URL
94-
95-
// Upload using clean HttpClient
96-
using (var blobClient = new HttpClient())
97-
using (var packageFileStream = new FileStream(PackagePath, FileMode.Open, FileAccess.Read))
98-
{
99-
var packageContent = new StreamContent(packageFileStream);
100-
packageContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream");
101-
102-
var request = new HttpRequestMessage(HttpMethod.Put, blobUri.Uri)
103-
{
104-
Content = packageContent
105-
};
106-
107-
request.Headers.Add("x-ms-blob-type", "BlockBlob");
108-
109-
var uploadResponse = blobClient.SendAsync(request).GetAwaiter().GetResult();
110-
111-
if (!uploadResponse.IsSuccessStatusCode)
112-
{
113-
var errorContent = uploadResponse.Content.ReadAsStringAsync().GetAwaiter().GetResult();
114-
throw new Exception($"Upload failed: {uploadResponse.StatusCode} - {errorContent}");
115-
}
116-
}
117-
}
118-
119-
private System.Net.Http.Headers.HttpResponseHeaders GetImportParameters(string baseUrl, string environmentName, UriBuilder blobUri)
120-
{
121-
var importPayload = new
122-
{
123-
packageLink = new
124-
{
125-
value = blobUri.Uri.ToString()
126-
}
127-
};
128-
var response = RestHelper.PostGetResponseHeader<JsonElement>(
129-
Connection.HttpClient,
130-
$"{baseUrl}/providers/Microsoft.BusinessAppPlatform/environments/{environmentName}/listImportParameters?api-version=2016-11-01",
131-
AccessToken,
132-
payload: importPayload,
133-
accept: "application/json"
134-
);
135-
WriteVerbose("Import parameters retrieved");
136-
System.Threading.Thread.Sleep(2500);
137-
return response;
138-
}
139-
140-
private JsonElement GetImportOperations(string importOperationsUrl)
141-
{
142-
var listImportOperations = RestHelper.Get(
143-
Connection.HttpClient,
144-
importOperationsUrl,
145-
AccessToken,
146-
accept: "application/json"
147-
);
148-
WriteVerbose("Import operations retrieved");
149-
return JsonSerializer.Deserialize<JsonElement>(listImportOperations);
150-
}
151-
152-
private JsonElement GetPropertiesElement(JsonElement importOperationsData)
153-
{
154-
if (!importOperationsData.TryGetProperty("properties", out JsonElement propertiesElement))
155-
{
156-
WriteObject("Import failed: 'properties' section missing.");
157-
throw new Exception("Import failed: 'properties' section missing.");
158-
}
159-
return propertiesElement;
160-
}
161-
162-
private void ValidateProperties(JsonElement propertiesElement)
163-
{
164-
bool hasStatus = propertiesElement.TryGetProperty("status", out _);
165-
bool hasPackageLink = propertiesElement.TryGetProperty("packageLink", out _);
166-
bool hasDetails = propertiesElement.TryGetProperty("details", out _);
167-
bool hasResources = propertiesElement.TryGetProperty("resources", out _);
168-
169-
if (!(hasStatus && hasPackageLink && hasDetails && hasResources))
170-
{
171-
WriteObject("Import failed: One or more required fields are missing in 'properties'.");
172-
throw new Exception("Import failed: One or more required fields are missing in 'properties'.");
173-
}
174-
if (!propertiesElement.TryGetProperty("resources", out JsonElement resourcesElement))
175-
{
176-
WriteObject("Import failed: 'resources' section missing in 'properties'.");
177-
return;
178-
}
179-
}
180-
181-
private JsonObject ParseResources(JsonElement propertiesElement)
182-
{
183-
if (!propertiesElement.TryGetProperty("resources", out JsonElement resourcesElement))
184-
{
185-
WriteObject("Import failed: 'resources' section missing in 'properties'.");
186-
throw new Exception("Import failed: 'resources' section missing in 'properties'.");
187-
}
188-
return JsonNode.Parse(resourcesElement.GetRawText()) as JsonObject;
189-
}
190-
191-
private JsonElement ValidateImportPackage(string baseUrl, string environmentName, JsonObject validatePackagePayload)
192-
{
193-
var validateResponse = RestHelper.Post(Connection.HttpClient, $"{baseUrl}/providers/Microsoft.BusinessAppPlatform/environments/{environmentName}/validateImportPackage?api-version=2016-11-01", AccessToken, payload: validatePackagePayload);
194-
return JsonSerializer.Deserialize<JsonElement>(validateResponse);
195-
}
196-
197-
private JsonObject TransformResources(JsonObject resourcesObject)
198-
{
199-
foreach (var property in resourcesObject)
200-
{
201-
string resourceKey = property.Key;
202-
var resource = property.Value as JsonObject;
203-
204-
if (resource != null && resource.TryGetPropertyValue("type", out JsonNode typeNode))
205-
{
206-
string resourceType = typeNode?.ToString();
207-
208-
if (resourceType == "Microsoft.Flow/flows")
209-
{
210-
resource["selectedCreationType"] = "New";
211-
if (ParameterSpecified(nameof(Name)))
212-
{
213-
if (resource.TryGetPropertyValue("details", out JsonNode detailsNode) && detailsNode is JsonObject detailsObject)
214-
{
215-
detailsObject["displayName"] = Name;
216-
}
217-
}
218-
}
219-
else if (resourceType == "Microsoft.PowerApps/apis/connections")
220-
{
221-
resource["selectedCreationType"] = "Existing";
222-
223-
// Only set the id if suggestedId exists
224-
if (resource.TryGetPropertyValue("suggestedId", out JsonNode suggestedIdNode) && suggestedIdNode != null)
225-
{
226-
resource["id"] = JsonValue.Create(suggestedIdNode.ToString());
227-
}
228-
}
229-
}
230-
}
231-
return resourcesObject;
232-
}
233-
234-
private JsonObject CreateImportObject(JsonElement importData, JsonObject resourceObject = null)
235-
{
236-
JsonObject resourcesObject = new JsonObject
237-
{
238-
["details"] = JsonNode.Parse(importData.GetProperty("details").GetRawText()),
239-
["packageLink"] = JsonNode.Parse(importData.GetProperty("packageLink").GetRawText()),
240-
["status"] = JsonNode.Parse(importData.GetProperty("status").GetRawText()),
241-
["resources"] = resourceObject ?? JsonNode.Parse(importData.GetProperty("resources").GetRawText())
242-
};
243-
return resourcesObject;
244-
}
245-
246-
private System.Net.Http.Headers.HttpResponseHeaders ImportPackage(string baseUrl, string environmentName, JsonObject importPackagePayload)
247-
{
248-
var importResult = RestHelper.PostGetResponseHeader<JsonElement>(
249-
Connection.HttpClient,
250-
$"{baseUrl}/providers/Microsoft.BusinessAppPlatform/environments/{environmentName}/importPackage?api-version=2016-11-01",
251-
AccessToken,
252-
payload: importPackagePayload,
253-
accept: "application/json"
254-
);
255-
WriteVerbose("Import package initiated");
256-
return importResult;
257-
}
258-
259-
private string WaitForImportCompletion(string importPackageResponseUrl)
260-
{
261-
string status;
262-
int retryCount = 0;
263-
264-
do
265-
{
266-
System.Threading.Thread.Sleep(2500);
267-
var importResultData = RestHelper.Get(Connection.HttpClient, importPackageResponseUrl, AccessToken, accept: "application/json");
268-
var importResultDataElement = JsonSerializer.Deserialize<JsonElement>(importResultData);
269-
270-
if (importResultDataElement.TryGetProperty("properties", out JsonElement importResultPropertiesElement) &&
271-
importResultPropertiesElement.TryGetProperty("status", out JsonElement statusElement))
272-
{
273-
status = statusElement.GetString();
274-
}
275-
else
276-
{
277-
WriteWarning("Failed to retrieve the status from the response.");
278-
throw new Exception("Import status could not be determined.");
279-
}
280-
281-
282-
if (status == "Running")
283-
{
284-
WriteVerbose("Import is still running. Waiting for completion...");
285-
retryCount++;
286-
}
287-
else if (status == "Failed")
288-
{
289-
ThrowImportError(importResultData);
290-
}
291-
} while (status == "Running" && retryCount < 5);
292-
293-
if (status == "Running")
294-
{
295-
throw new Exception("Import failed to complete after 5 attempts.");
296-
}
297-
298-
return status;
299-
}
300-
301-
private void ThrowImportError(string importResultData)
302-
{
303-
var importErrorResultData = JsonSerializer.Deserialize<JsonElement>(importResultData);
304-
if (importErrorResultData.TryGetProperty("properties", out JsonElement importErrorResultPropertiesElement) &&
305-
importErrorResultPropertiesElement.TryGetProperty("resources", out JsonElement resourcesElement))
306-
{
307-
foreach (var resource in resourcesElement.EnumerateObject())
308-
{
309-
if (resource.Value.TryGetProperty("error", out JsonElement errorElement))
310-
{
311-
string errorMessage = errorElement.TryGetProperty("message", out JsonElement messageElement)
312-
? messageElement.GetString()
313-
: errorElement.TryGetProperty("code", out JsonElement codeElement)
314-
? codeElement.GetString()
315-
: "Unknown error";
316-
throw new Exception($"Import failed: {errorMessage}");
317-
}
318-
}
319-
throw new Exception("Import failed: No error details found in resources.");
320-
}
321-
else
322-
{
323-
throw new Exception("Import failed: Unknown error.");
324-
}
325-
}
32658
}
32759
}

0 commit comments

Comments
 (0)