diff --git a/.gitignore b/.gitignore
index 120d8b6..bce3b4c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -151,3 +151,4 @@ UpgradeLog.htm
/src/AzureDevOpsDemoBuilder/log
/src/AzureDevOpsDemoBuilder/Appsettings.Development.json
/src/AzureDevOpsDemoBuilder/appsettings.json
+/src/ADOGenerator/ExtractedTemplate
diff --git a/src/ADOGenerator/ADOGenerator.csproj b/src/ADOGenerator/ADOGenerator.csproj
index 34dac0a..e1fc806 100644
--- a/src/ADOGenerator/ADOGenerator.csproj
+++ b/src/ADOGenerator/ADOGenerator.csproj
@@ -9,6 +9,7 @@
+
diff --git a/src/ADOGenerator/IServices/IExtractorService.cs b/src/ADOGenerator/IServices/IExtractorService.cs
new file mode 100644
index 0000000..f52425b
--- /dev/null
+++ b/src/ADOGenerator/IServices/IExtractorService.cs
@@ -0,0 +1,32 @@
+using ADOGenerator.Models;
+using RestAPI;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace ADOGenerator.IServices
+{
+ public interface IExtractorService
+ {
+ ProjectConfigurations ProjectConfiguration(Project model);
+ int GetTeamsCount(ProjectConfigurations appConfig);
+ int GetIterationsCount(ProjectConfigurations appConfig);
+ int GetBuildDefinitionCount(ProjectConfigurations appConfig);
+ int GetReleaseDefinitionCount(ProjectConfigurations appConfig);
+ string[] GenerateTemplateArifacts(Project model);
+ Dictionary GetWorkItemsCount(ProjectConfigurations appConfig);
+ List GetInstalledExtensions(ProjectConfigurations appConfig, string extractedFolderName);
+ void ExportQuries(ProjectConfigurations appConfig, string extractedFolderName);
+ bool ExportTeams(RestAPI.ADOConfiguration con, Project model, string extractedFolderName);
+ bool ExportIterations(ProjectConfigurations appConfig, string extractedFolderName);
+ bool ExportWorkItems(ProjectConfigurations appConfig, string extractedFolderName);
+ bool ExportRepositoryList(ProjectConfigurations appConfig, string extractedFolderName);
+ int GetBuildDefinitions(ProjectConfigurations appConfig, string extractedFolderName);
+ int GeneralizingGetReleaseDefinitions(ProjectConfigurations appConfig, string extractedFolderName);
+ void GetServiceEndpoints(ProjectConfigurations appConfig, string extractedFolderName);
+ bool ExportDeliveryPlans(ProjectConfigurations appConfig, string extractedFolderName);
+ bool IsTemplateExists(string templateName);
+ }
+}
diff --git a/src/ADOGenerator/IServices/IProjectService.cs b/src/ADOGenerator/IServices/IProjectService.cs
index 7982db5..c21caad 100644
--- a/src/ADOGenerator/IServices/IProjectService.cs
+++ b/src/ADOGenerator/IServices/IProjectService.cs
@@ -17,6 +17,9 @@ public interface IProjectService
public bool InstallExtensions(Project model, string accountName, string PAT);
public bool WhereDoseTemplateBelongTo(string templatName);
+ public HttpResponseMessage GetProjects(string accname, string pat, string authScheme);
+
+ public Task> SelectProject(string accessToken, HttpResponseMessage projectsData);
}
}
diff --git a/src/ADOGenerator/IServices/ITemplateService.cs b/src/ADOGenerator/IServices/ITemplateService.cs
index 419c9f7..4e497d7 100644
--- a/src/ADOGenerator/IServices/ITemplateService.cs
+++ b/src/ADOGenerator/IServices/ITemplateService.cs
@@ -1,6 +1,11 @@
-namespace ADOGenerator.IServices
+using ADOGenerator.Models;
+
+namespace ADOGenerator.IServices
{
public interface ITemplateService
{
+ bool AnalyzeProject(Project model);
+ bool CheckTemplateExists(Project model);
+ (bool,string,string) GenerateTemplateArtifacts(Project model);
}
}
diff --git a/src/ADOGenerator/Models/ExtractorAnalysis.cs b/src/ADOGenerator/Models/ExtractorAnalysis.cs
new file mode 100644
index 0000000..3d88cc5
--- /dev/null
+++ b/src/ADOGenerator/Models/ExtractorAnalysis.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace ADOGenerator.Models
+{
+ internal class ExtractorAnalysis
+ {
+ public int teamCount { get; set; }
+ public int IterationCount { get; set; }
+ public int BuildDefCount { get; set; }
+ public int ReleaseDefCount { get; set; }
+ public Dictionary WorkItemCounts { get; set; }
+ public List ErrorMessages { get; set; }
+ }
+}
diff --git a/src/ADOGenerator/Program.cs b/src/ADOGenerator/Program.cs
index 2bf1bec..0f1f594 100644
--- a/src/ADOGenerator/Program.cs
+++ b/src/ADOGenerator/Program.cs
@@ -4,7 +4,11 @@
using ADOGenerator.Services;
using Microsoft.Extensions.Configuration;
using Microsoft.Identity.Client;
+using Microsoft.VisualStudio.Services.DelegatedAuthorization;
+using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
+using RestAPI;
+using System.Text;
using System.Text.RegularExpressions;
var configuration = new ConfigurationBuilder()
@@ -13,211 +17,529 @@
.Build();
Console.WriteLine("Welcome to Azure DevOps Demo Generator! This tool will help you generate a demo environment for Azure DevOps.");
-
-try
+(string accessToken, string organizationName, string authScheme)? authenticationDetails = null;
+string authChoice = string.Empty;
+string currentPath = Directory.GetCurrentDirectory()
+ .Replace("bin\\Debug\\net8.0", "")
+ .Replace("bin\\Release\\net8.0", "")
+ .Replace("bin\\Debug", "")
+ .Replace("bin\\Release", "");
+
+do
{
- do
+ string id = Guid.NewGuid().ToString().Split('-')[0];
+ Console.ForegroundColor = ConsoleColor.Cyan;
+ Console.WriteLine("Do you want to create a new template or create a new project using the demo generator project template?");
+ Console.WriteLine("1. Create a new project using the demo generator project template");
+ Console.WriteLine("2. Generate new artifacts using an existing project.");
+ Console.ResetColor();
+ id.AddMessage(Environment.NewLine+"Enter the option number from the list of options above:");
+ var userChoiceTemplate = Console.ReadLine();
+
+ switch (userChoiceTemplate)
{
- string id = Guid.NewGuid().ToString().Split('-')[0];
- Init init = new Init();
- id.AddMessage("Template Details");
- var templatePath = Path.Combine(Directory.GetCurrentDirectory(), "Templates", "TemplateSetting.json");
+ case "1":
+ HandleNewProjectCreation(configuration, id);
+ break;
- if (!File.Exists(templatePath))
- {
- id.ErrorId().AddMessage("TemplateSettings.json file not found.");
+ case "2":
+ var (isArtifactsGenerated, template, model) = HandleArtifactGeneration(configuration, id);
+ if (isArtifactsGenerated)
+ {
+ HandleTemplateAndArtifactsUpdate(template, id, model, currentPath);
+ }
+ else
+ {
+ if(!template.Equals("skipped", StringComparison.OrdinalIgnoreCase))
+ {
+ id.ErrorId().AddMessage(Environment.NewLine + "Artifacts generation failed.");
+ }
+ }
break;
- }
- var templateSettings = File.ReadAllText(templatePath);
- var json = JObject.Parse(templateSettings);
- var groupwiseTemplates = json["GroupwiseTemplates"];
+ default:
+ Console.ForegroundColor = ConsoleColor.Red;
+ Console.WriteLine("Invalid choice. Please select either 1 or 2.");
+ Console.ResetColor();
+ continue;
+ }
- if (groupwiseTemplates == null)
+ Console.ForegroundColor = ConsoleColor.Green;
+ Console.WriteLine(Environment.NewLine+"Do you want to create another project? (yes/no): press enter to confirm");
+ Console.ResetColor();
+ var createAnotherProject = Console.ReadLine();
+ if (string.IsNullOrWhiteSpace(createAnotherProject) || createAnotherProject.Equals("yes", StringComparison.OrdinalIgnoreCase) || createAnotherProject.Equals("y", StringComparison.OrdinalIgnoreCase))
+ {
+ if (authChoice == "2")
{
- id.ErrorId().AddMessage("No templates found.");
- break;
+ authenticationDetails = null;
}
-
- int templateIndex = 1;
- var templateDictionary = new Dictionary();
-
- foreach (var group in groupwiseTemplates)
+ else
{
- var groupName = group["Groups"]?.ToString();
- Console.WriteLine(groupName);
-
- var templates = group["Template"];
- if (templates != null)
+ Console.ForegroundColor = ConsoleColor.Green;
+ Console.WriteLine("Do you want to use the existing authentication details? (yes/no): press enter to confirm");
+ Console.ResetColor();
+ var useExistingAuth = Console.ReadLine();
+ if (string.IsNullOrWhiteSpace(useExistingAuth) || useExistingAuth.Equals("yes", StringComparison.OrdinalIgnoreCase) || useExistingAuth.Equals("y", StringComparison.OrdinalIgnoreCase))
{
- foreach (var template in templates)
+ if (authenticationDetails != null)
{
- var templateName = template["Name"]?.ToString();
- Console.WriteLine($" {templateIndex}. {templateName}");
- templateDictionary.Add(templateIndex, templateName);
- templateIndex++;
+ authenticationDetails = (authenticationDetails.Value.accessToken, null, authenticationDetails.Value.authScheme);
}
}
+ else
+ {
+ authenticationDetails = null;
+ }
+ }
+ continue;
+ }
+
+ id.AddMessage("Exiting the application.");
+ Environment.Exit(0);
+
+} while (true);
+return 0;
+
+void HandleTemplateAndArtifactsUpdate(string template, string id, Project model, string currentPath)
+{
+ id.AddMessage(Environment.NewLine+"Do you want to update the template settings and move the artifacts to the executable directory? (yes/no): press enter to confirm");
+ var updateTemplateSettings = Console.ReadLine();
+
+ if (string.IsNullOrWhiteSpace(updateTemplateSettings) || updateTemplateSettings.Equals("yes", StringComparison.OrdinalIgnoreCase) || updateTemplateSettings.Equals("y", StringComparison.OrdinalIgnoreCase))
+ {
+ id.AddMessage(Environment.NewLine + "Updating template settings...");
+ var templatePathBin = Path.Combine(Directory.GetCurrentDirectory(), "Templates", "TemplateSetting.json");
+ var templatePathOriginal = Path.Combine(currentPath, "Templates", "TemplateSetting.json");
+
+ if (UpdateTemplateSettings(template, id, templatePathOriginal))
+ {
+ id.AddMessage(Environment.NewLine+"Template settings updated successfully at " + templatePathOriginal);
+ }
+ else
+ {
+ id.ErrorId().AddMessage(Environment.NewLine+"Template settings update failed at " + templatePathOriginal);
}
- id.AddMessage("Enter the template number from the list of templates above:");
- if (!int.TryParse(Console.ReadLine(), out var selectedTemplateNumber) || !templateDictionary.TryGetValue(selectedTemplateNumber, out var selectedTemplateName))
+ CopyFileIfExists(id,templatePathOriginal, templatePathBin);
+
+ id.AddMessage(Environment.NewLine+"Template settings copied to the current directory and updated successfully.");
+ id.AddMessage(Environment.NewLine + "Moving artifacts to the current directory...");
+
+ var artifactsPathOriginal = Path.Combine(currentPath, "Templates", $"CT-{model.ProjectName.Replace(" ", "-")}");
+ var artifactsPath = Path.Combine(Directory.GetCurrentDirectory(), "Templates", $"CT-{model.ProjectName.Replace(" ", "-")}");
+
+ MoveArtifacts(artifactsPathOriginal, artifactsPath, id);
+ }
+ else
+ {
+ SkipTemplateAndArtifactsUpdate(id, currentPath, model, template);
+ }
+}
+
+void CopyFileIfExists(string id,string sourcePath, string destinationPath)
+{
+ if (File.Exists(sourcePath))
+ {
+ File.Copy(sourcePath, destinationPath, true);
+ }
+ else
+ {
+ Console.ForegroundColor = ConsoleColor.Green;
+ id.AddMessage(Environment.NewLine+$"Source file '{sourcePath}' does not exist. Creating a new file at the destination.");
+ string fileContents = File.ReadAllText(sourcePath);
+ File.WriteAllText(destinationPath, fileContents);
+ id.AddMessage(Environment.NewLine + $"New file created at '{destinationPath}'.");
+ Console.ResetColor();
+ }
+}
+
+void MoveArtifacts(string sourcePath, string destinationPath, string id)
+{
+ if (Directory.Exists(sourcePath))
+ {
+ if (Directory.Exists(destinationPath))
{
- id.AddMessage("Invalid template number entered.");
- continue;
+ Directory.Delete(destinationPath, true);
}
- selectedTemplateName = selectedTemplateName.Trim();
- if (!TryGetTemplateDetails(groupwiseTemplates, selectedTemplateName, out var templateFolder, out var confirmedExtension, id))
+ Directory.CreateDirectory(destinationPath);
+
+ foreach (var directory in Directory.GetDirectories(sourcePath, "*", SearchOption.AllDirectories))
{
- id.AddMessage($"Template '{selectedTemplateName}' not found in the list.");
- id.AddMessage("Would you like to try again or exit? (type 'retry' to try again or 'exit' to quit):");
- var userChoice = Console.ReadLine();
- if (userChoice?.Equals("exit", StringComparison.OrdinalIgnoreCase) == true)
+ var relativePath = Path.GetRelativePath(sourcePath, directory);
+ var destinationDirectory = Path.Combine(destinationPath, relativePath);
+ Directory.CreateDirectory(destinationDirectory);
+ }
+
+ foreach (var file in Directory.GetFiles(sourcePath, "*", SearchOption.AllDirectories))
+ {
+ var relativePath = Path.GetRelativePath(sourcePath, file);
+ var destinationFile = Path.Combine(destinationPath, relativePath);
+ File.Copy(file, destinationFile, true);
+ }
+
+ id.AddMessage(Environment.NewLine+"Artifacts moved to the current directory.");
+ }
+ else
+ {
+ id.ErrorId().AddMessage(Environment.NewLine+"Artifacts directory not found at " + sourcePath);
+ }
+}
+
+void SkipTemplateAndArtifactsUpdate(string id, string currentPath, Project model, string template)
+{
+ var templatePathBin = Path.Combine(Directory.GetCurrentDirectory(), "Templates", "TemplateSetting.json");
+ var artifactsPathOriginal = Path.Combine(currentPath, "Templates", $"CT-{model.ProjectName.Replace(" ", "-")}");
+ var artifactsPath = Path.Combine(Directory.GetCurrentDirectory(), "Templates", $"CT-{model.ProjectName.Replace(" ", "-")}");
+ id.WarningId().AddMessage(Environment.NewLine+template);
+ id.WarningId().AddMessage(Environment.NewLine+"Copy the generated template JSON and update the template settings manually in the following file location: " + templatePathBin);
+ id.WarningId().AddMessage(Environment.NewLine+"Template settings update and copying artifacts skipped.");
+ id.WarningId().AddMessage(Environment.NewLine+"Copy the generated artifacts directory from " + artifactsPathOriginal + " and update the artifacts manually in the following directory location: " + artifactsPath);
+}
+
+
+void HandleNewProjectCreation(IConfiguration configuration, string id)
+{
+ Init init = new Init();
+ id.AddMessage(Environment.NewLine+"Template Details");
+
+ var templatePath = Path.Combine(Directory.GetCurrentDirectory(), "Templates", "TemplateSetting.json");
+ if (!File.Exists(templatePath))
+ {
+ id.ErrorId().AddMessage("TemplateSettings.json file not found.");
+ return;
+ }
+
+ var groupwiseTemplates = LoadTemplates(templatePath, id);
+ if (groupwiseTemplates == null) return;
+
+ var selectedTemplateName = SelectTemplate(groupwiseTemplates, id);
+ if (string.IsNullOrEmpty(selectedTemplateName)) return;
+
+ var templateFolder = string.Empty;
+ var confirmedExtension = false;
+ if (!TryGetTemplateDetails(groupwiseTemplates, selectedTemplateName, out templateFolder, out confirmedExtension, id))
+ {
+ return;
+ }
+
+ ValidateExtensions(templateFolder, id);
+
+ var (accessToken, organizationName, authScheme) = AuthenticateUser(init, id);
+ if (string.IsNullOrWhiteSpace(accessToken) || string.IsNullOrWhiteSpace(organizationName)) return;
+
+ var projectName = GetValidProjectName(init, id);
+ if (string.IsNullOrWhiteSpace(projectName)) return;
+
+ var project = new Project
+ {
+ id = id,
+ accessToken = accessToken,
+ accountName = organizationName,
+ ProjectName = projectName,
+ TemplateName = selectedTemplateName,
+ selectedTemplateFolder = templateFolder,
+ SelectedTemplate = templateFolder,
+ isExtensionNeeded = confirmedExtension,
+ isAgreeTerms = confirmedExtension,
+ adoAuthScheme = authScheme
+ };
+
+ CreateProjectEnvironment(project);
+}
+
+(bool,string,Project) HandleArtifactGeneration(IConfiguration configuration, string id)
+{
+ Init init = new Init();
+ var (accessToken, organizationName, authScheme) = AuthenticateUser(init, id);
+ if (string.IsNullOrWhiteSpace(accessToken) || string.IsNullOrWhiteSpace(organizationName)) return (false, string.Empty,null);
+
+ IProjectService projService = new ProjectService(configuration);
+ var projects = projService.GetProjects(organizationName, accessToken, authScheme);
+ var projectDetails = projService.SelectProject(accessToken, projects).Result;
+
+ var projectName = projectDetails[1];
+ if (string.IsNullOrWhiteSpace(projectName)) return (false, string.Empty, null);
+
+ var model = new Project
+ {
+ accountName = organizationName,
+ ProjectName = projectName,
+ ProjectId = projectDetails[0],
+ accessToken = accessToken,
+ adoAuthScheme = authScheme,
+ id = id
+ };
+
+ ITemplateService templateService = new TemplateService(configuration);
+ bool isTemplateExists = templateService.CheckTemplateExists(model);
+ string template = string.Empty;
+ if (isTemplateExists)
+ {
+ Console.ForegroundColor = ConsoleColor.Green;
+ Console.WriteLine(Environment.NewLine + "Do you still need to update the existing template?(yes/no)");
+ Console.ResetColor();
+ var updateTemplate = Console.ReadLine();
+ if (updateTemplate?.Equals("yes", StringComparison.OrdinalIgnoreCase) == true)
+ {
+ (bool isArtifactsGenerated, template, Project project) = AnalyzeAndGenerateArtifacts(model, templateService);
+ if (isArtifactsGenerated)
{
- id.AddMessage("Exiting the application.");
- return 0;
+ return (true, template, project);
}
- continue;
}
else
{
- id.AddMessage($"Selected template: {selectedTemplateName}");
- ValidateExtensions(templateFolder, id);
+ id.AddMessage(Environment.NewLine + "Template update skipped.");
+ return (false, "skipped", null);
}
+ }
+ else
+ {
+ (bool isArtifactsGenerated, template, Project project) = AnalyzeAndGenerateArtifacts(model, templateService);
+ if (isArtifactsGenerated)
+ {
+ return (true, template, project);
+ }
+ }
+ return (false, template, null);
+}
+(bool, string, Project) AnalyzeAndGenerateArtifacts(Project model, ITemplateService templateService)
+{
+ var analyzed = templateService.AnalyzeProject(model);
+ if (analyzed)
+ {
+ model.id.AddMessage("Artifacts analyzed successfully.");
+ }
+ else
+ {
+ model.id.ErrorId().AddMessage("Artifacts analysis failed.");
+ return (false, string.Empty, null);
+ }
+ Console.ForegroundColor = ConsoleColor.Green;
+ Console.WriteLine(Environment.NewLine + "Do you want to create artifacts yes/no:");
+ Console.ResetColor();
+ string response = Console.ReadLine();
+ if (response == "yes")
+ {
+ (bool isArtifactsGenerated, string template, string templateLocation) = templateService.GenerateTemplateArtifacts(model);
+ if (isArtifactsGenerated)
+ {
+ model.id.AddMessage(Environment.NewLine + "Artifacts has been generated sccessfully at the location: " + templateLocation);
+ return (true, template, model);
+ }
+ else
+ {
+ model.id.ErrorId().AddMessage(Environment.NewLine + "Artifacts generation failed.");
+ }
+ }
+ else
+ {
+ model.id.AddMessage(Environment.NewLine + "Artifacts generation skipped.");
+ return (false, "skipped", null);
+ }
+ return (false, string.Empty, null);
+}
+JToken LoadTemplates(string templatePath, string id)
+{
+ var templateSettings = File.ReadAllText(templatePath);
+ var json = JObject.Parse(templateSettings);
+ var groupwiseTemplates = json["GroupwiseTemplates"];
- id.AddMessage("Choose authentication method: 1. Device Login using AD auth 2. Personal Access Token (PAT)");
- var authChoice = Console.ReadLine();
+ if (groupwiseTemplates == null)
+ {
+ id.ErrorId().AddMessage(Environment.NewLine + "No templates found.");
+ return null;
+ }
+
+ return groupwiseTemplates;
+}
- string accessToken = string.Empty;
- string organizationName = string.Empty;
- string authScheme = string.Empty;
+string SelectTemplate(JToken groupwiseTemplates, string id)
+{
+ int templateIndex = 1;
+ var templateDictionary = new Dictionary();
- if (authChoice == "1")
+ foreach (var group in groupwiseTemplates)
+ {
+ var groupName = group["Groups"]?.ToString();
+ Console.ForegroundColor = ConsoleColor.Green;
+ Console.WriteLine(groupName);
+ Console.ResetColor();
+ var templates = group["Template"];
+ if (templates != null)
{
- try
+ Console.ForegroundColor = ConsoleColor.Yellow;
+ Console.WriteLine(new string('-', 100)); // Top border of the table
+ Console.WriteLine($"| {"Index",-10} | {"Template Name",-30} | {"Description",-50} |");
+ Console.WriteLine(new string('-', 100)); // Separator line
+ foreach (var template in templates)
{
- var app = PublicClientApplicationBuilder.Create(AuthService.clientId)
- .WithAuthority(AuthService.authority)
- .WithDefaultRedirectUri()
- .Build();
- AuthService authService = new AuthService();
+ var templateName = template["Name"]?.ToString();
+ var templateDescription = template["Description"]?.ToString();
+
+ // Wrap content to fit console width
+ var wrappedTemplateName = WrapText(templateName, 30);
+ var wrappedTemplateDescription = WrapText(templateDescription, 50);
+
+ var templateNameLines = wrappedTemplateName.Split(Environment.NewLine);
+ var templateDescriptionLines = wrappedTemplateDescription.Split(Environment.NewLine);
+ var maxLines = Math.Max(templateNameLines.Length, templateDescriptionLines.Length);
- var accounts = await app.GetAccountsAsync();
- if (accounts.Any())
+ for (int i = 0; i < maxLines; i++)
{
- try
+ var nameLine = i < templateNameLines.Length ? templateNameLines[i] : string.Empty;
+ var descriptionLine = i < templateDescriptionLines.Length ? templateDescriptionLines[i] : string.Empty;
+
+ if (i == 0)
{
- var result = await app.AcquireTokenSilent(AuthService.scopes, accounts.FirstOrDefault())
- .WithForceRefresh(true)
- .ExecuteAsync();
- accessToken = result.AccessToken;
+ Console.WriteLine($"| {templateIndex,-10} | {nameLine,-30} | {descriptionLine,-50} |");
}
- catch (Exception ex)
+ else
{
- var result = await authService.AcquireTokenAsync(app);
- accessToken = result.AccessToken;
- id.ErrorId().AddMessage($"Error: {ex.Message}");
+ Console.WriteLine($"| {"",-10} | {nameLine,-30} | {descriptionLine,-50} |");
}
}
- else
- {
- var result = await authService.AcquireTokenAsync(app);
- accessToken = result.AccessToken;
- }
- var memberId = await authService.GetProfileInfoAsync(accessToken);
- var organizations = await authService.GetOrganizationsAsync(accessToken, memberId);
- organizationName = await authService.SelectOrganization(accessToken, organizations);
- authScheme = "Bearer";
- }
- catch (Exception ex)
- {
- id.ErrorId().AddMessage($"Error: {ex.Message}");
- id.AddMessage("Exiting the application.");
- Environment.Exit(1);
+ Console.WriteLine(new string('-', 100)); // Divider after each row
+ templateDictionary.Add(templateIndex, templateName);
+ templateIndex++;
}
+ Console.ResetColor();
}
- else if (authChoice == "2")
- {
- id.AddMessage("Enter your Azure DevOps organization name:");
- organizationName = Console.ReadLine();
- if (string.IsNullOrWhiteSpace(organizationName))
- {
- id.ErrorId().AddMessage("Organization name cannot be empty.");
- continue;
- }
+ }
+ id.AddMessage(Environment.NewLine+"Enter the template number from the list of templates above:");
+ if (!int.TryParse(Console.ReadLine(), out var selectedTemplateNumber) || !templateDictionary.TryGetValue(selectedTemplateNumber, out var selectedTemplateName))
+ {
+ id.AddMessage(Environment.NewLine+"Invalid template number entered.");
+ return null;
+ }
+
+ return selectedTemplateName.Trim();
+}
+string WrapText(string text, int maxWidth)
+{
+ if (string.IsNullOrEmpty(text)) return string.Empty;
+
+ var wrappedText = new StringBuilder();
+ int currentIndex = 0;
+
+ while (currentIndex < text.Length)
+ {
+ int remainingLength = text.Length - currentIndex;
+ int lineLength = Math.Min(maxWidth, remainingLength);
+
+ // Split at the exact maxWidth if no whitespace or special character is found
+ wrappedText.AppendLine(text.Substring(currentIndex, lineLength).Trim());
+ currentIndex += lineLength;
+ }
+
+ return wrappedText.ToString().TrimEnd();
+}
- if (Uri.IsWellFormedUriString(organizationName, UriKind.Absolute))
+(string accessToken, string organizationName, string authScheme) AuthenticateUser(Init init, string id)
+{
+ string organizationName = string.Empty;
+ if (authenticationDetails.HasValue &&
+ (!string.IsNullOrWhiteSpace(authenticationDetails.Value.organizationName) ||
+ !string.IsNullOrWhiteSpace(authenticationDetails.Value.accessToken) ||
+ !string.IsNullOrWhiteSpace(authenticationDetails.Value.authScheme)))
+ {
+ if (string.IsNullOrWhiteSpace(authenticationDetails.Value.organizationName))
+ {
+ if (authChoice == "1")
{
- id.ErrorId().AddMessage("Please enter only the last part of the Azure DevOps URL (e.g., {ORGANIZATION_NAME} from https://dev.azure.com/{ORGANIZATION_NAME}).");
- continue;
+ AuthService authService = new AuthService();
+ var memberId = authService.GetProfileInfoAsync(authenticationDetails.Value.accessToken).Result;
+ var organizations = authService.GetOrganizationsAsync(authenticationDetails.Value.accessToken, memberId).Result;
+ organizationName = authService.SelectOrganization(authenticationDetails.Value.accessToken, organizations).Result;
}
-
- id.AddMessage("Enter your Azure DevOps personal access token:");
- accessToken = init.ReadSecret();
- if (string.IsNullOrWhiteSpace(accessToken))
+ else if (authChoice == "2")
{
- id.ErrorId().AddMessage("Personal access token cannot be empty.");
- continue;
+ id.AddMessage(Environment.NewLine + "Enter your Azure DevOps organization name:");
+ organizationName = Console.ReadLine();
}
-
- authScheme = "Basic";
+ authenticationDetails = (authenticationDetails.Value.accessToken, organizationName, authenticationDetails.Value.authScheme);
}
+ return authenticationDetails.Value;
+ }
+ id.AddMessage(Environment.NewLine + "Choose authentication method: 1. Device Login using AD auth 2. Personal Access Token (PAT)");
+ authChoice = Console.ReadLine();
- string projectName = "";
- do
+ string accessToken = string.Empty;
+ string authScheme = string.Empty;
+
+ if (authChoice == "1")
+ {
+ var app = PublicClientApplicationBuilder.Create(AuthService.clientId)
+ .WithAuthority(AuthService.authority)
+ .WithDefaultRedirectUri()
+ .Build();
+ AuthService authService = new AuthService();
+
+ var accounts = app.GetAccountsAsync().Result;
+ if (accounts.Any())
{
- id.AddMessage("Enter the new project name:");
- projectName = Console.ReadLine();
- if (string.IsNullOrWhiteSpace(projectName))
+ try
{
- id.ErrorId().AddMessage("Project name cannot be empty.");
- continue;
+ var result = app.AcquireTokenSilent(AuthService.scopes, accounts.FirstOrDefault())
+ .WithForceRefresh(true)
+ .ExecuteAsync().Result;
+ accessToken = result.AccessToken;
}
- if (!init.CheckProjectName(projectName))
+ catch (MsalUiRequiredException)
{
- id.ErrorId().AddMessage("Validation error: Project name is not valid.");
- id.AddMessage("Do you want to try with a valid project name or exit? (type 'retry' to try again or 'exit' to quit):");
- var userChoice = Console.ReadLine();
- if (userChoice?.Equals("exit", StringComparison.OrdinalIgnoreCase) == true)
- {
- id.AddMessage("Exiting the application.");
- Environment.Exit(1);
- }
- projectName = "";
- continue;
+ var result = authService.AcquireTokenAsync(app).Result;
+ accessToken = result.AccessToken;
}
- } while (string.IsNullOrWhiteSpace(projectName));
-
- if (string.IsNullOrWhiteSpace(organizationName) || string.IsNullOrWhiteSpace(accessToken) || string.IsNullOrWhiteSpace(projectName))
+ }
+ else
{
- id.ErrorId().AddMessage("Validation error: All inputs must be provided. Exiting..");
- Environment.Exit(1);
+ var result = authService.AcquireTokenAsync(app).Result;
+ accessToken = result.AccessToken;
}
- var project = new Project
- {
- id = id,
- accessToken = accessToken,
- accountName = organizationName,
- ProjectName = projectName,
- TemplateName = selectedTemplateName,
- selectedTemplateFolder = templateFolder,
- SelectedTemplate = templateFolder,
- isExtensionNeeded = confirmedExtension,
- isAgreeTerms = confirmedExtension,
- adoAuthScheme = authScheme
- };
-
- CreateProjectEnvironment(project);
-
- } while (true);
+ var memberId = authService.GetProfileInfoAsync(accessToken).Result;
+ var organizations = authService.GetOrganizationsAsync(accessToken, memberId).Result;
+ organizationName = authService.SelectOrganization(accessToken, organizations).Result;
+ authScheme = "Bearer";
+ }
+ else if (authChoice == "2")
+ {
+ id.AddMessage(Environment.NewLine + "Enter your Azure DevOps organization name:");
+ organizationName = Console.ReadLine();
+
+ id.AddMessage(Environment.NewLine + "Enter your Azure DevOps personal access token:");
+ accessToken = init.ReadSecret();
+
+ authScheme = "Basic";
+ }
+
+ authenticationDetails = (accessToken, organizationName, authScheme);
+ return authenticationDetails.Value;
}
-catch (Exception ex)
+
+string GetValidProjectName(Init init, string id)
{
- Console.WriteLine($"An error occurred: {ex.Message}");
- Console.WriteLine("Exiting the application.");
- Environment.Exit(1);
+ string projectName = "";
+ do
+ {
+ id.AddMessage(Environment.NewLine + "Enter the new project name:");
+ projectName = Console.ReadLine();
+ if (!init.CheckProjectName(projectName))
+ {
+ id.ErrorId().AddMessage(Environment.NewLine+"Validation error: Project name is not valid.");
+ id.AddMessage(Environment.NewLine + "Do you want to try with a valid project name or exit? (type 'retry' to try again or 'exit' to quit):");
+ var userChoice = Console.ReadLine();
+ if (userChoice?.Equals("exit", StringComparison.OrdinalIgnoreCase) == true)
+ {
+ id.AddMessage(Environment.NewLine + "Exiting the application.");
+ Environment.Exit(1);
+ }
+ projectName = "";
+ }
+ } while (string.IsNullOrWhiteSpace(projectName));
+ return projectName;
}
bool TryGetTemplateDetails(JToken groupwiseTemplates, string selectedTemplateName, out string templateFolder, out bool confirmedExtension, string id)
@@ -292,23 +614,23 @@ bool ValidateExtensions(string templateFolderPath, string id)
if (extensions.HasValues)
{
- id.AddMessage("Do you want to proceed with this extension? (yes/No): press enter to confirm");
+ id.AddMessage(Environment.NewLine + "Do you want to proceed with this extension? (yes/No): press enter to confirm");
var userConfirmation = Console.ReadLine();
if (string.IsNullOrWhiteSpace(userConfirmation) || (userConfirmation.Equals("yes", StringComparison.OrdinalIgnoreCase) || userConfirmation.Equals("y", StringComparison.OrdinalIgnoreCase)))
{
Console.ForegroundColor = ConsoleColor.Yellow;
- id.AddMessage("Agreed for license? (yes/no): press enter to confirm");
+ id.AddMessage(Environment.NewLine + "Agreed for license? (yes/no): press enter to confirm");
Console.ResetColor();
var licenseConfirmation = Console.ReadLine();
if (string.IsNullOrWhiteSpace(licenseConfirmation) || (licenseConfirmation.Equals("yes", StringComparison.OrdinalIgnoreCase) || licenseConfirmation.Equals("y", StringComparison.OrdinalIgnoreCase)))
{
- id.AddMessage("Confirmed Extension installation");
+ id.AddMessage(Environment.NewLine + "Confirmed Extension installation");
return true;
}
}
else
{
- id.AddMessage("Extension installation is not confirmed.");
+ id.AddMessage(Environment.NewLine + "Extension installation is not confirmed.");
}
}
@@ -317,42 +639,103 @@ bool ValidateExtensions(string templateFolderPath, string id)
void CreateProjectEnvironment(Project model)
{
- try
+ Console.WriteLine($"Creating project '{model.ProjectName}' in organization '{model.accountName}' using template from '{model.TemplateName}'...");
+ var projectService = new ProjectService(configuration);
+ var result = projectService.CreateProjectEnvironment(model);
+ if (result)
{
- Console.WriteLine($"Creating project '{model.ProjectName}' in organization '{model.accountName}' using template from '{model.TemplateName}'...");
- var projectService = new ProjectService(configuration);
- var result = projectService.CreateProjectEnvironment(model);
- if (result)
- {
- Console.WriteLine("Project created successfully.");
- }
- else
- {
- Console.WriteLine("Project creation failed.");
- }
- Console.WriteLine("Do you want to create another project? (yes/no): press enter to confirm");
- var createAnotherProject = Console.ReadLine();
- if (string.IsNullOrWhiteSpace(createAnotherProject) || createAnotherProject.Equals("yes", StringComparison.OrdinalIgnoreCase) || createAnotherProject.Equals("y", StringComparison.OrdinalIgnoreCase))
- {
- createAnotherProject = "yes";
- }
- else
- {
- createAnotherProject = "no";
- }
- Console.WriteLine();
- if (createAnotherProject.Equals("no", StringComparison.OrdinalIgnoreCase))
+ Console.ForegroundColor = ConsoleColor.Green;
+ Console.WriteLine("Project created successfully.");
+ Console.ResetColor();
+ }
+ else
+ {
+ Console.ForegroundColor = ConsoleColor.Red;
+ Console.WriteLine("Project creation failed.");
+ Console.ResetColor();
+ }
+}
+bool UpdateTemplateSettings(string template, string id, string templatePath)
+{
+ if (!File.Exists(templatePath))
+ {
+ id.ErrorId().AddMessage(Environment.NewLine+"TemplateSettings.json file not found at " + templatePath);
+ return false;
+ }
+
+ var templateSettings = File.ReadAllText(templatePath);
+ var json = JObject.Parse(templateSettings);
+
+ UpdateGroups(json, id);
+ UpdateGroupwiseTemplates(json, template, id);
+
+ File.WriteAllText(templatePath, JsonConvert.SerializeObject(json, Formatting.Indented));
+ return true;
+}
+
+void UpdateGroups(JObject json, string id)
+{
+ var groups = json["Groups"] as JArray ?? new JArray(json["Groups"]);
+ if (!groups.Any(g => g.ToString().Equals("Custom Templates", StringComparison.OrdinalIgnoreCase)))
+ {
+ var customTemplates = new JArray { "Custom Templates" };
+ foreach (var item in customTemplates)
{
- model.id.AddMessage("Exiting the application.");
- Environment.Exit(0);
+ groups.Add(item);
}
+ json["Groups"] = groups;
}
- catch (Exception ex)
+ else
{
- Console.WriteLine($"An error occurred while creating the project: {ex.Message}");
- model.id.AddMessage("Exiting the application.");
- Environment.Exit(1);
+ id.AddMessage("Custom Templates group already exists.");
}
}
-return 0;
+void UpdateGroupwiseTemplates(JObject json, string template, string id)
+{
+ var groupwiseTemplates = json["GroupwiseTemplates"] as JArray ?? new JArray(json["GroupwiseTemplates"]);
+ var newCustomTemplate = JObject.Parse(template);
+ var groupwiseCustomTemplates = newCustomTemplate["GroupwiseTemplates"];
+
+ if (groupwiseTemplates != null && groupwiseCustomTemplates != null)
+ {
+ foreach (var group in groupwiseCustomTemplates)
+ {
+ var groupName = group["Groups"]?.ToString();
+ var existingGroup = groupwiseTemplates.FirstOrDefault(g => g["Groups"]?.ToString().Equals(groupName, StringComparison.OrdinalIgnoreCase) == true);
+
+ if (existingGroup != null)
+ {
+ MergeTemplates(existingGroup, group, id);
+ }
+ else
+ {
+ groupwiseTemplates.Add(group);
+ }
+ }
+ }
+}
+
+void MergeTemplates(JToken existingGroup, JToken newGroup, string id)
+{
+ var existingTemplates = existingGroup["Template"] as JArray ?? new JArray(existingGroup["Template"]);
+ var newTemplates = newGroup["Template"] as JArray ?? new JArray(newGroup["Template"]);
+
+ var existingTemplateNames = existingTemplates.Select(t => t["Name"]?.ToString()).ToList();
+ var newTemplateNames = newTemplates.Select(t => t["Name"]?.ToString()).ToList();
+
+ var duplicateTemplates = newTemplateNames.Where(t => existingTemplateNames.Contains(t)).ToList();
+ if (duplicateTemplates.Any())
+ {
+ id.AddMessage($"Duplicate templates found: {string.Join(", ", duplicateTemplates)}. Skipping these templates.");
+ newTemplates = new JArray(newTemplates.Where(t => !duplicateTemplates.Contains(t["Name"]?.ToString())));
+ }
+ else
+ {
+ id.AddMessage($"No duplicate templates found. Adding new templates.");
+ }
+ foreach (var templateCustom in newTemplates)
+ {
+ existingTemplates.Add(templateCustom);
+ }
+}
\ No newline at end of file
diff --git a/src/ADOGenerator/ServiceExtensions.cs b/src/ADOGenerator/ServiceExtensions.cs
index 64033ad..c64d97b 100644
--- a/src/ADOGenerator/ServiceExtensions.cs
+++ b/src/ADOGenerator/ServiceExtensions.cs
@@ -26,6 +26,11 @@ public static string ErrorId(this string str)
str = str + "_Errors";
return str;
}
+ public static string WarningId(this string str)
+ {
+ str = str + "_Warning";
+ return str;
+ }
public static void AddMessage(this string id, string message)
{
@@ -40,8 +45,7 @@ public static void AddMessage(this string id, string message)
string fileName = $"{DateTime.Now.ToString("yyyy-MM-dd")}-{id}.txt";
if (id.EndsWith("_Errors"))
{
- // Create Errors Log Directory if not exists
- if (!Directory.Exists(Path.Combine(logFilePath, "Errors")))
+ if(!Directory.Exists(Path.Combine(logFilePath, "Errors")))
{
Directory.CreateDirectory(Path.Combine(logFilePath, "Errors"));
}
@@ -63,7 +67,7 @@ public static void AddMessage(this string id, string message)
}
File.AppendAllLines(Path.Combine(logFilePath, fileName), new string[] { message });
// Create Log file
- Console.ForegroundColor = ConsoleColor.Green;
+ Console.ForegroundColor = id.EndsWith("_Warning") ? ConsoleColor.Yellow : ConsoleColor.Green;
Console.WriteLine(message);
Console.ResetColor();
}
diff --git a/src/ADOGenerator/Services/AuthService.cs b/src/ADOGenerator/Services/AuthService.cs
index bccf493..0c0ae0b 100644
--- a/src/ADOGenerator/Services/AuthService.cs
+++ b/src/ADOGenerator/Services/AuthService.cs
@@ -69,17 +69,47 @@ public async Task SelectOrganization(string accessToken, JObject account
{
if (accountsJson["count"].Value() > 0)
{
- Console.WriteLine("Select an organization:");
+ Console.ForegroundColor = ConsoleColor.Green;
+ Console.WriteLine(Environment.NewLine + "Select an organization:");
+ Console.ResetColor();
var accounts = accountsJson["value"];
+ Console.ForegroundColor = ConsoleColor.Yellow;
+ Console.WriteLine("+-----+-------------------------------+--------------------------------------+");
+ Console.WriteLine("| No | Organization Name | Organization ID |");
+ Console.WriteLine("+-----+-------------------------------+--------------------------------------+");
for (int i = 0; i < accounts.Count(); i++)
{
- Console.WriteLine($"{i + 1}. {accounts[i]["accountName"]} (ID: {accounts[i]["accountId"]})");
+ string serialNo = (i + 1).ToString();
+ string accountName = accounts[i]["accountName"].ToString();
+ string accountId = accounts[i]["accountId"].ToString();
+
+ // Ensure proper wrapping and alignment
+ accountName = accountName.Length > 25 ? accountName.Substring(0, 22) + "..." : accountName.PadRight(25);
+
+ Console.WriteLine($"| {serialNo.PadRight(3)} | {accountName} | {accountId} |");
+
+ // Wrap to the next line if content exceeds the table width
+ if (accounts[i]["accountName"].ToString().Length > 25 || accounts[i]["accountId"].ToString().Length > 15)
+ {
+ string wrappedAccountName = accounts[i]["accountName"].ToString().Length > 25
+ ? accounts[i]["accountName"].ToString().Substring(25)
+ : string.Empty;
+
+ if (!string.IsNullOrEmpty(wrappedAccountName))
+ {
+ Console.WriteLine($"| | {wrappedAccountName.PadRight(25)} | |");
+ }
+ }
}
+ Console.WriteLine("+-----+-------------------------------+--------------------------------------+");
+ Console.ResetColor();
int selectedIndex;
do
{
- Console.Write("Enter the number of the organization: ");
+ Console.ForegroundColor = ConsoleColor.Green;
+ Console.Write(Environment.NewLine + "Enter the number of the organization: ");
+ Console.ResetColor();
} while (!int.TryParse(Console.ReadLine(), out selectedIndex) || selectedIndex < 1 || selectedIndex > accounts.Count());
var selectedAccountId = accounts[selectedIndex - 1]["accountId"].ToString();
diff --git a/src/ADOGenerator/Services/ExtractorService.cs b/src/ADOGenerator/Services/ExtractorService.cs
new file mode 100644
index 0000000..6a431e8
--- /dev/null
+++ b/src/ADOGenerator/Services/ExtractorService.cs
@@ -0,0 +1,1660 @@
+using ADOGenerator.IServices;
+using ADOGenerator.Models;
+using log4net;
+using Newtonsoft.Json.Linq;
+using Newtonsoft.Json;
+using RestAPI.DeliveryPlans;
+using RestAPI.ExtensionManagement;
+using RestAPI.Extractor;
+using RestAPI.ProjectsAndTeams;
+using RestAPI.Viewmodel.Extractor;
+using RestAPI.Viewmodel.GitHub;
+using RestAPI.Viewmodel.Plans;
+using RestAPI.Viewmodel.ProjectAndTeams;
+using RestAPI;
+using System;
+using System.Collections.Generic;
+using System.Configuration;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using static RestAPI.Viewmodel.Extractor.GetServiceEndpoints;
+using static RestAPI.Viewmodel.Plans.DeliveryPlans;
+using Configuration = RestAPI.ADOConfiguration;
+using RestAPI.QueriesAndWidgets;
+using Microsoft.Extensions.Configuration;
+using static ADOGenerator.Models.TemplateSelection;
+//using ADOProjectConfigurations = ADOGenerator.Models.ADOProjectConfigurations;
+
+namespace ADOGenerator.Services
+{
+ public class ExtractorService : IExtractorService
+ {
+ #region STATIC DECLARATIONS
+ private readonly IConfiguration _config;
+ public static ILog logger = LogManager.GetLogger("ErrorLog");
+ public static readonly object objLock = new object();
+ public static Dictionary statusMessages;
+ public List errorMessages = new List();
+ public static string extractedTemplatePath = string.Empty;
+ private ProjectProperties.Properties projectProperties = new ProjectProperties.Properties();
+ public static string currentPath = Directory.GetCurrentDirectory().Replace("bin\\Debug\\net8.0", "").Replace("bin\\Release\\net8.0", "").Replace("bin\\Debug", "").Replace("bin\\Release", "");
+
+ public ExtractorService(IConfiguration config)
+ {
+ _config = config;
+ }
+ #endregion STATIC DECLARATIONS
+
+ #region ANALYSIS - GET COUNTS
+ public ProjectConfigurations ProjectConfiguration(Project model)
+ {
+ string repoVersion = _config["AppSettings:RepoVersion"];
+ string buildVersion = _config["AppSettings:BuildVersion"];
+ string releaseVersion = _config["AppSettings:ReleaseVersion"];
+ string wikiVersion = _config["AppSettings:WikiVersion"];
+ string boardVersion = _config["AppSettings:BoardVersion"];
+ string workItemsVersion = _config["AppSettings:WorkItemsVersion"];
+ string releaseHost = _config["AppSettings:ReleaseHost"];
+ string defaultHost = _config["AppSettings:DefaultHost"];
+ string extensionHost = _config["AppSettings:ExtensionHost"];
+ string getReleaseVersion = _config["AppSettings:GetRelease"];
+ string agentQueueVersion = _config["AppSettings:AgentQueueVersion"];
+ string extensionVersion = _config["AppSettings:ExtensionVersion"];
+ string endpointVersion = _config["AppSettings:EndPointVersion"];
+ string queriesVersion = _config["AppSettings:QueriesVersion"];
+ string variableGroupsApiVersion = _config["AppSettings:VariableGroupsApiVersion"];
+ ProjectConfigurations projectConfig = new ProjectConfigurations();
+
+ projectConfig.AgentQueueConfig = new Configuration() { UriString = defaultHost + model.accountName + "/", PersonalAccessToken = model.accessToken, Project = model.ProjectName, AccountName = model.accountName, Id = model.id, VersionNumber = wikiVersion, _adoAuthScheme = model.adoAuthScheme };
+ projectConfig.WorkItemConfig = new Configuration() { UriString = defaultHost + model.accountName + "/", PersonalAccessToken = model.accessToken, Project = model.ProjectName, AccountName = model.accountName, Id = model.id, VersionNumber = wikiVersion, _adoAuthScheme = model.adoAuthScheme };
+ projectConfig.BuildDefinitionConfig = new Configuration() { UriString = defaultHost + model.accountName + "/", PersonalAccessToken = model.accessToken, Project = model.ProjectName, AccountName = model.accountName, Id = model.id, VersionNumber = buildVersion, _adoAuthScheme = model.adoAuthScheme };
+ projectConfig.ReleaseDefinitionConfig = new Configuration() { UriString = releaseHost + model.accountName + "/", PersonalAccessToken = model.accessToken, Project = model.ProjectName, AccountName = model.accountName, Id = model.id, VersionNumber = releaseVersion, _adoAuthScheme = model.adoAuthScheme };
+ projectConfig.RepoConfig = new Configuration() { UriString = defaultHost + model.accountName + "/", PersonalAccessToken = model.accessToken, Project = model.ProjectName, AccountName = model.accountName, Id = model.id, VersionNumber = repoVersion, _adoAuthScheme = model.adoAuthScheme };
+ projectConfig.BoardConfig = new Configuration() { UriString = defaultHost + model.accountName + "/", PersonalAccessToken = model.accessToken, Project = model.ProjectName, AccountName = model.accountName, Id = model.id, VersionNumber = boardVersion, _adoAuthScheme = model.adoAuthScheme };
+ projectConfig.Config = new Configuration() { UriString = defaultHost + model.accountName + "/", PersonalAccessToken = model.accessToken, Project = model.ProjectName, AccountName = model.accountName, Id = model.id, _adoAuthScheme = model.adoAuthScheme };
+ projectConfig.GetReleaseConfig = new Configuration() { UriString = releaseHost + model.accountName + "/", PersonalAccessToken = model.accessToken, Project = model.ProjectName, AccountName = model.accountName, Id = model.id, VersionNumber = getReleaseVersion, _adoAuthScheme = model.adoAuthScheme };
+ projectConfig.ExtensionConfig = new Configuration() { UriString = extensionHost + model.accountName + "/", PersonalAccessToken = model.accessToken, Project = model.ProjectName, AccountName = model.accountName, Id = model.id, VersionNumber = extensionVersion, _adoAuthScheme = model.adoAuthScheme };
+ projectConfig.EndpointConfig = new Configuration() { UriString = defaultHost + model.accountName + "/", PersonalAccessToken = model.accessToken, Project = model.ProjectName, AccountName = model.accountName, Id = model.id, VersionNumber = endpointVersion, _adoAuthScheme = model.adoAuthScheme };
+ projectConfig.QueriesConfig = new Configuration() { UriString = defaultHost + model.accountName + "/", PersonalAccessToken = model.accessToken, Project = model.ProjectName, AccountName = model.accountName, Id = model.id, VersionNumber = queriesVersion, _adoAuthScheme = model.adoAuthScheme };
+ projectConfig.VariableGroupConfig = new Configuration() { UriString = defaultHost + model.accountName + "/", PersonalAccessToken = model.accessToken, Project = model.ProjectName, AccountName = model.accountName, Id = model.id, VersionNumber = variableGroupsApiVersion, _adoAuthScheme = model.adoAuthScheme };
+
+ return projectConfig;
+ }
+ public bool IsTemplateExists(string templateName)
+ {
+ string templatesDirectory = currentPath + @"Templates\";
+ string templateSettingsPath = templatesDirectory + "TemplateSetting.json";
+ if (!File.Exists(templateSettingsPath))
+ {
+ return false;
+ }
+ string templateSettingsContent = File.ReadAllText(templateSettingsPath);
+ JObject templateSettings = JObject.Parse(templateSettingsContent);
+ // Check if the template name exists in the settings by looping the GroupwiseTemplates
+ bool templateExists = false;
+ string templateFolder = string.Empty;
+ foreach (var group in templateSettings["GroupwiseTemplates"])
+ {
+ var templates = group["Template"];
+ foreach (var template in templates)
+ {
+ if (template["Name"].ToString() == templateName)
+ {
+ templateExists = true;
+ templateFolder = template["TemplateFolder"].ToString();
+ break;
+ }
+ }
+ if (templateExists)
+ {
+ break;
+ }
+ }
+ if(!templateExists)
+ {
+ return false;
+ }
+ string templatePath = templatesDirectory + templateFolder;
+ if (Directory.Exists(templatePath))
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ public int GetTeamsCount(ProjectConfigurations appConfig)
+ {
+ RestAPI.Extractor.ClassificationNodes nodes = new RestAPI.Extractor.ClassificationNodes(appConfig.BoardConfig);
+ TeamList teamList = nodes.ExportTeamList("");
+ int count = 0;
+ if (teamList.value != null)
+ {
+ count = teamList.value.Count;
+ }
+ return count;
+ }
+ public int GetIterationsCount(ProjectConfigurations appConfig)
+ {
+ RestAPI.Extractor.ClassificationNodes nodes = new RestAPI.Extractor.ClassificationNodes(appConfig.BoardConfig);
+ GetINumIteration.Iterations iterations = new GetINumIteration.Iterations();
+ iterations = nodes.GetiterationCount();
+ if (iterations.count > 0)
+ {
+ return iterations.count;
+ }
+ else
+ {
+ if (!(string.IsNullOrEmpty(nodes.LastFailureMessage)))
+ {
+ errorMessages.Add("Error while fetching iteration(s) count: " + nodes.LastFailureMessage);
+ }
+ return 0;
+ }
+ }
+ public int GetBuildDefinitionCount(ProjectConfigurations appConfig)
+ {
+ int BuildDefCount = 0;
+ BuildandReleaseDefs buildandReleaseDefs = new BuildandReleaseDefs(appConfig.BuildDefinitionConfig);
+ GetBuildDefResponse.BuildDef buildDef = new GetBuildDefResponse.BuildDef();
+ buildDef = buildandReleaseDefs.GetBuildDefCount();
+ if (buildDef.count > 0)
+ {
+ BuildDefCount = buildDef.count;
+ }
+ else if (!string.IsNullOrEmpty(buildandReleaseDefs.LastFailureMessage))
+ {
+ errorMessages.Add("Error while fetching build definition count: " + buildandReleaseDefs.LastFailureMessage);
+ }
+ return BuildDefCount;
+ }
+ public int GetReleaseDefinitionCount(ProjectConfigurations appConfig)
+ {
+ int ReleaseDefCount = 0;
+ BuildandReleaseDefs buildandReleaseDefs = new BuildandReleaseDefs(appConfig.ReleaseDefinitionConfig);
+ GetReleaseDefResponse.ReleaseDef releaseDef = new GetReleaseDefResponse.ReleaseDef();
+ releaseDef = buildandReleaseDefs.GetReleaseDefCount();
+ if (releaseDef.count > 0)
+ {
+ ReleaseDefCount = releaseDef.count;
+ }
+ else if (!string.IsNullOrEmpty(buildandReleaseDefs.LastFailureMessage))
+ {
+ errorMessages.Add("Error while fetching release definition count: " + buildandReleaseDefs.LastFailureMessage);
+ }
+ return ReleaseDefCount;
+ }
+ public Dictionary GetWorkItemsCount(ProjectConfigurations appConfig)
+ {
+ string[] workItemtypes = GetAllWorkItemsName(appConfig);//{ "Epic", "Feature", "Product Backlog Item", "Task", "Test Case", "Bug", "User Story", "Test Suite", "Test Plan", "Issue" };
+ GetWorkItemsCount itemsCount = new GetWorkItemsCount(appConfig.WorkItemConfig);
+ Dictionary fetchedWorkItemsCount = new Dictionary();
+ if (workItemtypes.Length > 0)
+ {
+ foreach (var workItem in workItemtypes)
+ {
+ itemsCount.LastFailureMessage = "";
+ WorkItemFetchResponse.WorkItems WITCount = itemsCount.GetWorkItemsfromSource(workItem);
+ if (WITCount.count > 0)
+ {
+ fetchedWorkItemsCount.Add(workItem, WITCount.count);
+ }
+ else if (!string.IsNullOrEmpty(itemsCount.LastFailureMessage))
+ {
+ errorMessages.Add(string.Format("Error while querying work item - {0}: {1}", workItem, itemsCount.LastFailureMessage));
+ }
+ }
+ }
+
+ return fetchedWorkItemsCount;
+ }
+ #endregion ANALYSIS - GET COUNTS
+
+ #region GENERATE ARTIFACTS
+ public string[] GenerateTemplateArifacts(Project model)
+ {
+ model.id.AddMessage(Environment.NewLine+"Template Generation Started");
+ extractedTemplatePath = currentPath + @"Templates\";
+ string extractedFolderName = extractedTemplatePath + $"CT-{model.ProjectName.Replace(" ", "-")}";
+ if (Directory.Exists(extractedFolderName))
+ {
+ Directory.Delete(extractedFolderName, true);
+ }
+ ProjectConfigurations appConfig = ProjectConfiguration(model);
+
+ GetInstalledExtensions(appConfig, extractedFolderName);
+
+ ExportQuries(appConfig, extractedFolderName);
+ ExportTeams(appConfig.BoardConfig, model, extractedFolderName);
+
+ if (ExportIterations(appConfig, extractedFolderName))
+ {
+ model.id.AddMessage("Iterations Definition Exported");
+ }
+ string filePathToRead = currentPath + @"\\PreSetting";
+
+ string projectSetting = "";
+ projectSetting = filePathToRead + "\\ProjectSettings.json";
+ projectSetting = File.ReadAllText(projectSetting);
+ projectSetting = projectSetting.Replace("$type$", model.ProcessTemplate).Replace("$id$", projectProperties.value.Where(x => x.name == "System.ProcessTemplateType").FirstOrDefault().value);
+ File.WriteAllText(extractedFolderName + "\\ProjectSettings.json", projectSetting);
+
+ string projectTemplate = "";
+ projectTemplate = filePathToRead + "\\ProjectTemplate.json";
+ projectTemplate = File.ReadAllText(projectTemplate);
+ File.WriteAllText(extractedFolderName + "\\ProjectTemplate.json", projectTemplate);
+
+ string teamArea = "";
+ teamArea = filePathToRead + "\\TeamArea.json";
+ teamArea = File.ReadAllText(teamArea);
+ File.WriteAllText(extractedFolderName + "\\TeamArea.json", teamArea);
+ model.id.AddMessage("Team Areas Exported");
+
+ if(ExportWorkItems(appConfig, extractedFolderName))
+ model.id.AddMessage("Work Items Exported");
+
+ if(ExportDeliveryPlans(appConfig, extractedFolderName))
+ model.id.AddMessage("Delivery Plans Exported");
+
+ if(ExportRepositoryList(appConfig, extractedFolderName))
+ model.id.AddMessage("Repository Exported");
+
+ GetServiceEndpoints(appConfig, extractedFolderName);
+ int count = GetBuildDefinitions(appConfig, extractedFolderName);
+ if (count >= 1)
+ {
+ model.id.AddMessage("Build Definition Exported");
+ }
+
+ int relCount = GeneralizingGetReleaseDefinitions(appConfig, extractedFolderName);
+ if (relCount >= 1)
+ {
+ model.id.AddMessage("Release Definition Exported");
+ }
+
+ // Generate custom template JSON
+ var customTemplateJson = new
+ {
+ Groups = new[]
+ {
+ "Custom Templates"
+ },
+ GroupwiseTemplates = new[]
+ {
+ new
+ {
+ Groups = "Custom Templates",
+ Template = new[]
+ {
+ new
+ {
+ Name = model.ProjectName,
+ TemplateFolder = $"CT-{model.ProjectName.Replace(" ", "-")}",
+ Description = $"This is a custom template for the project '{model.ProjectName}'.",
+ }
+ }
+ }
+ }
+ };
+ return new string[] { model.id, JsonConvert.SerializeObject(customTemplateJson, Formatting.Indented) , extractedFolderName };
+ }
+ public List GetInstalledExtensions(ProjectConfigurations appConfig,string extractedFolderName)
+ {
+ try
+ {
+ GetListExtenison listExtenison = new GetListExtenison(appConfig.ExtensionConfig);
+ List extensionList = new List();
+ GetExtensions.ExtensionsList returnExtensionsList = listExtenison.GetInstalledExtensions();
+ if (returnExtensionsList != null && returnExtensionsList.count > 0)
+ {
+ List builtInExtensions = returnExtensionsList.value.Where(x => x.flags == null).ToList();
+ List trustedExtensions = returnExtensionsList.value.Where(x => x.flags != null && x.flags.ToString() == "trusted").ToList();
+ builtInExtensions.AddRange(trustedExtensions);
+ returnExtensionsList.value = builtInExtensions;
+
+ foreach (GetExtensions.Value data in returnExtensionsList.value)
+ {
+ RequiredExtensions.ExtensionWithLink extension = new RequiredExtensions.ExtensionWithLink();
+ if (data.extensionName.ToLower() != "analytics")
+ {
+ extension.extensionId = data.extensionId;
+ extension.extensionName = data.extensionName;
+ extension.publisherId = data.publisherId;
+ extension.publisherName = data.publisherName;
+ extension.link = "" + data.extensionName + "";
+ extension.License = "License Terms";
+ extensionList.Add(extension);
+ }
+ }
+ RequiredExtensions.listExtension listExtension = new RequiredExtensions.listExtension();
+ if (extensionList.Count > 0)
+ {
+ listExtension.Extensions = extensionList;
+ if (!Directory.Exists(extractedFolderName))
+ {
+ Directory.CreateDirectory(extractedFolderName);
+ }
+ string fetchedJson = JsonConvert.SerializeObject(listExtension, Formatting.Indented);
+
+ File.WriteAllText(extractedFolderName + "\\Extensions.json", JsonConvert.SerializeObject(listExtension, Formatting.Indented));
+ appConfig.ExtensionConfig.Id.AddMessage("Extensions Exported");
+ }
+ }
+ else if (!string.IsNullOrEmpty(listExtenison.LastFailureMessage))
+ {
+ appConfig.ExtensionConfig.Id.ErrorId().AddMessage("Some error occured while fetching extensions"+Environment.NewLine+listExtenison.LastFailureMessage);
+ }
+ return extensionList;
+ }
+ catch (Exception ex)
+ {
+ appConfig.ExtensionConfig.Id.ErrorId().AddMessage(DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss") + "\t" + ex.Message + "\n" + ex.StackTrace + "\n");
+ }
+ return new List();
+ }
+ public void ExportQuries(ProjectConfigurations appConfig, string extractedFolderName)
+ {
+ try
+ {
+ Queries queries = new Queries(appConfig.QueriesConfig);
+ GetQueries.Queries listQueries = queries.GetQueriesWiql();
+ if (listQueries.count > 0)
+ {
+ foreach (var _queries in listQueries.value)
+ {
+ if (_queries.hasChildren)
+ {
+ foreach (var query in _queries.children)
+ {
+ if (!query.hasChildren)
+ {
+ if (query.wiql != null)
+ {
+ query.wiql = query.wiql.Replace(appConfig.QueriesConfig.Project, "$projectId$");
+ JObject jobj = new JObject();
+ jobj["name"] = query.name;
+ jobj["wiql"] = query.wiql;
+ if (!Directory.Exists(extractedFolderName + "\\Dashboard\\Queries"))
+ {
+ Directory.CreateDirectory(extractedFolderName + "\\Dashboard");
+ File.WriteAllText(extractedFolderName + "\\Dashboard\\Dashboard.json", JsonConvert.SerializeObject("text", Formatting.Indented));
+ }
+ if (!Directory.Exists(extractedFolderName + "\\Dashboard\\Queries"))
+ {
+ Directory.CreateDirectory(extractedFolderName + "\\Dashboard\\Queries");
+ File.WriteAllText(extractedFolderName + "\\Dashboard\\Queries\\" + query.name + ".json", JsonConvert.SerializeObject(jobj, Formatting.Indented));
+ }
+ else
+ {
+ File.WriteAllText(extractedFolderName + "\\Dashboard\\Queries\\" + query.name + ".json", JsonConvert.SerializeObject(jobj, Formatting.Indented));
+ }
+ }
+ }
+ else
+ {
+ foreach (var child1 in query.children)
+ {
+ if (child1.wiql != null)
+ {
+ child1.wiql = child1.wiql.Replace(appConfig.QueriesConfig.Project, "$projectId$");
+ JObject jobj = new JObject();
+ jobj["name"] = child1.name;
+ jobj["wiql"] = child1.wiql;
+ if (!Directory.Exists(extractedFolderName + "\\Dashboard\\Queries"))
+ {
+ Directory.CreateDirectory(extractedFolderName + "\\Dashboard\\Queries");
+
+ File.WriteAllText(extractedFolderName + "\\Dashboard\\Queries\\" + child1.name + ".json", JsonConvert.SerializeObject(jobj, Formatting.Indented));
+ }
+ else
+ {
+ File.WriteAllText(extractedFolderName + "\\Dashboard\\Queries\\" + child1.name + ".json", JsonConvert.SerializeObject(jobj, Formatting.Indented));
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ appConfig.QueriesConfig.Id.AddMessage("Queries Exported");
+ }
+ else if (!string.IsNullOrEmpty(queries.LastFailureMessage))
+ {
+ appConfig.QueriesConfig.Id.ErrorId().AddMessage("Error while fetching queries");
+ }
+ }
+ catch (Exception ex)
+ {
+ appConfig.QueriesConfig.Id.ErrorId().AddMessage(DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss") + "\t" + ex.Message + "\n" + ex.StackTrace + "\n");
+ }
+
+ }
+ public bool ExportTeams(Configuration con, Project model,string extractedFolderName)
+ {
+ try
+ {
+ string defaultTeamID = string.Empty;
+ RestAPI.Extractor.ClassificationNodes nodes = new RestAPI.Extractor.ClassificationNodes(con);
+ TeamList _team = new TeamList();
+
+ string defaultHost = _config["AppSettings:DefaultHost"];
+ string ProjectPropertyVersion = _config["AppSettings:ProjectPropertyVersion"];
+ ADOConfiguration config = new ADOConfiguration() { AccountName = model.accountName, PersonalAccessToken = model.accessToken, UriString = defaultHost + model.accountName, VersionNumber = ProjectPropertyVersion, ProjectId = model.ProjectId, _adoAuthScheme = model.adoAuthScheme };
+
+ Projects projects = new Projects(config);
+ projectProperties = projects.GetProjectProperties();
+
+ if (projectProperties.count > 0)
+ {
+ defaultTeamID = projectProperties.value.Where(x => x.name == "System.Microsoft.TeamFoundation.Team.Default").FirstOrDefault().value;
+ }
+ _team = nodes.ExportTeamList(defaultTeamID);
+ if (_team.value != null)
+ {
+ con.Id.AddMessage("Teams");
+
+ string fetchedJson = JsonConvert.SerializeObject(_team.value, Formatting.Indented);
+ if (fetchedJson != "")
+ {
+ if (!Directory.Exists(extractedFolderName + "\\Teams"))
+ {
+ Directory.CreateDirectory(extractedFolderName + "\\Teams");
+ }
+ File.WriteAllText(extractedFolderName + "\\Teams\\Teams.json", fetchedJson);
+
+ List boardTypes = new List();
+ boardTypes.Add("Epics");
+ if (model.ProcessTemplate.ToLower() == "agile")
+ {
+ boardTypes.Add("Features");
+ boardTypes.Add("Stories");
+ }
+ else if (model.ProcessTemplate.ToLower() == "basic")
+ {
+ boardTypes.Add("Issues");
+ }
+ else if (model.ProcessTemplate.ToLower() == "scrum")
+ {
+ boardTypes.Add("Features");
+ boardTypes.Add("Backlog Items");
+ }
+
+ foreach (var team in _team.value)
+ {
+ List columnResponsesScrum = new List();
+ List columnResponsesAgile = new List();
+ List columnResponsesBasic = new List();
+ List boardRows = new List();
+
+ ExportTeamSetting.Setting listTeamSetting = new ExportTeamSetting.Setting();
+
+ List jObjCardFieldList = new List();
+ List jObjcardStyleList = new List();
+ string teamFolderPath = extractedFolderName + "\\Teams\\" + team.name;
+ if (!Directory.Exists(teamFolderPath))
+ {
+ Directory.CreateDirectory(teamFolderPath);
+ }
+ //Export Board Colums for each team
+ con.Team = team.name;
+
+ ClassificationNodes teamNodes = new ClassificationNodes(con);
+ foreach (var boardType in boardTypes)
+ {
+ var response = teamNodes.ExportBoardColums(boardType);
+ if (response.IsSuccessStatusCode && response.StatusCode == System.Net.HttpStatusCode.OK)
+ {
+ if (model.ProcessTemplate.ToLower() == "scrum")
+ {
+ string res = response.Content.ReadAsStringAsync().Result;
+ BoardColumnResponseScrum.ColumnResponse scrumColumns = JsonConvert.DeserializeObject(res);
+ scrumColumns.BoardName = boardType;
+ columnResponsesScrum.Add(scrumColumns);
+ }
+ else if (model.ProcessTemplate.ToLower() == "agile")
+ {
+ string res = response.Content.ReadAsStringAsync().Result;
+ BoardColumnResponseAgile.ColumnResponse agileColumns = JsonConvert.DeserializeObject(res);
+ agileColumns.BoardName = boardType;
+ columnResponsesAgile.Add(agileColumns);
+ }
+ else if (model.ProcessTemplate.ToLower() == "basic")
+ {
+ string res = response.Content.ReadAsStringAsync().Result;
+ BoardColumnResponseBasic.ColumnResponse basicColumns = JsonConvert.DeserializeObject(res);
+ basicColumns.BoardName = boardType;
+ columnResponsesBasic.Add(basicColumns);
+ }
+ con.Id.AddMessage("Board Columns Exported for "+ boardType);
+ Thread.Sleep(2000);
+ }
+ else
+ {
+ var errorMessage = response.Content.ReadAsStringAsync();
+ string error = RestAPI.Utility.GeterroMessage(errorMessage.Result.ToString());
+ teamNodes.LastFailureMessage = error;
+ con.Id.ErrorId().AddMessage("Error occured while exporting Board Columns: " + teamNodes.LastFailureMessage);
+ }
+
+ //Export board rows for each team
+ ExportBoardRows.Rows rows = teamNodes.ExportBoardRows(boardType);
+ if (rows.value != null && rows.value.Count > 0)
+ {
+ rows.BoardName = boardType;
+ boardRows.Add(rows);
+ con.Id.AddMessage("Board Rows Exported for "+boardType);
+ Thread.Sleep(2000);
+ }
+ else if (!string.IsNullOrEmpty(teamNodes.LastFailureMessage))
+ {
+ con.Id.ErrorId().AddMessage("Error occured while exporting Board Rows: " + teamNodes.LastFailureMessage);
+ }
+
+
+ //Export Card Fields for each team
+ var cardFieldResponse = teamNodes.ExportCardFields(boardType);
+ if (cardFieldResponse.IsSuccessStatusCode && cardFieldResponse.StatusCode == System.Net.HttpStatusCode.OK)
+ {
+ string res = cardFieldResponse.Content.ReadAsStringAsync().Result;
+ JObject jObj = JsonConvert.DeserializeObject(res);
+ jObj["BoardName"] = boardType;
+ jObjCardFieldList.Add(jObj);
+ con.Id.AddMessage("Card fields Definition Exported for "+ boardType);
+
+ }
+ else
+ {
+ var errorMessage = cardFieldResponse.Content.ReadAsStringAsync();
+ string error = RestAPI.Utility.GeterroMessage(errorMessage.Result.ToString());
+ teamNodes.LastFailureMessage = error;
+ con.Id.ErrorId().AddMessage("Error occured while exporting Card Fields: " + teamNodes.LastFailureMessage);
+ }
+
+ // Export card styles for each team
+ var cardStyleResponse = teamNodes.ExportCardStyle(boardType);
+ if (cardStyleResponse.IsSuccessStatusCode && cardStyleResponse.StatusCode == System.Net.HttpStatusCode.OK)
+ {
+ string res = cardStyleResponse.Content.ReadAsStringAsync().Result;
+ res = res.Replace(con.Project, "$ProjectName$");
+ JObject jObj = JsonConvert.DeserializeObject(res);
+ jObj["BoardName"] = boardType;
+ var style = jObj;
+ style["url"] = "";
+ style["_links"] = "{}";
+ var tagStyle = style["rules"]["tagStyle"];
+ if (tagStyle == null)
+ {
+ style["rules"]["tagStyle"] = new JArray();
+ }
+ jObjcardStyleList.Add(jObj);
+ con.Id.AddMessage("Card style exported for "+ boardType);
+
+ }
+ else
+ {
+ var errorMessage = cardStyleResponse.Content.ReadAsStringAsync();
+ string error = RestAPI.Utility.GeterroMessage(errorMessage.Result.ToString());
+ teamNodes.LastFailureMessage = error;
+ con.Id.ErrorId().AddMessage("Error occured while exporting Card Styles: " + teamNodes.LastFailureMessage);
+ }
+ }
+ //Export Team Setting for each team
+ if (model.ProcessTemplate.ToLower() != "basic")
+ {
+ ExportTeamSetting.Setting teamSetting = teamNodes.ExportTeamSetting();
+ if (teamSetting.backlogVisibilities != null)
+ {
+ listTeamSetting = teamSetting;
+ con.Id.AddMessage("Team Settings Definition Exported");
+ }
+ }
+ else if (!string.IsNullOrEmpty(teamNodes.LastFailureMessage))
+ {
+ con.Id.ErrorId().AddMessage("Error occured while exporting Team Setting: " + teamNodes.LastFailureMessage);
+ }
+
+ if (columnResponsesAgile.Count > 0)
+ {
+ File.WriteAllText(teamFolderPath + "\\BoardColumns.json", JsonConvert.SerializeObject(columnResponsesAgile, Formatting.Indented, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }));
+ }
+ if (columnResponsesScrum.Count > 0)
+ {
+ File.WriteAllText(teamFolderPath + "\\BoardColumns.json", JsonConvert.SerializeObject(columnResponsesScrum, Formatting.Indented, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }));
+ }
+ if (columnResponsesBasic.Count > 0)
+ {
+ File.WriteAllText(teamFolderPath + "\\BoardColumns.json", JsonConvert.SerializeObject(columnResponsesBasic, Formatting.Indented, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }));
+ }
+ if (boardRows.Count > 0)
+ {
+ File.WriteAllText(teamFolderPath + "\\BoardRows.json", JsonConvert.SerializeObject(boardRows, Formatting.Indented));
+ }
+ if (!string.IsNullOrEmpty(listTeamSetting.bugsBehavior))
+ {
+ File.WriteAllText(teamFolderPath + "\\TeamSetting.json", JsonConvert.SerializeObject(listTeamSetting, Formatting.Indented));
+ }
+ if (jObjCardFieldList.Count > 0)
+ {
+ File.WriteAllText(teamFolderPath + "\\CardFields.json", JsonConvert.SerializeObject(jObjCardFieldList, Formatting.Indented));
+ }
+ if (jObjcardStyleList.Count > 0)
+ {
+ File.WriteAllText(teamFolderPath + "\\CardStyles.json", JsonConvert.SerializeObject(jObjcardStyleList, Formatting.Indented));
+ }
+ }
+
+ return true;
+ }
+ else if (!string.IsNullOrEmpty(nodes.LastFailureMessage))
+ {
+ con.Id.ErrorId().AddMessage(nodes.LastFailureMessage);
+ string error = nodes.LastFailureMessage;
+ return false;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else
+ {
+ con.Id.ErrorId().AddMessage(nodes.LastFailureMessage);
+ return false;
+ }
+ }
+ catch (Exception ex)
+ {
+ con.Id.ErrorId().AddMessage(DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss") + "\t" + ex.Message + "\n" + ex.StackTrace + "\n");
+ }
+ return false;
+ }
+ public bool ExportIterations(ProjectConfigurations appConfig,string extractedFolderName)
+ {
+ try
+ {
+ ClassificationNodes nodes = new RestAPI.Extractor.ClassificationNodes(appConfig.BoardConfig);
+ ExportedIterations.Iterations viewModel = nodes.ExportIterationsToSave();
+ string fetchedJson = JsonConvert.SerializeObject(viewModel, Formatting.Indented);
+ if (fetchedJson != "")
+ {
+ if (!Directory.Exists(extractedFolderName))
+ {
+ Directory.CreateDirectory(extractedFolderName);
+ }
+ File.WriteAllText(extractedFolderName + "\\Iterations.json", fetchedJson);
+ return true;
+ }
+ else
+ {
+ string error = nodes.LastFailureMessage;
+ appConfig.BoardConfig.Id.ErrorId().AddMessage(error);
+ return false;
+ }
+ }
+ catch (Exception ex)
+ {
+ appConfig.BoardConfig.Id.ErrorId().AddMessage(DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss") + "\t" + ex.Message + "\n" + ex.StackTrace + "\n");
+ }
+ return false;
+ }
+ public bool ExportWorkItems(ProjectConfigurations appConfig, string extractedFolderName)
+ {
+ bool isWorkItemExported = false;
+ try
+ {
+ string[] workItemtypes = GetAllWorkItemsName(appConfig);//{ "Epic", "Feature", "Product Backlog Item", "Task", "Test Case", "Bug", "User Story", "Test Suite", "Test Plan", "Issue" };
+ if (!Directory.Exists(extractedFolderName))
+ {
+ Directory.CreateDirectory(extractedFolderName);
+ }
+
+ if (workItemtypes.Length > 0)
+ {
+ foreach (var WIT in workItemtypes)
+ {
+ GetWorkItemsCount WorkitemsCount = new GetWorkItemsCount(appConfig.WorkItemConfig);
+ WorkItemFetchResponse.WorkItems fetchedWorkItem = WorkitemsCount.GetWorkItemsfromSource(WIT);
+ string workItemJson = JsonConvert.SerializeObject(fetchedWorkItem, Formatting.Indented);
+ if (fetchedWorkItem.count > 0)
+ {
+ workItemJson = workItemJson.Replace(appConfig.WorkItemConfig.Project + "\\", "$ProjectName$\\");
+ string item = WIT;
+ if (!Directory.Exists(extractedFolderName + "\\WorkItems"))
+ {
+ Directory.CreateDirectory(extractedFolderName + "\\WorkItems");
+ }
+ File.WriteAllText(extractedFolderName + "\\WorkItems\\" + item + ".json", workItemJson);
+ }
+ else if (!string.IsNullOrEmpty(WorkitemsCount.LastFailureMessage))
+ {
+ appConfig.WorkItemConfig.Id.ErrorId().AddMessage(WorkitemsCount.LastFailureMessage);
+ }
+ }
+ isWorkItemExported = true;
+ }
+ }
+ catch (Exception ex)
+ {
+ appConfig.WorkItemConfig.Id.ErrorId().AddMessage(DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss") + "\t" + ex.Message + "\n" + ex.StackTrace + "\n");
+ }
+ return isWorkItemExported;
+ }
+ public bool ExportRepositoryList(ProjectConfigurations appConfig, string extractedFolderName)
+ {
+ bool isRepoExported = false;
+ try
+ {
+ BuildandReleaseDefs repolist = new BuildandReleaseDefs(appConfig.RepoConfig);
+ RepositoryList.Repository repos = repolist.GetRepoList();
+ if (repos.count > 0)
+ {
+ foreach (var repo in repos.value)
+ {
+ string preSettingPath = currentPath + @"\PreSetting";
+ string host = appConfig.RepoConfig.UriString + appConfig.RepoConfig.Project;
+ string sourceCodeJson = File.ReadAllText(preSettingPath + "\\ImportSourceCode.json");
+ sourceCodeJson = sourceCodeJson.Replace("$Host$", host).Replace("$Repo$", repo.name);
+ string endPointJson = File.ReadAllText(preSettingPath + "\\ServiceEndPoint.json");
+ endPointJson = endPointJson.Replace("$Host$", host).Replace("$Repo$", repo.name);
+ if (!Directory.Exists(extractedFolderName + "\\ImportSourceCode"))
+ {
+ Directory.CreateDirectory(extractedFolderName + "\\ImportSourceCode");
+ File.WriteAllText(extractedFolderName + "\\ImportSourceCode\\" + repo.name + ".json", sourceCodeJson);
+ }
+ else
+ {
+ File.WriteAllText(extractedFolderName + "\\ImportSourceCode\\" + repo.name + ".json", sourceCodeJson);
+ }
+ if (!Directory.Exists(extractedFolderName + "\\ServiceEndpoints"))
+ {
+ Directory.CreateDirectory(extractedFolderName + "\\ServiceEndpoints");
+ File.WriteAllText(extractedFolderName + "\\ServiceEndpoints\\" + repo.name + "-code.json", endPointJson);
+ }
+ else
+ {
+ File.WriteAllText(extractedFolderName + "\\ServiceEndpoints\\" + repo.name + "-code.json", endPointJson);
+ }
+ }
+ isRepoExported = true;
+ }
+ }
+ catch (Exception ex)
+ {
+ appConfig.RepoConfig.Id.ErrorId().AddMessage(DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss") + "\t" + ex.Message + "\n" + ex.StackTrace + "\n");
+ }
+ return isRepoExported;
+ }
+ ///
+ /// Get the Build definitions to write into file
+ ///
+ ///
+ ///
+ public int GetBuildDefinitions(ProjectConfigurations appConfig, string extractedFolderName)
+ {
+ try
+ {
+ BuildandReleaseDefs buildandReleaseDefs = new BuildandReleaseDefs(appConfig.BuildDefinitionConfig);
+ List builds = buildandReleaseDefs.ExportBuildDefinitions();
+ BuildandReleaseDefs repoDefs = new BuildandReleaseDefs(appConfig.RepoConfig);
+ Dictionary variableGroupNameId = GetVariableGroups(appConfig, extractedFolderName);
+ RepositoryList.Repository repo = repoDefs.GetRepoList();
+ if (builds.Count > 0)
+ {
+ int count = 1;
+ //creating ImportCode Json file
+ foreach (JObject def in builds)
+ {
+ string repoID = "";
+ var buildName = def["name"];
+ string fileName = buildName.ToString().Replace(".", "") + ".json";
+ var repoName = def["repository"]["name"];
+ var type = def["repository"]["type"];
+ foreach (var re in repo.value)
+ {
+ if (re.name == repoName.ToString())
+ {
+ repoID = re.id;
+ }
+ }
+ def["authoredBy"] = "{}";
+ def["project"] = "{}";
+ def["url"] = "";
+ def["uri"] = "";
+ def["id"] = "";
+ if (def["queue"]["pool"].HasValues)
+ {
+ def["queue"]["pool"]["id"] = "";
+ }
+ def["_links"] = "{}";
+ def["createdDate"] = "";
+ if (def["variableGroups"] != null)
+ {
+ var variableGroup = def["variableGroups"].HasValues ? def["variableGroups"].ToArray() : new JToken[0];
+ if (variableGroup.Length > 0)
+ {
+ foreach (var groupId in variableGroup)
+ {
+ groupId["id"] = "$" + variableGroupNameId.Where(x => x.Key == groupId["id"].ToString()).FirstOrDefault().Value + "$";
+ }
+ }
+ }
+ var yamalfilename = def["process"]["yamlFilename"];
+
+ #region YML PIPELINES OF TYPE AZURE REPOS
+ if (yamalfilename != null && type.ToString().ToLower() == "tfsgit")
+ {
+ count = YmlWithAzureRepos(appConfig, count, extractedFolderName, def, fileName, type);
+ }
+ #endregion
+
+ #region YML PIPELINE WITH GITHUB
+ else if (yamalfilename != null && type.ToString().ToLower() == "github")
+ {
+ count = YmlWithGitHub(appConfig, count, extractedFolderName, def, fileName, type);
+ }
+ #endregion
+
+ #region OTHER
+ else if (yamalfilename == null)
+ {
+ count = NormalPipeline(appConfig, count, extractedFolderName, def, fileName, repoName, type);
+ }
+ #endregion
+ }
+ return count;
+ }
+ }
+ catch (Exception ex)
+ {
+ logger.Info(DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss") + "\t" + ex.Message + "\n" + ex.StackTrace + "\n");
+ }
+ return 0;
+ }
+ ///
+ /// Normal Build pipeline, which could be either pointing from Azure Repos or GitHub
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ private static int NormalPipeline(ProjectConfigurations appConfig, int count, string templatePath, JObject def, string fileName, JToken repoName, JToken type)
+ {
+ try
+ {
+ def["queue"]["id"] = "";
+ def["queue"]["url"] = "";
+ def["queue"]["_links"] = "{}";
+ def["queue"]["pool"]["id"] = "";
+ def["_links"] = "{}";
+ def["createdDate"] = "";
+
+ var process = def["process"];
+ if (process != null)
+ {
+ var phases = process["phases"];
+ if (phases != null)
+ {
+ foreach (var phase in phases)
+ {
+ phase["target"]["queue"] = "{}";
+ var steps = phase["steps"];
+ if (steps != null)
+ {
+ foreach (var step in steps)
+ {
+ string keyConfig = File.ReadAllText(currentPath + @"\\Templates\EndpointKeyConfig.json");
+ KeyConfig.Keys keyC = new KeyConfig.Keys();
+ keyC = JsonConvert.DeserializeObject(keyConfig);
+ foreach (var key in keyC.keys)
+ {
+ string keyVal = step[key] != null ? step[key].ToString() : "";
+ if (!string.IsNullOrEmpty(keyVal))
+ {
+ step[key] = "";
+ }
+ }
+ foreach (var key in keyC.keys)
+ {
+ string keyVal = step["inputs"][key] != null ? step["inputs"][key].ToString() : "";
+ if (!string.IsNullOrEmpty(keyVal))
+ {
+ step["inputs"][key] = "";
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (type.ToString().ToLower() == "github")
+ {
+
+ Guid g = Guid.NewGuid();
+ string randStr = g.ToString().Substring(0, 8);
+ def["repository"]["type"] = "Git";
+ def["repository"]["properties"]["fullName"] = "repository";
+ def["repository"]["properties"]["connectedServiceId"] = "$GitHub_" + randStr + "$";
+ def["repository"]["name"] = "repository";
+ string url = def["repository"]["url"].ToString();
+ if (url != "")
+ {
+ string endPointString = File.ReadAllText(currentPath + @"PreSetting\\GitHubEndPoint.json");
+ endPointString = endPointString.Replace("$GitHubURL$", url).Replace("$Name$", "GitHub_" + randStr);
+
+ if (!Directory.Exists(extractedTemplatePath + appConfig.RepoConfig.Project + "\\ServiceEndpoints"))
+ {
+ Directory.CreateDirectory(extractedTemplatePath + appConfig.RepoConfig.Project + "\\ServiceEndpoints");
+ File.WriteAllText(extractedTemplatePath + appConfig.RepoConfig.Project + "\\ServiceEndpoints\\GitHub" + randStr + "-EndPoint.json", endPointString);
+ }
+ else
+ {
+ File.WriteAllText(extractedTemplatePath + appConfig.RepoConfig.Project + "\\ServiceEndpoints\\GitHub" + randStr + "-EndPoint.json", endPointString);
+ }
+ }
+ }
+ else if (type.ToString().ToLower() == "git")
+ {
+ Guid g = Guid.NewGuid();
+ string randStr = g.ToString().Substring(0, 8);
+ string url = def["repository"]["url"].ToString();
+ string endPointString = File.ReadAllText(currentPath + @"PreSetting\\GitHubEndPoint.json");
+ endPointString = endPointString.Replace("$GitHubURL$", url).Replace("$Name$", "GitHub_" + randStr);
+
+ if (!Directory.Exists(extractedTemplatePath + appConfig.RepoConfig.Project + "\\ServiceEndpoints"))
+ {
+ Directory.CreateDirectory(extractedTemplatePath + appConfig.RepoConfig.Project + "\\ServiceEndpoints");
+ File.WriteAllText(extractedTemplatePath + appConfig.RepoConfig.Project + "\\ServiceEndpoints\\GitHub_" + randStr + "-EndPoint.json", endPointString);
+ }
+ else
+ {
+ File.WriteAllText(extractedTemplatePath + appConfig.RepoConfig.Project + "\\ServiceEndpoints\\GitHub_" + randStr + "-EndPoint.json", endPointString);
+ }
+ def["repository"]["properties"]["connectedServiceId"] = "$GitHub_" + randStr + "$";
+ }
+ else
+ {
+ def["repository"]["id"] = "$" + repoName + "$";
+ def["repository"]["url"] = "";
+ def["repository"]["properties"]["connectedServiceId"] = "";
+ }
+ var input = def["processParameters"]["inputs"];
+ if (input != null)
+ {
+ if (input.HasValues)
+ {
+ foreach (var i in input)
+ {
+ i["defaultValue"] = "";
+
+ }
+ }
+ }
+ var build = def["build"];
+ if (build != null)
+ {
+ if (build.HasValues)
+ {
+ foreach (var b in build)
+ {
+ b["inputs"]["serverEndpoint"] = "";
+ }
+ }
+ }
+ count++;
+ if (!Directory.Exists(templatePath + "\\BuildDefinitions"))
+ {
+ Directory.CreateDirectory(templatePath + "\\BuildDefinitions");
+ File.WriteAllText(templatePath + "\\BuildDefinitions\\" + fileName, JsonConvert.SerializeObject(def, Formatting.Indented));
+ }
+ else
+ {
+ File.WriteAllText(templatePath + "\\BuildDefinitions\\" + fileName, JsonConvert.SerializeObject(def, Formatting.Indented));
+ }
+
+ return count;
+ }
+ catch (Exception ex)
+ {
+ logger.Info(DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss") + "Exporting normalPipeline \t" + ex.Message + "\n" + ex.StackTrace + "\n");
+ }
+ return count;
+ }
+ ///
+ /// YAML pipeline which is pointing to GitHub
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ private static int YmlWithGitHub(ProjectConfigurations appConfig, int count, string templatePath, JObject def, string fileName, JToken type)
+ {
+ try
+ {
+ Guid g = Guid.NewGuid();
+ string randStr = g.ToString().Substring(0, 8);
+ var ymlRepoUrl = def["repository"]["url"].ToString();
+ if (!Directory.Exists(templatePath + "\\ImportSourceCode"))
+ {
+ Directory.CreateDirectory(templatePath + "\\ImportSourceCode");
+ }
+ if (type.ToString().ToLower() == "github")
+ {
+ string gitHubRepo = def["repository"]["id"].ToString();
+ string[] gitHubIdSplit = gitHubRepo.Split('/');
+ gitHubIdSplit[0] = "$username$";
+ gitHubRepo = string.Join("/", gitHubIdSplit);
+
+ ForkRepos.Fork gitHubRepoList = new ForkRepos.Fork();
+ gitHubRepoList.repositories = new List();
+ if (File.Exists(templatePath + "\\ImportSourceCode\\GitRepository.json"))
+ {
+ string readrepo = File.ReadAllText(templatePath + "\\ImportSourceCode\\GitRepository.json");
+ gitHubRepoList = JsonConvert.DeserializeObject(readrepo);
+ }
+ ForkRepos.Repository repoName = new ForkRepos.Repository
+ {
+ fullName = def["repository"]["id"].ToString(),
+ endPointName = "GitHub_" + randStr
+ };
+ gitHubRepoList.repositories.Add(repoName);
+
+ File.WriteAllText(templatePath + "\\ImportSourceCode\\GitRepository.json", JsonConvert.SerializeObject(gitHubRepoList, Formatting.Indented));
+
+ def["repository"]["properties"]["apiUrl"] = "https://api.github.com/repos/" + gitHubRepo;
+ def["repository"]["properties"]["branchesUrl"] = "https://api.github.com/repos/" + gitHubRepo + "/branches";
+ def["repository"]["properties"]["cloneUrl"] = "https://github.com/" + gitHubRepo + ".git";
+ def["repository"]["properties"]["fullName"] = "repository";
+ def["repository"]["properties"]["manageUrl"] = "https://github.com/" + gitHubRepo;
+ def["repository"]["properties"]["connectedServiceId"] = "$GitHub_" + randStr + "$";
+ def["repository"]["name"] = gitHubRepo;
+ def["repository"]["url"] = "https://github.com/" + gitHubRepo + ".git";
+ def["repository"]["id"] = gitHubRepo;
+ }
+ if (ymlRepoUrl != "")
+ {
+ string endPointString = File.ReadAllText(currentPath + @"PreSetting\\GitHubEndPoint.json");
+ endPointString = endPointString.Replace("$GitHubURL$", ymlRepoUrl).Replace("$Name$", "GitHub_" + randStr);
+
+ if (!Directory.Exists(templatePath + "\\ServiceEndpoints"))
+ {
+ Directory.CreateDirectory(templatePath + "\\ServiceEndpoints");
+ File.WriteAllText(templatePath + "\\ServiceEndpoints\\GitHub_" + randStr + "-EndPoint.json", endPointString);
+ }
+ else
+ {
+ File.WriteAllText(templatePath + "\\ServiceEndpoints\\GitHub_" + randStr + "-EndPoint.json", endPointString);
+ }
+ }
+ count = count + 1;
+ if (!Directory.Exists(templatePath + "\\BuildDefinitionGitHub"))
+ {
+ Directory.CreateDirectory(templatePath + "\\BuildDefinitionGitHub");
+ File.WriteAllText(templatePath + "\\BuildDefinitionGitHub\\" + fileName, JsonConvert.SerializeObject(def, Formatting.Indented));
+ }
+ else
+ {
+ File.WriteAllText(templatePath + "\\BuildDefinitionGitHub\\" + fileName, JsonConvert.SerializeObject(def, Formatting.Indented));
+ }
+
+ return count;
+ }
+ catch (Exception ex)
+ {
+ logger.Info(DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss") + "Exporting ymlWithGitHub \t" + ex.Message + "\n" + ex.StackTrace + "\n");
+ }
+ return count;
+ }
+ ///
+ /// YAML pipeline which is pointing to Azure Repos
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ private static int YmlWithAzureRepos(ProjectConfigurations appConfig, int count, string templatePath, JObject def, string fileName, JToken type)
+ {
+ try
+ {
+ Guid g = Guid.NewGuid();
+ string randStr = g.ToString().Substring(0, 8);
+ // def["triggers"] = new JArray();
+ if (type.ToString().ToLower() == "github")
+ {
+ def["repository"]["properties"]["fullName"] = "repository";
+ def["repository"]["properties"]["connectedServiceId"] = "$GitHub_" + randStr + "$";
+ def["repository"]["name"] = "repository";
+ }
+ var ymlRepoUrl = def["repository"]["url"].ToString();
+ if (ymlRepoUrl != "")
+ {
+ string endPointString = File.ReadAllText(currentPath + @"PreSetting\\GitHubEndPoint.json");
+ endPointString = endPointString.Replace("$GitHubURL$", ymlRepoUrl).Replace("$Name$", "GitHub_" + randStr);
+ if (!Directory.Exists(templatePath + "\\ServiceEndpoints"))
+ {
+ Directory.CreateDirectory(templatePath + "\\ServiceEndpoints");
+ File.WriteAllText(templatePath + "\\ServiceEndpoints\\GitHub-" + randStr + "-EndPoint.json", endPointString);
+ }
+ else
+ {
+ File.WriteAllText(templatePath + "\\ServiceEndpoints\\GitHub-" + randStr + "-EndPoint.json", endPointString);
+ }
+ }
+ string[] splitYmlRepoUrl = ymlRepoUrl.Split('/');
+ if (splitYmlRepoUrl.Length > 0)
+ {
+ splitYmlRepoUrl[2] = "$Organization$@dev.azure.com";
+ splitYmlRepoUrl[3] = "$Organization$";
+ splitYmlRepoUrl[4] = "$ProjectName$";
+ ymlRepoUrl = string.Join("/", splitYmlRepoUrl);
+ def["repository"]["url"] = ymlRepoUrl;
+ def["repository"]["properties"]["cloneUrl"] = ymlRepoUrl;
+ }
+ def["repository"]["properties"]["safeRepository"] = string.Format("${0}$", def["repository"]["name"].ToString());
+ def["repository"]["id"] = string.Format("${0}$", def["repository"]["name"].ToString());
+ var queueHref = def["queue"]["_links"]["self"]["href"].ToString();
+ if (queueHref != "")
+ {
+ string[] splitQhref = queueHref.Split('/');
+ if (splitQhref.Length > 0)
+ {
+ splitQhref[3] = "$Organization$";
+ splitQhref[splitQhref.Length - 1] = "$" + def["queue"]["name"].ToString() + "$";
+ def["queue"]["_links"]["self"]["href"] = string.Join("/", splitQhref);
+ }
+ def["queue"]["id"] = "$" + def["queue"]["name"] + "$";
+ def["queue"]["url"] = string.Join("/", splitQhref);
+ }
+ count = count + 1;
+ if (!Directory.Exists(templatePath + "\\BuildDefinitions"))
+ {
+ Directory.CreateDirectory(templatePath + "\\BuildDefinitions");
+ File.WriteAllText(templatePath + "\\BuildDefinitions\\" + fileName, JsonConvert.SerializeObject(def, Formatting.Indented));
+ }
+ else
+ {
+ File.WriteAllText(templatePath + "\\BuildDefinitions\\" + fileName, JsonConvert.SerializeObject(def, Formatting.Indented));
+ }
+
+ return count;
+ }
+ catch (Exception ex)
+ {
+ logger.Info(DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss") + "Exporting ymlWithAzureRepos \t" + ex.Message + "\n" + ex.StackTrace + "\n");
+ }
+ return count;
+ }
+ ///
+ /// Generalizing the release definition method to make it work for All kind of Release definition
+ ///
+ ///
+ ///
+ public int GeneralizingGetReleaseDefinitions(ProjectConfigurations appConfig, string extractedFolderName)
+ {
+ try
+ {
+ BuildandReleaseDefs releaseDefs = new BuildandReleaseDefs(appConfig.ReleaseDefinitionConfig);
+ List releases = releaseDefs.GetReleaseDefs();
+ string rells = JsonConvert.SerializeObject(releases);
+ BuildandReleaseDefs agent = new BuildandReleaseDefs(appConfig.AgentQueueConfig);
+ Dictionary variableGroupNameId = GetVariableGroups(appConfig, extractedFolderName);
+ Dictionary queue = agent.GetQueues();
+ int releasecount = 1;
+ if (releases.Count > 0)
+ {
+ foreach (JObject rel in releases)
+ {
+ var name = rel["name"];
+ rel["id"] = "";
+ rel["url"] = "";
+ rel["_links"] = "{}";
+ rel["createdBy"] = "{}";
+ rel["createdOn"] = "";
+ rel["modifiedBy"] = "{}";
+ rel["modifiedOn"] = "";
+
+ var variableGroup = rel["variableGroups"].HasValues ? rel["variableGroups"].ToArray() : new JToken[0];
+ if (variableGroup.Length > 0)
+ {
+ foreach (var groupId in variableGroup)
+ {
+ rel["variableGroups"] = new JArray("$" + variableGroupNameId.Where(x => x.Key == groupId.ToString()).FirstOrDefault().Value + "$");
+ }
+ }
+ else
+ {
+ rel["variableGroups"] = new JArray();
+ }
+ var env = rel["environments"];
+ foreach (var e in env)
+ {
+ e["badgeUrl"] = "";
+ var envVariableGroup = e["variableGroups"].HasValues ? e["variableGroups"].ToArray() : new JToken[0];
+ if (envVariableGroup.Length > 0)
+ {
+ foreach (var envgroupId in envVariableGroup)
+ {
+ e["variableGroups"] = new JArray("$" + variableGroupNameId.Where(x => x.Key == envgroupId.ToString()).FirstOrDefault().Value + "$");
+ }
+ }
+ else
+ {
+ e["variableGroups"] = new JArray();
+ }
+ var owner = e["owner"];
+ owner["id"] = "$OwnerId$";
+ owner["displayName"] = "$OwnerDisplayName$";
+ owner["uniqueName"] = "$OwnerUniqueName$";
+ owner["url"] = "";
+ owner["_links"] = "{}";
+ owner["imageUrl"] = "";
+ owner["descriptor"] = "";
+
+ var deployPhases = e["deployPhases"];
+ if (deployPhases.HasValues)
+ {
+ foreach (var dep in deployPhases)
+ {
+
+ var deploymentInput = dep["deploymentInput"];
+ var queueID = deploymentInput["queueId"];
+ string queueName = "";
+ if (queue != null)
+ {
+ if (queue.Count > 0)
+ {
+ var q = queue.ContainsValue(Convert.ToInt32(queueID));
+ if (q)
+ {
+ var agenetName = queue.Where(x => x.Value.ToString() == queueID.ToString()).FirstOrDefault();
+ if (agenetName.Key != null)
+ {
+ queueName = agenetName.Key.ToString();
+ }
+ else
+ {
+ queueName = "";
+ }
+ }
+ }
+ }
+ if (queueName != "")
+ {
+ deploymentInput["queueId"] = "$" + queueName + "$";
+ }
+ else
+ {
+ deploymentInput["queueId"] = "";
+ }
+
+ var workflow = dep["workflowTasks"];
+ if (workflow.HasValues)
+ {
+ foreach (var flow in workflow)
+ {
+ var input = flow["inputs"];
+ string keyConfig = File.ReadAllText(currentPath + @"\\Templates\EndpointKeyConfig.json");
+ KeyConfig.Keys keyC = new KeyConfig.Keys();
+ keyC = JsonConvert.DeserializeObject(keyConfig);
+ foreach (var key in keyC.keys)
+ {
+ string keyVal = input[key] != null ? input[key].ToString() : "";
+ if (!string.IsNullOrEmpty(keyVal))
+ {
+ input[key] = "";
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ var artifact = rel["artifacts"];
+ if (artifact.HasValues)
+ {
+ foreach (var art in artifact)
+ {
+ string buildName = art["definitionReference"]["definition"]["name"].ToString();
+ string type = art["type"].ToString();
+ if (type.ToLower() == "build")
+ {
+ art["sourceId"] = "$ProjectId$:" + "$" + buildName + "-id$";
+ art["definitionReference"]["definition"]["id"] = "$" + buildName + "-id$";
+ art["definitionReference"]["project"]["id"] = "$ProjectId$";
+ art["definitionReference"]["project"]["name"] = "$ProjectName$";
+ art["definitionReference"]["artifactSourceDefinitionUrl"] = "{}";
+ }
+ if (type.ToLower() == "azurecontainerrepository")
+ {
+ art["sourceId"] = "$ProjectId$:" + "$" + buildName + "-id$";
+ art["definitionReference"]["connection"]["id"] = "";
+ art["definitionReference"]["definition"]["id"] = "";
+ art["definitionReference"]["definition"]["name"] = "";
+ art["definitionReference"]["registryurl"]["id"] = "";
+ art["definitionReference"]["registryurl"]["name"] = "";
+ art["definitionReference"]["resourcegroup"]["id"] = "";
+ art["definitionReference"]["resourcegroup"]["name"] = "";
+ }
+ }
+ }
+ if (!(Directory.Exists(extractedFolderName + "\\ReleaseDefinitions")))
+ {
+ Directory.CreateDirectory(extractedFolderName + "\\ReleaseDefinitions");
+ File.WriteAllText(extractedFolderName + "\\ReleaseDefinitions\\" + name + ".json", JsonConvert.SerializeObject(rel, Formatting.Indented));
+ }
+ else
+ {
+ File.WriteAllText(extractedFolderName + "\\ReleaseDefinitions\\" + name + ".json", JsonConvert.SerializeObject(rel, Formatting.Indented));
+ }
+ releasecount++;
+ }
+ }
+ return releasecount;
+ }
+ catch (Exception ex)
+ {
+ logger.Info(DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss") + "\t" + ex.Message + "\n" + ex.StackTrace + "\n");
+ appConfig.ReleaseDefinitionConfig.Id.ErrorId().AddMessage(ex.Message + Environment.NewLine + ex.StackTrace);
+ }
+ return 0;
+ }
+ ///
+ /// Get different kinds of service endpoints and format it into POST json format
+ ///
+ ///
+ public void GetServiceEndpoints(ProjectConfigurations appConfig, string extractedFolderName)
+ {
+ try
+ {
+ RestAPI.Service.ServiceEndPoint serviceEndPoint = new RestAPI.Service.ServiceEndPoint(appConfig.EndpointConfig);
+ ServiceEndPoint getServiceEndPoint = serviceEndPoint.GetServiceEndPoints();
+ if (getServiceEndPoint.count > 0)
+ {
+ foreach (RestAPI.Viewmodel.Extractor.GetServiceEndpoints.Value endpoint in getServiceEndPoint.value)
+ {
+ switch (endpoint.authorization.scheme)
+ {
+ case "OAuth":
+ case "InstallationToken":
+ switch (endpoint.type)
+ {
+ case "github":
+ case "GitHub":
+ if (endpoint.authorization.parameters == null)
+ {
+ endpoint.authorization.parameters = new RestAPI.Viewmodel.Extractor.GetServiceEndpoints.Parameters
+ {
+ AccessToken = "AccessToken"
+ };
+ }
+ else
+ {
+ endpoint.authorization.parameters.AccessToken = endpoint.authorization.parameters.AccessToken ?? "AccessToken";
+ }
+ break;
+ }
+ break;
+ case "UsernamePassword":
+ endpoint.authorization.parameters.username = endpoint.authorization.parameters.username ?? "username";
+ endpoint.authorization.parameters.password = endpoint.authorization.parameters.password ?? "password";
+ break;
+ case "ManagedServiceIdentity":
+ if (endpoint.authorization.parameters == null)
+ {
+ endpoint.authorization.parameters = new RestAPI.Viewmodel.Extractor.GetServiceEndpoints.Parameters
+ {
+ tenantId = Guid.NewGuid().ToString()
+ };
+ }
+ else
+ {
+ endpoint.authorization.parameters.tenantId = endpoint.authorization.parameters.tenantId ?? Guid.NewGuid().ToString();
+ }
+ break;
+ case "ServicePrincipal":
+ switch (endpoint.type)
+ {
+ case "devCenter":
+ endpoint.authorization.parameters.servicePrincipalKey = endpoint.authorization.parameters.servicePrincipalKey ?? "P2ssw0rd@123";
+ break;
+ case "azurerm":
+ endpoint.authorization.parameters.url = null;
+ endpoint.authorization.parameters.servicePrincipalId = endpoint.authorization.parameters.servicePrincipalId ?? Guid.NewGuid().ToString();
+ endpoint.authorization.parameters.authenticationType = endpoint.authorization.parameters.authenticationType ?? "spnKey";
+ endpoint.authorization.parameters.tenantId = endpoint.authorization.parameters.tenantId ?? Guid.NewGuid().ToString();
+ endpoint.authorization.parameters.servicePrincipalKey = endpoint.authorization.parameters.servicePrincipalKey ?? "spnKey";
+ switch (endpoint.data.scopeLevel)
+ {
+ case "ManagementGroup":
+ endpoint.data.managementGroupId = endpoint.data.managementGroupId ?? "managedgroup";
+ endpoint.data.managementGroupName = endpoint.data.managementGroupName ?? "groupname";
+ break;
+ }
+ break;
+ }
+ break;
+ case "Certificate":
+ switch (endpoint.type)
+ {
+ case "dockerhost":
+ if (endpoint.authorization.parameters == null)
+ {
+ endpoint.authorization.parameters = new RestAPI.Viewmodel.Extractor.GetServiceEndpoints.Parameters();
+ endpoint.authorization.parameters.cacert = endpoint.authorization.parameters.cacert ?? "cacert";
+ endpoint.authorization.parameters.cert = endpoint.authorization.parameters.cert ?? "cert";
+ endpoint.authorization.parameters.key = endpoint.authorization.parameters.key ?? "key";
+ }
+ else
+ {
+ endpoint.authorization.parameters.cacert = endpoint.authorization.parameters.cacert ?? "cacert";
+ endpoint.authorization.parameters.cert = endpoint.authorization.parameters.cert ?? "cert";
+ endpoint.authorization.parameters.key = endpoint.authorization.parameters.key ?? "key";
+ }
+ break;
+
+ case "azure":
+ if (endpoint.authorization.parameters == null)
+ {
+ endpoint.authorization.parameters = new RestAPI.Viewmodel.Extractor.GetServiceEndpoints.Parameters
+ {
+ certificate = "certificate"
+ };
+ }
+ else
+ {
+ endpoint.authorization.parameters.certificate = endpoint.authorization.parameters.certificate ?? "certificate";
+ }
+ break;
+ }
+ break;
+ case "Token":
+ if (endpoint.authorization.parameters == null)
+ {
+ endpoint.authorization.parameters = new RestAPI.Viewmodel.Extractor.GetServiceEndpoints.Parameters
+ {
+ apitoken = "apitoken"
+ };
+ }
+ else
+ {
+ endpoint.authorization.parameters.apitoken = endpoint.authorization.parameters.apitoken ?? "apitoken";
+ }
+ break;
+ case "None":
+ switch (endpoint.type)
+ {
+ case "AzureServiceBus":
+ if (endpoint.authorization.parameters == null)
+ {
+ endpoint.authorization.parameters = new RestAPI.Viewmodel.Extractor.GetServiceEndpoints.Parameters
+ {
+ serviceBusConnectionString = "connectionstring"
+ };
+ }
+ else
+ {
+ endpoint.authorization.parameters.serviceBusConnectionString = endpoint.authorization.parameters.serviceBusConnectionString ?? "connectionstring";
+ }
+ break;
+ case "externalnugetfeed":
+ if (endpoint.authorization.parameters == null)
+ {
+ endpoint.authorization.parameters = new RestAPI.Viewmodel.Extractor.GetServiceEndpoints.Parameters
+ {
+ nugetkey = "nugetkey"
+ };
+ }
+ else
+ {
+ endpoint.authorization.parameters.nugetkey = endpoint.authorization.parameters.nugetkey ?? "nugetkey";
+ }
+ break;
+ }
+ break;
+
+ }
+ string endpointString = JsonConvert.SerializeObject(endpoint);
+ if (!Directory.Exists(extractedFolderName + "\\ServiceEndpoints"))
+ {
+ Directory.CreateDirectory(extractedFolderName + "\\ServiceEndpoints");
+ File.WriteAllText(extractedFolderName + "\\ServiceEndpoints\\", JsonConvert.SerializeObject(endpoint, Formatting.Indented, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }));
+ }
+ else
+ {
+ File.WriteAllText(extractedFolderName + "\\ServiceEndpoints\\" + endpoint.name + ".json", JsonConvert.SerializeObject(endpoint, Formatting.Indented, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }));
+ }
+ }
+ appConfig.EndpointConfig.Id.AddMessage("Service endpoints exported");
+ }
+ else if (!string.IsNullOrEmpty(serviceEndPoint.LastFailureMessage))
+ {
+ appConfig.EndpointConfig.Id.ErrorId().AddMessage("Error occured while fetching service endpoints");
+ }
+ }
+ catch (Exception ex)
+ {
+ appConfig.EndpointConfig.Id.ErrorId().AddMessage(DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss") + "\t" + ex.Message + "\n" + ex.StackTrace + "\n");
+ }
+ }
+ ///
+ /// Get All work item names
+ ///
+ ///
+ ///
+ private string[] GetAllWorkItemsName(ProjectConfigurations appConfig)
+ {
+ GetWorkItemsCount getWorkItems = new GetWorkItemsCount(appConfig.WorkItemConfig);
+ WorkItemNames.Names workItems = getWorkItems.GetAllWorkItemNames();
+ List workItemNames = new List();
+ if (workItems.count > 0)
+ {
+ foreach (var workItem in workItems.value)
+ {
+ workItemNames.Add(workItem.name);
+ }
+ }
+ return workItemNames.ToArray();
+ }
+ private Dictionary GetVariableGroups(ProjectConfigurations appConfig, string extractedFolderName)
+ {
+ VariableGroups variableGroups = new VariableGroups(appConfig.VariableGroupConfig);
+ GetVariableGroups.Groups groups = variableGroups.GetVariableGroups();
+ Dictionary varibaleGroupDictionary = new Dictionary();
+ if (groups.count > 0)
+ {
+ if (!(Directory.Exists(extractedFolderName + "\\VariableGroups")))
+ {
+ Directory.CreateDirectory(extractedFolderName + "\\VariableGroups");
+ File.WriteAllText(extractedFolderName + "\\VariableGroups\\VariableGroup.json", JsonConvert.SerializeObject(groups, Formatting.Indented));
+ }
+ else
+ {
+ File.WriteAllText(extractedFolderName + "\\VariableGroups\\VariableGroup.json", JsonConvert.SerializeObject(groups, Formatting.Indented));
+ }
+ foreach (var vg in groups.value)
+ {
+ if (!varibaleGroupDictionary.ContainsKey(vg.id))
+ {
+ varibaleGroupDictionary.Add(vg.id, vg.name);
+ }
+ }
+ }
+ return varibaleGroupDictionary;
+ }
+ public bool ExportDeliveryPlans(ProjectConfigurations appConfig, string extractedFolderName)
+ {
+ bool isDeliveryPlanExported = false;
+ try
+ {
+ Plans plans = new Plans(appConfig.WorkItemConfig);
+ GetPlans.Root plansList = plans.GetDeliveryPlans(appConfig.WorkItemConfig.AccountName, appConfig.WorkItemConfig.Project);
+ if (plansList.count > 0)
+ {
+ RestAPI.Extractor.ClassificationNodes nodes = new RestAPI.Extractor.ClassificationNodes(appConfig.BoardConfig);
+ string defaultTeamID = string.Empty;
+
+ var teamsRes = nodes.GetTeams();
+ RootTeams rootTeams = new RootTeams();
+ if (teamsRes != null && teamsRes.IsSuccessStatusCode)
+ {
+ rootTeams = JsonConvert.DeserializeObject(teamsRes.Content.ReadAsStringAsync().Result);
+ }
+
+ foreach (var plan in plansList.value)
+ {
+ APlan.Root aplan = plans.GetAPlan(appConfig.WorkItemConfig.AccountName, appConfig.WorkItemConfig.Project, plan.id);
+ if (aplan.properties?.teamBacklogMappings != null)
+ {
+ foreach (var team in aplan.properties?.teamBacklogMappings)
+ {
+ if (rootTeams.count > 0)
+ {
+ foreach (var teams in rootTeams.value)
+ {
+ if (team.teamId == teams.id)
+ {
+ team.teamId = $"${teams.name}$";
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (!string.IsNullOrEmpty(aplan.id))
+ {
+ if (!(Directory.Exists(extractedFolderName + "\\DeliveryPlans")))
+ {
+ Directory.CreateDirectory(extractedFolderName + "\\DeliveryPlans");
+ File.WriteAllText(extractedFolderName + $"\\DeliveryPlans\\{aplan.name}.json", JsonConvert.SerializeObject(aplan, Formatting.Indented));
+ }
+ else
+ {
+ File.WriteAllText(extractedFolderName + $"\\DeliveryPlans\\{aplan.name}.json", JsonConvert.SerializeObject(aplan, Formatting.Indented));
+ }
+ }
+ }
+ isDeliveryPlanExported = true;
+ }
+ }
+ catch (Exception ex)
+ {
+ appConfig.WorkItemConfig.Id.ErrorId().AddMessage(DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss") + "\t" + ex.Message + "\n" + ex.StackTrace + "\n");
+ }
+ return isDeliveryPlanExported;
+ }
+ #endregion END GENERATE ARTIFACTS
+ }
+}
diff --git a/src/ADOGenerator/Services/Init.cs b/src/ADOGenerator/Services/Init.cs
index f922eeb..adef919 100644
--- a/src/ADOGenerator/Services/Init.cs
+++ b/src/ADOGenerator/Services/Init.cs
@@ -52,6 +52,10 @@ public bool CheckProjectName(string name)
public string ExtractHref(string link)
{
+ if(string.IsNullOrEmpty(link))
+ {
+ return string.Empty;
+ }
var startIndex = link.IndexOf("href='") + 6;
var endIndex = link.IndexOf("'", startIndex);
return link.Substring(startIndex, endIndex - startIndex);
diff --git a/src/ADOGenerator/Services/ProjectService.cs b/src/ADOGenerator/Services/ProjectService.cs
index c0f9c41..2db13d3 100644
--- a/src/ADOGenerator/Services/ProjectService.cs
+++ b/src/ADOGenerator/Services/ProjectService.cs
@@ -72,6 +72,108 @@ public HttpResponseMessage GetprojectList(string accname, string pat)
HttpResponseMessage response = projects.GetListOfProjects();
return response;
}
+
+ public HttpResponseMessage GetProjects(string accname, string pat, string authScheme)
+ {
+ string defaultHost = _configuration["AppSettings:DefaultHost"];
+ if (string.IsNullOrEmpty(defaultHost))
+ {
+ throw new InvalidOperationException("DefaultHost configuration is missing.");
+ }
+
+ string ProjectCreationVersion = _configuration["AppSettings:ProjectCreationVersion"];
+ if (ProjectCreationVersion == null)
+ {
+ throw new InvalidOperationException("ProjectCreationVersion configuration is missing.");
+ }
+
+ ADOConfiguration config = new ADOConfiguration() { AccountName = accname, PersonalAccessToken = pat, UriString = defaultHost + accname, VersionNumber = ProjectCreationVersion, _adoAuthScheme = authScheme };
+ Projects projects = new Projects(config);
+ HttpResponseMessage response = projects.GetListOfProjects();
+ return response;
+ }
+
+
+ public async Task> SelectProject(string accessToken, HttpResponseMessage projectsData)
+ {
+ var projectsJson = JObject.Parse(await projectsData.Content.ReadAsStringAsync());
+ List projectDetails = new List();
+ return await Task.Run(() =>
+ {
+ if (projectsJson["count"].Value() > 0)
+ {
+ Console.ForegroundColor = ConsoleColor.Green;
+ Console.WriteLine(Environment.NewLine + "Select an Project:");
+ Console.ResetColor();
+ var projects = projectsJson["value"];
+ Console.ForegroundColor = ConsoleColor.Yellow;
+ Console.WriteLine("+-----+--------------------------------+--------------------------------------+");
+ Console.WriteLine("| No | Project Name | Project ID |");
+ Console.WriteLine("+-----+--------------------------------+--------------------------------------+");
+ for (int i = 0; i < projects.Count(); i++)
+ {
+ string projectName = projects[i]["name"].ToString();
+ string projectId = projects[i]["id"].ToString();
+
+ // Wrap text if needed for Project Name
+ if (projectName.Length > 30)
+ {
+ string wrappedName = projectName.Substring(0, 30);
+ Console.WriteLine($"| {i + 1,-3} | {wrappedName.PadRight(30)} | {projectId.PadRight(36)} |");
+ projectName = projectName.Substring(30);
+ while (projectName.Length > 0)
+ {
+ wrappedName = projectName.Length > 30 ? projectName.Substring(0, 30) : projectName;
+ Console.WriteLine($"| | {wrappedName.PadRight(30)} | {"".PadRight(36)} |");
+ projectName = projectName.Length > 30 ? projectName.Substring(30) : string.Empty;
+ }
+ }
+ else
+ {
+ Console.WriteLine($"| {i + 1,-3} | {projectName.PadRight(30)} | {projectId.PadRight(36)} |");
+ }
+
+ // Wrap text if needed for Project ID
+ if (projectId.Length > 36)
+ {
+ string wrappedId = projectId.Substring(0, 36);
+ Console.WriteLine($"| | {"".PadRight(30)} | {wrappedId.PadRight(36)} |");
+ projectId = projectId.Substring(36);
+ while (projectId.Length > 0)
+ {
+ wrappedId = projectId.Length > 36 ? projectId.Substring(0, 36) : projectId;
+ Console.WriteLine($"| | {"".PadRight(30)} | {wrappedId.PadRight(36)} |");
+ projectId = projectId.Length > 36 ? projectId.Substring(36) : string.Empty;
+ }
+ }
+ }
+ Console.WriteLine("+-----+--------------------------------+--------------------------------------+");
+ Console.ResetColor();
+ Console.ForegroundColor = ConsoleColor.Green;
+ Console.WriteLine("Please select a project that uses the standard Scrum or Agile process template");
+ Console.ResetColor();
+ int selectedIndex;
+ do
+ {
+ Console.ForegroundColor= ConsoleColor.Green;
+ Console.Write(Environment.NewLine+"Enter the number of the project: ");
+ Console.ResetColor();
+ } while (!int.TryParse(Console.ReadLine(), out selectedIndex) || selectedIndex < 1 || selectedIndex > projects.Count());
+
+ projectDetails.Add(projects[selectedIndex - 1]["id"].ToString());
+ projectDetails.Add(projects[selectedIndex - 1]["name"].ToString());
+ return projectDetails;
+ }
+ else
+ {
+ Console.ForegroundColor = ConsoleColor.Red;
+ Console.WriteLine("No organizations found.");
+ Console.ResetColor();
+ }
+ return null;
+ });
+ }
+
///
/// Get the path where we can file template related json files for selected template
///
diff --git a/src/ADOGenerator/Services/TemplateService.cs b/src/ADOGenerator/Services/TemplateService.cs
index c10927b..608c8b3 100644
--- a/src/ADOGenerator/Services/TemplateService.cs
+++ b/src/ADOGenerator/Services/TemplateService.cs
@@ -1,9 +1,140 @@
using ADOGenerator.IServices;
+using ADOGenerator.Models;
+using ADOGenerator.Services;
+using Microsoft.Extensions.Configuration;
+using RestAPI.Extractor;
+using RestAPI.ProjectsAndTeams;
+using RestAPI;
+using ADOGenerator;
-namespace ADOGenerator.Services
+public class TemplateService : ITemplateService
{
- public class TemplateService : ITemplateService
+ private readonly IConfiguration _config;
+ private readonly IExtractorService extractorService;
+
+ public TemplateService(IConfiguration config)
{
- public TemplateService() { }
+ _config = config;
+ extractorService = new ExtractorService(_config);
+ }
+
+ public bool AnalyzeProject(Project model)
+ {
+ try
+ {
+ string defaultHost = _config["AppSettings:DefaultHost"];
+ string projectPropertyVersion = _config["AppSettings:ProjectPropertyVersion"];
+
+ ADOConfiguration config = new ADOConfiguration
+ {
+ AccountName = model.accountName,
+ PersonalAccessToken = model.accessToken,
+ UriString = defaultHost + model.accountName,
+ VersionNumber = projectPropertyVersion,
+ ProjectId = model.ProjectId,
+ _adoAuthScheme = model.adoAuthScheme
+ };
+
+ Projects projects = new Projects(config);
+ ProjectProperties.Properties load = projects.GetProjectProperties();
+ model.ProcessTemplate = load.value[4].value;
+ ExtractorService es = new ExtractorService(_config);
+ ExtractorAnalysis analysis = new ExtractorAnalysis();
+ ProjectConfigurations appConfig = extractorService.ProjectConfiguration(model);
+ analysis.teamCount = extractorService.GetTeamsCount(appConfig);
+ analysis.IterationCount = extractorService.GetIterationsCount(appConfig);
+ analysis.WorkItemCounts = extractorService.GetWorkItemsCount(appConfig);
+ analysis.BuildDefCount = extractorService.GetBuildDefinitionCount(appConfig);
+ analysis.ReleaseDefCount = extractorService.GetReleaseDefinitionCount(appConfig);
+ analysis.ErrorMessages = es.errorMessages;
+
+ LogAnalysisResults(model, analysis);
+
+ return true;
+ }
+ catch (Exception ex)
+ {
+ model.id.ErrorId().AddMessage("Error during project analysis: " + ex.Message);
+ return false;
+ }
+ }
+ public bool CheckTemplateExists(Project model)
+ {
+ try
+ {
+ if (extractorService.IsTemplateExists(model.ProjectName))
+ {
+ model.id.AddMessage("Template already exists.");
+ return true; // Template exists
+ }
+ else
+ {
+ model.id.AddMessage("Template does not exist.");
+ return false; // Template does not exist
+ }
+ }
+ catch (Exception ex)
+ {
+ model.id.ErrorId().AddMessage("Error checking template existence: " + ex.Message);
+ return false; // Error occurred while checking template existence
+ }
+ }
+
+ public (bool,string,string) GenerateTemplateArtifacts(Project model)
+ {
+ try
+ {
+ string[] createdTemplate = extractorService.GenerateTemplateArifacts(model);
+ if (createdTemplate == null || createdTemplate.Length == 0)
+ {
+ model.id.AddMessage("No artifacts were generated.");
+ return (false, string.Empty,string.Empty); // No artifacts generated
+ }
+ string template = createdTemplate[1];
+ string templateLocation = createdTemplate[2];
+ return (true,template, templateLocation); // Artifact generation completed successfully
+ }
+ catch (Exception ex)
+ {
+ model.id.ErrorId().AddMessage("Error during artifact generation: " + ex.Message);
+ return (false, string.Empty, string.Empty); // Artifact generation failed
+ }
+ }
+
+ private void LogAnalysisResults(Project model, ExtractorAnalysis analysis)
+ {
+ model.id.AddMessage(Environment.NewLine + "-------------------------------------------------------------------");
+ model.id.AddMessage("| Analysis of the project |");
+ model.id.AddMessage("|-----------------------------------------------------------------|");
+ model.id.AddMessage($"| {"Property",-30} | {"Value",-30} |");
+ model.id.AddMessage("|-----------------------------------------------------------------|");
+ model.id.AddMessage($"| {"Project Name",-30} | {model.ProjectName.PadRight(30)} |");
+ model.id.AddMessage($"| {"Process Template Type",-30} | {model.ProcessTemplate.PadRight(30)} |");
+ model.id.AddMessage($"| {"Teams Count",-30} | {analysis.teamCount.ToString().PadRight(30)} |");
+ model.id.AddMessage($"| {"Iterations Count",-30} | {analysis.IterationCount.ToString().PadRight(30)} |");
+ model.id.AddMessage($"| {"Build Definitions Count",-30} | {analysis.BuildDefCount.ToString().PadRight(30)} |");
+ model.id.AddMessage($"| {"Release Definitions Count",-30} | {analysis.ReleaseDefCount.ToString().PadRight(30)} |");
+
+ if (analysis.WorkItemCounts.Count > 0)
+ {
+ model.id.AddMessage("|-----------------------------------------------------------------|");
+ model.id.AddMessage("| Work Items Count: |");
+ model.id.AddMessage("|-----------------------------------------------------------------|");
+ foreach (var item in analysis.WorkItemCounts)
+ {
+ model.id.AddMessage($"| {item.Key.PadRight(30)} | {item.Value.ToString().PadRight(30)} |");
+ }
+ }
+ if (analysis.ErrorMessages.Count > 0)
+ {
+ model.id.AddMessage("|-----------------------------------------------------------------|");
+ model.id.AddMessage("| Errors: |");
+ model.id.AddMessage("|-----------------------------------------------------------------|");
+ foreach (var item in analysis.ErrorMessages)
+ {
+ model.id.AddMessage($"| {item.PadRight(60)} |");
+ }
+ }
+ model.id.AddMessage("-------------------------------------------------------------------");
}
}
diff --git a/src/API/Extractor/GetWorkItemsCount.cs b/src/API/Extractor/GetWorkItemsCount.cs
index a16b0fc..2afaf0b 100644
--- a/src/API/Extractor/GetWorkItemsCount.cs
+++ b/src/API/Extractor/GetWorkItemsCount.cs
@@ -99,7 +99,7 @@ public WorkItemFetchResponse.WorkItems GetWorkItemsDetailInBatch(string workitem
HttpResponseMessage response = client.GetAsync(_configuration.UriString + "/_apis/wit/workitems?api-version=" + _configuration.VersionNumber + "&ids=" + workitemstoFetch + "&$expand=relations").Result;
if (response.IsSuccessStatusCode && response.StatusCode == System.Net.HttpStatusCode.OK)
{
- viewModel = response.Content.ReadFromJsonAsync().Result;
+ viewModel = JsonConvert.DeserializeObject(response.Content.ReadAsStringAsync().Result);
}
else
{