-
Notifications
You must be signed in to change notification settings - Fork 65
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
- Loading branch information
1 parent
129fdde
commit 393eaa1
Showing
2 changed files
with
254 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,242 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
using Microsoft.DevProxy.Abstractions; | ||
using System.Net.Http.Headers; | ||
using System.Text.Json; | ||
using System.Text.Json.Serialization; | ||
|
||
namespace Microsoft.DevProxy; | ||
|
||
class ProxyPresetInfo | ||
{ | ||
public IList<string> ConfigFiles { get; set; } = new List<string>(); | ||
public IList<string> MockFiles { get; set; } = new List<string>(); | ||
} | ||
|
||
class GitHubTreeResponse | ||
{ | ||
[JsonPropertyName("tree")] | ||
public GitHubTreeItem[] Tree { get; set; } = Array.Empty<GitHubTreeItem>(); | ||
[JsonPropertyName("truncated")] | ||
public bool Truncated { get; set; } | ||
} | ||
|
||
class GitHubTreeItem | ||
{ | ||
[JsonPropertyName("path")] | ||
public string Path { get; set; } = string.Empty; | ||
[JsonPropertyName("type")] | ||
public string Type { get; set; } = string.Empty; | ||
} | ||
|
||
public static class PresetGetCommandHandler | ||
{ | ||
public static async Task DownloadPreset(string presetId, ILogger logger) | ||
{ | ||
try | ||
{ | ||
var appFolder = ProxyUtils.AppFolder; | ||
if (string.IsNullOrEmpty(appFolder) || !Directory.Exists(appFolder)) | ||
{ | ||
logger.LogError("App folder not found"); | ||
return; | ||
} | ||
|
||
var presetsFolderPath = Path.Combine(appFolder, "presets"); | ||
logger.LogDebug($"Checking if presets folder {presetsFolderPath} exists..."); | ||
if (!Directory.Exists(presetsFolderPath)) | ||
{ | ||
logger.LogDebug("Presets folder not found, creating it..."); | ||
Directory.CreateDirectory(presetsFolderPath); | ||
logger.LogDebug("Presets folder created"); | ||
} | ||
|
||
logger.LogDebug($"Getting target folder path for preset {presetId}..."); | ||
var targetFolderPath = GetTargetFolderPath(appFolder, presetId); | ||
logger.LogDebug($"Creating target folder {targetFolderPath}..."); | ||
Directory.CreateDirectory(targetFolderPath); | ||
|
||
logger.LogInfo($"Downloading preset {presetId}..."); | ||
|
||
var sampleFiles = await GetFilesToDownload(presetId, logger); | ||
foreach (var sampleFile in sampleFiles) | ||
{ | ||
await DownloadFile(sampleFile, targetFolderPath, presetId, logger); | ||
} | ||
|
||
logger.LogInfo($"Preset saved in {targetFolderPath}"); | ||
var presetInfo = GetPresetInfo(targetFolderPath, logger); | ||
if (!presetInfo.ConfigFiles.Any() && !presetInfo.MockFiles.Any()) | ||
{ | ||
return; | ||
} | ||
|
||
logger.LogInfo(""); | ||
if (presetInfo.ConfigFiles.Any()) | ||
{ | ||
logger.LogInfo("To start Dev Proxy with the preset, run:"); | ||
foreach (var configFile in presetInfo.ConfigFiles) | ||
{ | ||
logger.LogInfo($" devproxy --config-file \"{configFile.Replace(appFolder, "~appFolder")}\""); | ||
} | ||
} | ||
else | ||
{ | ||
logger.LogInfo("To start Dev Proxy with the mock file, enable the MockResponsePlugin or GraphMockResponsePlugin and run:"); | ||
foreach (var mockFile in presetInfo.MockFiles) | ||
{ | ||
logger.LogInfo($" devproxy --mock-file \"{mockFile.Replace(appFolder, "~appFolder")}\""); | ||
} | ||
} | ||
} | ||
catch (Exception ex) | ||
{ | ||
logger.LogError(ex.Message); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Returns the list of files that can be used as entry points for the preset | ||
/// </summary> | ||
/// <remarks> | ||
/// A sample in the gallery can have multiple entry points. It can | ||
/// contain multiple config files or no config files and a multiple | ||
/// mock files. This method returns the list of files that Dev Proxy | ||
/// can use as entry points. | ||
/// If there's one or more config files, it'll return an array of | ||
/// these file names. If there are no proxy configs, it'll return | ||
/// an array of all the mock files. If there are no mocks, it'll return | ||
/// an empty array indicating that there's no entry point. | ||
/// </remarks> | ||
/// <param name="presetFolder">Full path to the folder with preset files</param> | ||
/// <returns>Array of files that can be used to start proxy with</returns> | ||
private static ProxyPresetInfo GetPresetInfo(string presetFolder, ILogger logger) | ||
{ | ||
var presetInfo = new ProxyPresetInfo(); | ||
|
||
logger.LogDebug($"Getting list of JSON files in {presetFolder}..."); | ||
var jsonFiles = Directory.GetFiles(presetFolder, "*.json"); | ||
if (!jsonFiles.Any()) | ||
{ | ||
logger.LogDebug("No JSON files found"); | ||
return presetInfo; | ||
} | ||
|
||
foreach (var jsonFile in jsonFiles) | ||
{ | ||
logger.LogDebug($"Reading file {jsonFile}..."); | ||
|
||
var fileContents = File.ReadAllText(jsonFile); | ||
if (fileContents.Contains("\"plugins\":")) | ||
{ | ||
logger.LogDebug($"File {jsonFile} contains proxy config"); | ||
presetInfo.ConfigFiles.Add(jsonFile); | ||
continue; | ||
} | ||
|
||
if (fileContents.Contains("\"responses\":")) | ||
{ | ||
logger.LogDebug($"File {jsonFile} contains mock data"); | ||
presetInfo.MockFiles.Add(jsonFile); | ||
continue; | ||
} | ||
|
||
logger.LogDebug($"File {jsonFile} is not a proxy config or mock data"); | ||
} | ||
|
||
if (presetInfo.ConfigFiles.Any()) | ||
{ | ||
logger.LogDebug($"Found {presetInfo.ConfigFiles.Count} proxy config files. Clearing mocks..."); | ||
presetInfo.MockFiles.Clear(); | ||
} | ||
|
||
return presetInfo; | ||
} | ||
|
||
private static string GetTargetFolderPath(string appFolder, string presetId) | ||
{ | ||
var baseFolder = Path.Combine(appFolder, "presets", presetId); | ||
var newFolder = baseFolder; | ||
var i = 1; | ||
while (Directory.Exists(newFolder)) | ||
{ | ||
newFolder = baseFolder + i++; | ||
} | ||
|
||
return newFolder; | ||
} | ||
|
||
private static async Task<string[]> GetFilesToDownload(string sampleFolderName, ILogger logger) | ||
{ | ||
logger.LogDebug($"Getting list of files in Dev Proxy samples repo..."); | ||
var url = $"https://api.github.com/repos/pnp/proxy-samples/git/trees/main?recursive=1"; | ||
using (var client = new HttpClient()) | ||
{ | ||
client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("dev-proxy", ProxyUtils.ProductVersion)); | ||
var response = await client.GetAsync(url); | ||
|
||
if (response.IsSuccessStatusCode) | ||
{ | ||
var content = await response.Content.ReadAsStringAsync(); | ||
var tree = JsonSerializer.Deserialize<GitHubTreeResponse>(content); | ||
if (tree is null) | ||
{ | ||
throw new Exception("Failed to get list of files from GitHub"); | ||
} | ||
|
||
var samplePath = $"samples/{sampleFolderName}"; | ||
|
||
var filesToDownload = tree.Tree | ||
.Where(f => f.Path.StartsWith(samplePath, StringComparison.OrdinalIgnoreCase) && f.Type == "blob") | ||
.Select(f => f.Path) | ||
.ToArray(); | ||
|
||
foreach (var file in filesToDownload) | ||
{ | ||
logger.LogDebug($"Found file {file}"); | ||
} | ||
|
||
return filesToDownload; | ||
} | ||
else | ||
{ | ||
throw new Exception($"Failed to get list of files from GitHub. Status code: {response.StatusCode}"); | ||
} | ||
} | ||
} | ||
|
||
private static async Task DownloadFile(string filePath, string targetFolderPath, string presetId, ILogger logger) | ||
{ | ||
var url = $"https://raw.githubusercontent.com/pnp/proxy-samples/main/{filePath.Replace("#", "%23")}"; | ||
logger.LogDebug($"Downloading file {filePath}..."); | ||
|
||
using (var client = new HttpClient()) | ||
{ | ||
var response = await client.GetAsync(url); | ||
|
||
if (response.IsSuccessStatusCode) | ||
{ | ||
var contentStream = await response.Content.ReadAsStreamAsync(); | ||
var filePathInsideSample = Path.GetRelativePath($"samples/{presetId}", filePath); | ||
var directoryNameInsideSample = Path.GetDirectoryName(filePathInsideSample); | ||
if (directoryNameInsideSample is not null) | ||
{ | ||
Directory.CreateDirectory(Path.Combine(targetFolderPath, directoryNameInsideSample)); | ||
} | ||
var localFilePath = Path.Combine(targetFolderPath, filePathInsideSample); | ||
|
||
using (var fileStream = new FileStream(localFilePath, FileMode.Create, FileAccess.Write, FileShare.None)) | ||
{ | ||
await contentStream.CopyToAsync(fileStream); | ||
} | ||
|
||
logger.LogDebug($"File downloaded successfully to {localFilePath}"); | ||
} | ||
else | ||
{ | ||
throw new Exception($"Failed to download file {url}. Status code: {response.StatusCode}"); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters