From 07a7e7f5b817e98a667a729bb2bb048dc3f615b7 Mon Sep 17 00:00:00 2001 From: Priya Solanki Date: Wed, 2 Apr 2025 00:52:09 +0530 Subject: [PATCH 1/4] demo generator changes to create artifacts. --- src/ADOGenerator/ADOGenerator.csproj | 5 + .../IServices/IExtractorService.cs | 32 + src/ADOGenerator/IServices/IProjectService.cs | 3 + .../IServices/ITemplateService.cs | 6 +- src/ADOGenerator/Models/ExtractorAnalysis.cs | 18 + src/ADOGenerator/Program.cs | 356 ++-- src/ADOGenerator/Services/ExtractorService.cs | 1657 +++++++++++++++++ src/ADOGenerator/Services/ProjectService.cs | 55 + src/ADOGenerator/Services/TemplateService.cs | 71 +- 9 files changed, 2075 insertions(+), 128 deletions(-) create mode 100644 src/ADOGenerator/IServices/IExtractorService.cs create mode 100644 src/ADOGenerator/Models/ExtractorAnalysis.cs create mode 100644 src/ADOGenerator/Services/ExtractorService.cs diff --git a/src/ADOGenerator/ADOGenerator.csproj b/src/ADOGenerator/ADOGenerator.csproj index 34dac0a..f9229e9 100644 --- a/src/ADOGenerator/ADOGenerator.csproj +++ b/src/ADOGenerator/ADOGenerator.csproj @@ -9,6 +9,7 @@ + @@ -87,4 +88,8 @@ + + + + diff --git a/src/ADOGenerator/IServices/IExtractorService.cs b/src/ADOGenerator/IServices/IExtractorService.cs new file mode 100644 index 0000000..1546ba1 --- /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); + void ExportQuries(ProjectConfigurations appConfig); + bool ExportTeams(RestAPI.ADOConfiguration con, Project model); + bool ExportIterations(ProjectConfigurations appConfig); + void ExportWorkItems(ProjectConfigurations appConfig); + void ExportRepositoryList(ProjectConfigurations appConfig); + int GetBuildDefinitions(ProjectConfigurations appConfig); + int GeneralizingGetReleaseDefinitions(ProjectConfigurations appConfig); + void GetServiceEndpoints(ProjectConfigurations appConfig); + void ExportDeliveryPlans(ProjectConfigurations appConfig); + + } +} 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..02cb0ec 100644 --- a/src/ADOGenerator/IServices/ITemplateService.cs +++ b/src/ADOGenerator/IServices/ITemplateService.cs @@ -1,6 +1,10 @@ -namespace ADOGenerator.IServices +using ADOGenerator.Models; + +namespace ADOGenerator.IServices { public interface ITemplateService { + bool AnalyzeProject(Project model); + bool StartEnvironmentSetupProcess(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 77385dd..872a001 100644 --- a/src/ADOGenerator/Program.cs +++ b/src/ADOGenerator/Program.cs @@ -5,6 +5,7 @@ using Microsoft.Extensions.Configuration; using Microsoft.Identity.Client; using Newtonsoft.Json.Linq; +using RestAPI; using System.Text.RegularExpressions; var configuration = new ConfigurationBuilder() @@ -16,174 +17,277 @@ do { - string id = Guid.NewGuid().ToString().Split('-')[0]; - Init init = new Init(); - id.AddMessage("Template Details"); - var templatePath = Path.Combine(Directory.GetCurrentDirectory(), "Templates", "TemplateSetting.json"); + 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 a new Artifacts using exsisting project."); + var userChoiceTemplate = Console.ReadLine(); - if (!File.Exists(templatePath)) + if (userChoiceTemplate == "1") { - id.ErrorId().AddMessage("TemplateSettings.json file not found."); - break; - } + string id = Guid.NewGuid().ToString().Split('-')[0]; + Init init = new Init(); + id.AddMessage("Template Details"); + var templatePath = Path.Combine(Directory.GetCurrentDirectory(), "Templates", "TemplateSetting.json"); - var templateSettings = File.ReadAllText(templatePath); - var json = JObject.Parse(templateSettings); - var groupwiseTemplates = json["GroupwiseTemplates"]; + if (!File.Exists(templatePath)) + { + id.ErrorId().AddMessage("TemplateSettings.json file not found."); + break; + } - if (groupwiseTemplates == null) - { - id.ErrorId().AddMessage("No templates found."); - break; - } + var templateSettings = File.ReadAllText(templatePath); + var json = JObject.Parse(templateSettings); + var groupwiseTemplates = json["GroupwiseTemplates"]; - int templateIndex = 1; - var templateDictionary = new Dictionary(); + if (groupwiseTemplates == null) + { + id.ErrorId().AddMessage("No templates found."); + break; + } - foreach (var group in groupwiseTemplates) - { - var groupName = group["Groups"]?.ToString(); - Console.WriteLine(groupName); + int templateIndex = 1; + var templateDictionary = new Dictionary(); - var templates = group["Template"]; - if (templates != null) + foreach (var group in groupwiseTemplates) { - foreach (var template in templates) + var groupName = group["Groups"]?.ToString(); + Console.WriteLine(groupName); + + var templates = group["Template"]; + if (templates != null) { - var templateName = template["Name"]?.ToString(); - Console.WriteLine($" {templateIndex}. {templateName}"); - templateDictionary.Add(templateIndex, templateName); - templateIndex++; + foreach (var template in templates) + { + var templateName = template["Name"]?.ToString(); + Console.WriteLine($" {templateIndex}. {templateName}"); + templateDictionary.Add(templateIndex, templateName); + templateIndex++; + } } } - } - // option 1 : Authenticate using Device Login using AD auth - // option 2: using PAT - // let user decide which approach they want to select - // if option 1, invoke the methods from AuthService - // if option 2, continue with below code + // option 1 : Authenticate using Device Login using AD auth + // option 2: using PAT + // let user decide which approach they want to select + // if option 1, invoke the methods from AuthService + // if option 2, continue with below code - 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)) - { - id.AddMessage("Invalid template number entered."); - continue; - } - selectedTemplateName = selectedTemplateName.Trim(); - if (!TryGetTemplateDetails(groupwiseTemplates, selectedTemplateName, out var templateFolder, out var confirmedExtension, id)) - { - 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) + 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)) { - id.AddMessage("Exiting the application."); - return 0; + id.AddMessage("Invalid template number entered."); + continue; + } + selectedTemplateName = selectedTemplateName.Trim(); + if (!TryGetTemplateDetails(groupwiseTemplates, selectedTemplateName, out var templateFolder, out var confirmedExtension, id)) + { + 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) + { + id.AddMessage("Exiting the application."); + return 0; + } + continue; + } + else + { + id.AddMessage($"Selected template: {selectedTemplateName}"); + ValidateExtensions(templateFolder, id); } - continue; - } - else - { - id.AddMessage($"Selected template: {selectedTemplateName}"); - ValidateExtensions(templateFolder, id); - } id.AddMessage("Choose authentication method: 1. Device Login using AD auth 2. Personal Access Token (PAT)"); - var authChoice = Console.ReadLine(); + var authChoice = Console.ReadLine(); - string accessToken = string.Empty; - string organizationName = string.Empty; - string authScheme = string.Empty; + string accessToken = string.Empty; + string organizationName = 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 = await app.GetAccountsAsync(); - if (accounts.Any()) + if (authChoice == "1") { - try + var app = PublicClientApplicationBuilder.Create(AuthService.clientId) + .WithAuthority(AuthService.authority) + .WithDefaultRedirectUri() + .Build(); + AuthService authService = new AuthService(); + + var accounts = await app.GetAccountsAsync(); + if (accounts.Any()) { - var result = await app.AcquireTokenSilent(AuthService.scopes, accounts.FirstOrDefault()) - .WithForceRefresh(true) - .ExecuteAsync(); - accessToken = result.AccessToken; + try + { + var result = await app.AcquireTokenSilent(AuthService.scopes, accounts.FirstOrDefault()) + .WithForceRefresh(true) + .ExecuteAsync(); + accessToken = result.AccessToken; + } + catch (MsalUiRequiredException) + { + var result = await authService.AcquireTokenAsync(app); + accessToken = result.AccessToken; + } } - catch (MsalUiRequiredException) + 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"; + } - else + else if (authChoice == "2") { - var result = await authService.AcquireTokenAsync(app); - accessToken = result.AccessToken; + id.AddMessage("Enter your Azure DevOps organization name:"); + organizationName = Console.ReadLine(); + + id.AddMessage("Enter your Azure DevOps personal access token:"); + accessToken = init.ReadSecret(); + + authScheme = "Basic"; } + string projectName = ""; + do + { + id.AddMessage("Enter the new project name:"); + projectName = Console.ReadLine(); + if (!init.CheckProjectName(projectName)) + { + 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; + } + } while (string.IsNullOrWhiteSpace(projectName)); - var memberId = await authService.GetProfileInfoAsync(accessToken); - var organizations = await authService.GetOrganizationsAsync(accessToken, memberId); - organizationName = await authService.SelectOrganization(accessToken, organizations); - authScheme = "Bearer"; + if (string.IsNullOrWhiteSpace(organizationName) || string.IsNullOrWhiteSpace(accessToken) || string.IsNullOrWhiteSpace(projectName)) + { + id.ErrorId().AddMessage("Validation error: All inputs must be provided. Exiting.."); + Environment.Exit(1); + } + 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); } - else if (authChoice == "2") + else if (userChoiceTemplate == "2") { - id.AddMessage("Enter your Azure DevOps organization name:"); - organizationName = Console.ReadLine(); + string id = Guid.NewGuid().ToString().Split('-')[0]; + Init init = new Init(); + id.AddMessage("Template Details"); + id.AddMessage("Choose authentication method: 1. Device Login using AD auth 2. Personal Access Token (PAT)"); + var authChoice = Console.ReadLine(); - id.AddMessage("Enter your Azure DevOps personal access token:"); - accessToken = init.ReadSecret(); + string accessToken = string.Empty; + string organizationName = string.Empty; + string authScheme = string.Empty; - authScheme = "Basic"; - } - string projectName = ""; - do - { - id.AddMessage("Enter the new project name:"); - projectName = Console.ReadLine(); - if (!init.CheckProjectName(projectName)) + if (authChoice == "1") { - 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) + var app = PublicClientApplicationBuilder.Create(AuthService.clientId) + .WithAuthority(AuthService.authority) + .WithDefaultRedirectUri() + .Build(); + AuthService authService = new AuthService(); + + var accounts = await app.GetAccountsAsync(); + if (accounts.Any()) { - id.AddMessage("Exiting the application."); - Environment.Exit(1); + try + { + var result = await app.AcquireTokenSilent(AuthService.scopes, accounts.FirstOrDefault()) + .WithForceRefresh(true) + .ExecuteAsync(); + accessToken = result.AccessToken; + } + catch (MsalUiRequiredException) + { + var result = await authService.AcquireTokenAsync(app); + accessToken = result.AccessToken; + } } - projectName = ""; - continue; + 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"; } - } while (string.IsNullOrWhiteSpace(projectName)); + else if (authChoice == "2") + { + id.AddMessage("Enter your Azure DevOps organization name:"); + organizationName = Console.ReadLine(); - if (string.IsNullOrWhiteSpace(organizationName) || string.IsNullOrWhiteSpace(accessToken) || string.IsNullOrWhiteSpace(projectName)) - { - id.ErrorId().AddMessage("Validation error: All inputs must be provided. Exiting.."); - Environment.Exit(1); - } + id.AddMessage("Enter your Azure DevOps personal access token:"); + accessToken = init.ReadSecret(); - 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); + authScheme = "Basic"; + } + string projectName = ""; + + IProjectService projService = new ProjectService(configuration); + var projects = projService.GetProjects(organizationName, accessToken, authScheme); + var projectDetails = await projService.SelectProject(accessToken, projects); + projectName = projectDetails[1]; + + if (string.IsNullOrWhiteSpace(organizationName) || string.IsNullOrWhiteSpace(accessToken) || string.IsNullOrWhiteSpace(projectName)) + { + id.ErrorId().AddMessage("Validation error: All inputs must be provided. Exiting.."); + Environment.Exit(1); + } + //var trackingId = Guid.NewGuid().ToString().Split('-')[0]; + Project model = new Project(); + model.accountName = organizationName; + model.ProjectName = projectName; + model.ProjectId = projectDetails[0]; + model.accessToken = accessToken; + model.adoAuthScheme = authScheme; + model.id = id; + ITemplateService templateService = new TemplateService(configuration); + var analyzed = templateService.AnalyzeProject(model); + if (analyzed) + { + id.AddMessage("Artifacts generated successfully."); + //templateService.StartEnvironmentSetupProcess(model); + + } + else + { + id.ErrorId().AddMessage("Artifacts generation failed."); + } + } + else + { + Console.WriteLine("Invalid choice. Please select either 1 or 2."); + continue; + } } while (true); bool TryGetTemplateDetails(JToken groupwiseTemplates, string selectedTemplateName, out string templateFolder, out bool confirmedExtension, string id) diff --git a/src/ADOGenerator/Services/ExtractorService.cs b/src/ADOGenerator/Services/ExtractorService.cs new file mode 100644 index 0000000..042035a --- /dev/null +++ b/src/ADOGenerator/Services/ExtractorService.cs @@ -0,0 +1,1657 @@ +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 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; + } + + public static void AddMessage(string id, string message) + { + lock (objLock) + { + if (id.EndsWith("_Errors")) + { + StatusMessages[id] = (StatusMessages.ContainsKey(id) ? StatusMessages[id] : string.Empty) + message; + } + else + { + StatusMessages[id] = message; + } + } + } + public static Dictionary StatusMessages + { + get + { + if (statusMessages == null) + { + statusMessages = new Dictionary(); + } + + return statusMessages; + } + set + { + statusMessages = value; + } + } + public static string GetStatusMessage(string id) + { + lock (objLock) + { + string message = string.Empty; + if (StatusMessages.Keys.Count(x => x == id) == 1) + { + message = StatusMessages[id]; + } + else + { + return "100"; + } + + if (id.EndsWith("_Errors")) + { + RemoveKey(id); + } + + return message; + } + } + + public static void RemoveKey(string id) + { + lock (objLock) + { + StatusMessages.Remove(id); + } + } + + #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 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; + } + #endregion ANALYSIS - GET COUNTS + + #region GENERATE ARTIFACTS + public string[] GenerateTemplateArifacts(Project model) + { + extractedTemplatePath = currentPath + @"ExtractedTemplate\"; + + if (Directory.Exists(extractedTemplatePath)) + { + string[] subdirs = Directory.GetDirectories(extractedTemplatePath) + .Select(Path.GetFileName) + .ToArray(); + foreach (string folderName in subdirs) + { + DirectoryInfo d = new DirectoryInfo(extractedTemplatePath + folderName); + if (d.CreationTime < DateTime.Now.AddHours(-1)) + Directory.Delete(extractedTemplatePath + folderName, true); + } + } + + AddMessage(model.id, ""); + ProjectConfigurations appConfig = ProjectConfiguration(model); + + GetInstalledExtensions(appConfig); + + ExportQuries(appConfig); + ExportTeams(appConfig.BoardConfig, model); + + if (ExportIterations(appConfig)) + { + AddMessage(model.id, "Iterations Definition"); + } + string extractedFolderName = extractedTemplatePath + model.ProjectName; + 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); + AddMessage(model.id, "Team Areas"); + + ExportWorkItems(appConfig); + AddMessage(model.id, "Work Items"); + + ExportDeliveryPlans(appConfig); + AddMessage(model.id, "Delivery Plans"); + + ExportRepositoryList(appConfig); + AddMessage(model.id, "Repository and Service Endpoint"); + + GetServiceEndpoints(appConfig); + int count = GetBuildDefinitions(appConfig); + if (count >= 1) + { + AddMessage(model.id, "Build Definition"); + } + + int relCount = GeneralizingGetReleaseDefinitions(appConfig); + if (relCount >= 1) + { + AddMessage(model.id, "Release Definition"); + } + + StatusMessages[model.id] = "100"; + return new string[] { model.id, "" }; + } + + 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; + } + + public List GetInstalledExtensions(ProjectConfigurations appConfig) + { + 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(extractedTemplatePath + appConfig.ExtensionConfig.Project)) + { + Directory.CreateDirectory(extractedTemplatePath + appConfig.ExtensionConfig.Project); + } + string fetchedJson = JsonConvert.SerializeObject(listExtension, Formatting.Indented); + + File.WriteAllText(extractedTemplatePath + appConfig.ExtensionConfig.Project + "\\Extensions.json", JsonConvert.SerializeObject(listExtension, Formatting.Indented)); + } + } + else if (!string.IsNullOrEmpty(listExtenison.LastFailureMessage)) + { + AddMessage(appConfig.ExtensionConfig.Id.ErrorId(), "Some error occured while fetching extensions"); + } + return extensionList; + } + catch (Exception ex) + { + logger.Info(DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss") + "\t" + ex.Message + "\n" + ex.StackTrace + "\n"); + } + return new List(); + } + + public void ExportQuries(ProjectConfigurations appConfig) + { + 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(extractedTemplatePath + appConfig.QueriesConfig.Project + "\\Dashboard\\Queries")) + { + Directory.CreateDirectory(extractedTemplatePath + appConfig.QueriesConfig.Project + "\\Dashboard"); + File.WriteAllText(extractedTemplatePath + appConfig.QueriesConfig.Project + "\\Dashboard\\Dashboard.json", JsonConvert.SerializeObject("text", Formatting.Indented)); + } + if (!Directory.Exists(extractedTemplatePath + appConfig.QueriesConfig.Project + "\\Dashboard\\Queries")) + { + Directory.CreateDirectory(extractedTemplatePath + appConfig.QueriesConfig.Project + "\\Dashboard\\Queries"); + File.WriteAllText(extractedTemplatePath + appConfig.QueriesConfig.Project + "\\Dashboard\\Queries\\" + query.name + ".json", JsonConvert.SerializeObject(jobj, Formatting.Indented)); + } + else + { + File.WriteAllText(extractedTemplatePath + appConfig.QueriesConfig.Project + "\\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(extractedTemplatePath + appConfig.QueriesConfig.Project + "\\Dashboard\\Queries")) + { + Directory.CreateDirectory(extractedTemplatePath + appConfig.QueriesConfig.Project + "\\Dashboard\\Queries"); + + File.WriteAllText(extractedTemplatePath + appConfig.QueriesConfig.Project + "\\Dashboard\\Queries\\" + child1.name + ".json", JsonConvert.SerializeObject(jobj, Formatting.Indented)); + } + else + { + File.WriteAllText(extractedTemplatePath + appConfig.QueriesConfig.Project + "\\Dashboard\\Queries\\" + child1.name + ".json", JsonConvert.SerializeObject(jobj, Formatting.Indented)); + } + } + } + } + } + } + } + } + else if (!string.IsNullOrEmpty(queries.LastFailureMessage)) + { + AddMessage(appConfig.QueriesConfig.Id.ErrorId(), "Error while fetching queries"); + } + } + catch (Exception ex) + { + logger.Info(DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss") + "\t" + ex.Message + "\n" + ex.StackTrace + "\n"); + } + + } + + public bool ExportTeams(Configuration con, Project model) + { + 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) + { + AddMessage(con.Id, "Teams"); + + string fetchedJson = JsonConvert.SerializeObject(_team.value, Formatting.Indented); + if (fetchedJson != "") + { + if (!Directory.Exists(extractedTemplatePath + con.Project + "\\Teams")) + { + Directory.CreateDirectory(extractedTemplatePath + con.Project + "\\Teams"); + } + File.WriteAllText(extractedTemplatePath + con.Project + "\\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 = extractedTemplatePath + con.Project + "\\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); + } + AddMessage(con.Id, "Board Columns"); + Thread.Sleep(2000); + } + else + { + var errorMessage = response.Content.ReadAsStringAsync(); + string error = RestAPI.Utility.GeterroMessage(errorMessage.Result.ToString()); + teamNodes.LastFailureMessage = error; + AddMessage(con.Id.ErrorId(), "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); + AddMessage(con.Id, "Board Rows"); + Thread.Sleep(2000); + } + else if (!string.IsNullOrEmpty(teamNodes.LastFailureMessage)) + { + AddMessage(con.Id.ErrorId(), "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); + AddMessage(con.Id, "Card fields Definition"); + + } + else + { + var errorMessage = cardFieldResponse.Content.ReadAsStringAsync(); + string error = RestAPI.Utility.GeterroMessage(errorMessage.Result.ToString()); + teamNodes.LastFailureMessage = error; + AddMessage(con.Id.ErrorId(), "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); + AddMessage(con.Id, "Card style"); + + } + else + { + var errorMessage = cardStyleResponse.Content.ReadAsStringAsync(); + string error = RestAPI.Utility.GeterroMessage(errorMessage.Result.ToString()); + teamNodes.LastFailureMessage = error; + AddMessage(con.Id.ErrorId(), "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; + AddMessage(con.Id, "Team Settings Definition"); + } + } + else if (!string.IsNullOrEmpty(teamNodes.LastFailureMessage)) + { + AddMessage(con.Id.ErrorId(), "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)) + { + AddMessage(con.Id.ErrorId(), nodes.LastFailureMessage); + string error = nodes.LastFailureMessage; + return false; + } + else + { + return false; + } + } + else + { + AddMessage(con.Id.ErrorId(), nodes.LastFailureMessage); + return false; + } + } + catch (Exception ex) + { + logger.Info(DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss") + "\t" + ex.Message + "\n" + ex.StackTrace + "\n"); + } + return false; + } + + public bool ExportIterations(ProjectConfigurations appConfig) + { + 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(extractedTemplatePath + appConfig.BoardConfig.Project)) + { + Directory.CreateDirectory(extractedTemplatePath + appConfig.BoardConfig.Project); + } + File.WriteAllText(extractedTemplatePath + appConfig.BoardConfig.Project + "\\Iterations.json", fetchedJson); + return true; + } + else + { + string error = nodes.LastFailureMessage; + AddMessage(appConfig.BoardConfig.Id.ErrorId(), error); + return false; + } + } + catch (Exception ex) + { + logger.Info(DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss") + "\t" + ex.Message + "\n" + ex.StackTrace + "\n"); + } + return false; + } + + public void ExportWorkItems(ProjectConfigurations appConfig) + { + string[] workItemtypes = GetAllWorkItemsName(appConfig);//{ "Epic", "Feature", "Product Backlog Item", "Task", "Test Case", "Bug", "User Story", "Test Suite", "Test Plan", "Issue" }; + if (!Directory.Exists(extractedTemplatePath + appConfig.WorkItemConfig.Project)) + { + Directory.CreateDirectory(extractedTemplatePath + appConfig.WorkItemConfig.Project); + } + + 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(extractedTemplatePath + appConfig.WorkItemConfig.Project + "\\WorkItems")) + { + Directory.CreateDirectory(extractedTemplatePath + appConfig.WorkItemConfig.Project + "\\WorkItems"); + } + File.WriteAllText(extractedTemplatePath + appConfig.WorkItemConfig.Project + "\\WorkItems\\" + item + ".json", workItemJson); + } + else if (!string.IsNullOrEmpty(WorkitemsCount.LastFailureMessage)) + { + AddMessage(appConfig.WorkItemConfig.Id.ErrorId(), WorkitemsCount.LastFailureMessage); + } + } + } + } + + public void ExportRepositoryList(ProjectConfigurations appConfig) + { + 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 templateFolderPath = extractedTemplatePath + appConfig.RepoConfig.Project; + 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(templateFolderPath + "\\ImportSourceCode")) + { + Directory.CreateDirectory(templateFolderPath + "\\ImportSourceCode"); + File.WriteAllText(templateFolderPath + "\\ImportSourceCode\\" + repo.name + ".json", sourceCodeJson); + } + else + { + File.WriteAllText(templateFolderPath + "\\ImportSourceCode\\" + repo.name + ".json", sourceCodeJson); + } + if (!Directory.Exists(templateFolderPath + "\\ServiceEndpoints")) + { + Directory.CreateDirectory(templateFolderPath + "\\ServiceEndpoints"); + File.WriteAllText(templateFolderPath + "\\ServiceEndpoints\\" + repo.name + "-code.json", endPointJson); + } + else + { + File.WriteAllText(templateFolderPath + "\\ServiceEndpoints\\" + repo.name + "-code.json", endPointJson); + } + } + } + } + + /// + /// Get the Build definitions to write into file + /// + /// + /// + public int GetBuildDefinitions(ProjectConfigurations appConfig) + { + try + { + BuildandReleaseDefs buildandReleaseDefs = new BuildandReleaseDefs(appConfig.BuildDefinitionConfig); + List builds = buildandReleaseDefs.ExportBuildDefinitions(); + BuildandReleaseDefs repoDefs = new BuildandReleaseDefs(appConfig.RepoConfig); + Dictionary variableGroupNameId = GetVariableGroups(appConfig); + RepositoryList.Repository repo = repoDefs.GetRepoList(); + if (builds.Count > 0) + { + int count = 1; + //creating ImportCode Json file + string templatePath = extractedTemplatePath + appConfig.BuildDefinitionConfig.Project; + 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, templatePath, def, fileName, type); + } + #endregion + + #region YML PIPELINE WITH GITHUB + else if (yamalfilename != null && type.ToString().ToLower() == "github") + { + count = YmlWithGitHub(appConfig, count, templatePath, def, fileName, type); + } + #endregion + + #region OTHER + else if (yamalfilename == null) + { + count = NormalPipeline(appConfig, count, templatePath, 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(extractedTemplatePath + appConfig.BuildDefinitionConfig.Project + "\\ImportSourceCode")) + { + Directory.CreateDirectory(extractedTemplatePath + appConfig.BuildDefinitionConfig.Project + "\\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(extractedTemplatePath + appConfig.BuildDefinitionConfig.Project + "\\ImportSourceCode\\GitRepository.json")) + { + string readrepo = File.ReadAllText(extractedTemplatePath + appConfig.BuildDefinitionConfig.Project + "\\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(extractedTemplatePath + appConfig.BuildDefinitionConfig.Project + "\\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(extractedTemplatePath + appConfig.BuildDefinitionConfig.Project + "\\ServiceEndpoints")) + { + Directory.CreateDirectory(extractedTemplatePath + appConfig.BuildDefinitionConfig.Project + "\\ServiceEndpoints"); + File.WriteAllText(extractedTemplatePath + appConfig.BuildDefinitionConfig.Project + "\\ServiceEndpoints\\GitHub_" + randStr + "-EndPoint.json", endPointString); + } + else + { + File.WriteAllText(extractedTemplatePath + appConfig.BuildDefinitionConfig.Project + "\\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(extractedTemplatePath + appConfig.BuildDefinitionConfig.Project + "\\ServiceEndpoints")) + { + Directory.CreateDirectory(extractedTemplatePath + appConfig.BuildDefinitionConfig.Project + "\\ServiceEndpoints"); + File.WriteAllText(extractedTemplatePath + appConfig.BuildDefinitionConfig.Project + "\\ServiceEndpoints\\GitHub-" + randStr + "-EndPoint.json", endPointString); + } + else + { + File.WriteAllText(extractedTemplatePath + appConfig.BuildDefinitionConfig.Project + "\\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) + { + 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); + Dictionary queue = agent.GetQueues(); + string templatePath = extractedTemplatePath + appConfig.ReleaseDefinitionConfig.Project; + 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(templatePath + "\\ReleaseDefinitions"))) + { + Directory.CreateDirectory(templatePath + "\\ReleaseDefinitions"); + File.WriteAllText(templatePath + "\\ReleaseDefinitions\\" + name + ".json", JsonConvert.SerializeObject(rel, Formatting.Indented)); + } + else + { + File.WriteAllText(templatePath + "\\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"); + AddMessage(appConfig.ReleaseDefinitionConfig.Id.ErrorId(), 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) + { + 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(extractedTemplatePath + appConfig.EndpointConfig.Project + "\\ServiceEndpoints")) + { + Directory.CreateDirectory(extractedTemplatePath + appConfig.EndpointConfig.Project + "\\ServiceEndpoints"); + File.WriteAllText(extractedTemplatePath + appConfig.EndpointConfig.Project + "\\ServiceEndpoints\\", JsonConvert.SerializeObject(endpoint, Formatting.Indented, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore })); + } + else + { + File.WriteAllText(extractedTemplatePath + appConfig.EndpointConfig.Project + "\\ServiceEndpoints\\" + endpoint.name + ".json", JsonConvert.SerializeObject(endpoint, Formatting.Indented, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore })); + } + } + } + else if (!string.IsNullOrEmpty(serviceEndPoint.LastFailureMessage)) + { + AddMessage(appConfig.EndpointConfig.Id.ErrorId(), "Error occured while fetchin service endpoints"); + } + } + catch (Exception ex) + { + logger.Info(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) + { + VariableGroups variableGroups = new VariableGroups(appConfig.VariableGroupConfig); + GetVariableGroups.Groups groups = variableGroups.GetVariableGroups(); + Dictionary varibaleGroupDictionary = new Dictionary(); + string templatePath = extractedTemplatePath + appConfig.ReleaseDefinitionConfig.Project; + if (groups.count > 0) + { + if (!(Directory.Exists(templatePath + "\\VariableGroups"))) + { + Directory.CreateDirectory(templatePath + "\\VariableGroups"); + File.WriteAllText(templatePath + "\\VariableGroups\\VariableGroup.json", JsonConvert.SerializeObject(groups, Formatting.Indented)); + } + else + { + File.WriteAllText(templatePath + "\\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 void ExportDeliveryPlans(ProjectConfigurations appConfig) + { + try + { + Plans plans = new Plans(appConfig.WorkItemConfig); + GetPlans.Root plansList = plans.GetDeliveryPlans(appConfig.WorkItemConfig.AccountName, appConfig.WorkItemConfig.Project); + if (plansList.count > 0) + { + string templatePath = extractedTemplatePath + appConfig.WorkItemConfig.Project; + + 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(templatePath + "\\DeliveryPlans"))) + { + Directory.CreateDirectory(templatePath + "\\DeliveryPlans"); + File.WriteAllText(templatePath + $"\\DeliveryPlans\\{aplan.name}.json", JsonConvert.SerializeObject(aplan, Formatting.Indented)); + } + else + { + File.WriteAllText(templatePath + $"\\DeliveryPlans\\{aplan.name}.json", JsonConvert.SerializeObject(aplan, Formatting.Indented)); + } + } + } + } + } + catch (Exception ex) + { + + } + + } + #endregion END GENERATE ARTIFACTS + + + } +} diff --git a/src/ADOGenerator/Services/ProjectService.cs b/src/ADOGenerator/Services/ProjectService.cs index 3aba47b..7fd3e68 100644 --- a/src/ADOGenerator/Services/ProjectService.cs +++ b/src/ADOGenerator/Services/ProjectService.cs @@ -72,6 +72,61 @@ 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.WriteLine("Select an Project:"); + var projects = projectsJson["value"]; + for (int i = 0; i < projects.Count(); i++) + { + Console.WriteLine($"{i + 1}. {projects[i]["name"]} (ID: {projects[i]["id"]})"); + } + Console.WriteLine("Please select a project that uses the standard Scrum or Agile process template"); + int selectedIndex; + do + { + Console.Write("Enter the number of the project: "); + } 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.WriteLine("No organizations found."); + } + 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..f7f394a 100644 --- a/src/ADOGenerator/Services/TemplateService.cs +++ b/src/ADOGenerator/Services/TemplateService.cs @@ -1,9 +1,78 @@ using ADOGenerator.IServices; +using ADOGenerator.Models; +using Microsoft.Extensions.Configuration; +using RestAPI; +using RestAPI.Extractor; +using RestAPI.ProjectsAndTeams; +using System.Configuration; namespace ADOGenerator.Services { public class TemplateService : ITemplateService { - public TemplateService() { } + private readonly IConfiguration _config; + IExtractorService extractorService; + public TemplateService(IConfiguration config) { + _config = config; + extractorService = new ExtractorService(_config); + } + + public bool AnalyzeProject(Project model) + { + string defaultHost = _config["AppSettings:DefaultHost"]; + string ProjectPropertyVersion = _config["AppSettings:ProjectPropertyVersion"]; + //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}; + + ProjectProperties.Properties load = new ProjectProperties.Properties(); + Projects projects = new Projects(config); + load = projects.GetProjectProperties(); + model.ProcessTemplate = load.value[4].value; + //model.ProjectId = load.value[0].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; + + Console.WriteLine("Analysis of the project"); + Console.WriteLine("Project Name: " + model.ProjectName); + Console.WriteLine("Processtemplate Type: " + model.ProcessTemplate); + Console.WriteLine("Teams Count: " + analysis.teamCount); + Console.WriteLine("Iterations Count: " + analysis.IterationCount); + Console.WriteLine("Work Items Count: "); + foreach (var item in analysis.WorkItemCounts) + { + Console.WriteLine(item.Key + " : " + item.Value); + } + Console.WriteLine("Build Definitions Count: " + analysis.BuildDefCount); + Console.WriteLine("Release Definitions Count: " + analysis.ReleaseDefCount); + Console.WriteLine("Errors: "); + foreach (var item in analysis.ErrorMessages) + { + Console.WriteLine(item); + } + + Console.WriteLine("Do you want to create artifacts yes/no:"); + string response = Console.ReadLine(); + if (response == "yes") + { + return StartEnvironmentSetupProcess(model); + } + else + { + return false; + } + } + + public bool StartEnvironmentSetupProcess(Project model) + { + extractorService.GenerateTemplateArifacts(model); + return true; + } } } From 98c2deee13d72fe57494ed30b53acd6d2ae5ac07 Mon Sep 17 00:00:00 2001 From: Akshay H Date: Fri, 11 Apr 2025 19:15:38 +0530 Subject: [PATCH 2/4] Enhance Azure DevOps Demo Generator functionality - Added `UpgradeLog.htm` to `.gitignore`. - Updated `ADOGenerator.csproj` to include `ExtractedTemplate` folder and ensure files are copied to output. - Modified `ITemplateService` to change `StartEnvironmentSetupProcess` to `GenerateTemplateArtifacts`, returning a tuple. - Improved user interaction in `Program.cs` with JSON handling and better project creation logic. - Refactored `ExtractorService` for improved path handling and directory creation. - Enhanced `TemplateService` structure and error handling for project analysis and artifact generation. - Overall improvements to functionality, maintainability, and user experience. --- .gitignore | 1 + src/ADOGenerator/ADOGenerator.csproj | 7 +- .../IServices/ITemplateService.cs | 2 +- src/ADOGenerator/Program.cs | 575 ++++++++++-------- src/ADOGenerator/Services/ExtractorService.cs | 46 +- src/ADOGenerator/Services/TemplateService.cs | 115 ++-- 6 files changed, 449 insertions(+), 297 deletions(-) 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 f9229e9..ff06325 100644 --- a/src/ADOGenerator/ADOGenerator.csproj +++ b/src/ADOGenerator/ADOGenerator.csproj @@ -86,10 +86,9 @@ Always - - - - + + Always + diff --git a/src/ADOGenerator/IServices/ITemplateService.cs b/src/ADOGenerator/IServices/ITemplateService.cs index 02cb0ec..831ba54 100644 --- a/src/ADOGenerator/IServices/ITemplateService.cs +++ b/src/ADOGenerator/IServices/ITemplateService.cs @@ -5,6 +5,6 @@ namespace ADOGenerator.IServices public interface ITemplateService { bool AnalyzeProject(Project model); - bool StartEnvironmentSetupProcess(Project model); + (bool,string) GenerateTemplateArtifacts(Project model); } } diff --git a/src/ADOGenerator/Program.cs b/src/ADOGenerator/Program.cs index 872a001..f62203b 100644 --- a/src/ADOGenerator/Program.cs +++ b/src/ADOGenerator/Program.cs @@ -4,6 +4,7 @@ using ADOGenerator.Services; using Microsoft.Extensions.Configuration; using Microsoft.Identity.Client; +using Newtonsoft.Json; using Newtonsoft.Json.Linq; using RestAPI; using System.Text.RegularExpressions; @@ -14,281 +15,392 @@ .Build(); Console.WriteLine("Welcome to Azure DevOps Demo Generator! This tool will help you generate a demo environment for Azure DevOps."); - +(string accessToken, string organizationName, string authScheme)? authenticationDetails = null; +string authChoice = string.Empty; do { + string id = Guid.NewGuid().ToString().Split('-')[0]; 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 a new Artifacts using exsisting project."); + Console.WriteLine("2. Generate a new Artifacts using existing project."); + id.AddMessage("Enter the option number from the list of options above:"); var userChoiceTemplate = Console.ReadLine(); if (userChoiceTemplate == "1") { - string id = Guid.NewGuid().ToString().Split('-')[0]; - Init init = new Init(); - id.AddMessage("Template Details"); - var templatePath = Path.Combine(Directory.GetCurrentDirectory(), "Templates", "TemplateSetting.json"); - - if (!File.Exists(templatePath)) + HandleNewProjectCreation(configuration,id); + } + else if (userChoiceTemplate == "2") + { + (bool isArtifactsGenerated,string template) = HandleArtifactGeneration(configuration,id); + if (isArtifactsGenerated) { - id.ErrorId().AddMessage("TemplateSettings.json file not found."); - break; + id.AddMessage("Do you want to update the template settings? (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("Updating template settings..."); + var templatePath = Path.Combine(Directory.GetCurrentDirectory(), "Templates", "TemplateSetting.json"); + if (!File.Exists(templatePath)) + { + id.ErrorId().AddMessage("TemplateSettings.json file not found."); + } + else + { + var templateSettings = File.ReadAllText(templatePath); + var json = JObject.Parse(templateSettings); + 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) + { + groups.Add(item); + } + json["Groups"] = groups; + } + else + { + id.AddMessage("Custom Templates group already exists."); + } + 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) + { + var existingTemplates = existingGroup["Template"] as JArray ?? new JArray(existingGroup["Template"]); + var newTemplates = group["Template"] as JArray ?? new JArray(group["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."); + } + if (newTemplates != null) + { + foreach (var templateCustom in newTemplates) + { + existingTemplates.Add(templateCustom); + } + } + } + else + { + groupwiseTemplates.Add(group); + } + } + } + File.WriteAllText(templatePath, JsonConvert.SerializeObject(json,Formatting.Indented)); + } + } + else + { + Console.WriteLine("Copy the generated template json and update the template settings manually."); + id.AddMessage("Template settings update skipped."); + } + } - - var templateSettings = File.ReadAllText(templatePath); - var json = JObject.Parse(templateSettings); - var groupwiseTemplates = json["GroupwiseTemplates"]; - - if (groupwiseTemplates == null) + else { - id.ErrorId().AddMessage("No templates found."); - break; + id.ErrorId().AddMessage("Artifacts generation failed."); } + } + else + { + Console.WriteLine("Invalid choice. Please select either 1 or 2."); + continue; + } + if (authenticationDetails.HasValue) + { + authenticationDetails = (authenticationDetails.Value.accessToken, null, authenticationDetails.Value.authScheme); + } + 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)) + { + id.AddMessage("Exiting the application."); + Environment.Exit(0); + } +} while (true); +return 0; - int templateIndex = 1; - var templateDictionary = new Dictionary(); +void HandleNewProjectCreation(IConfiguration configuration, string id) +{ + Init init = new Init(); + id.AddMessage("Template Details"); - foreach (var group in groupwiseTemplates) - { - var groupName = group["Groups"]?.ToString(); - Console.WriteLine(groupName); + var templatePath = Path.Combine(Directory.GetCurrentDirectory(), "Templates", "TemplateSetting.json"); + if (!File.Exists(templatePath)) + { + id.ErrorId().AddMessage("TemplateSettings.json file not found."); + return; + } - var templates = group["Template"]; - if (templates != null) - { - foreach (var template in templates) - { - var templateName = template["Name"]?.ToString(); - Console.WriteLine($" {templateIndex}. {templateName}"); - templateDictionary.Add(templateIndex, templateName); - templateIndex++; - } - } - } + var groupwiseTemplates = LoadTemplates(templatePath, id); + if (groupwiseTemplates == null) return; - // option 1 : Authenticate using Device Login using AD auth - // option 2: using PAT - // let user decide which approach they want to select - // if option 1, invoke the methods from AuthService - // if option 2, continue with below code + var selectedTemplateName = SelectTemplate(groupwiseTemplates, id); + if (string.IsNullOrEmpty(selectedTemplateName)) return; - 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)) - { - id.AddMessage("Invalid template number entered."); - continue; - } - selectedTemplateName = selectedTemplateName.Trim(); - if (!TryGetTemplateDetails(groupwiseTemplates, selectedTemplateName, out var templateFolder, out var confirmedExtension, id)) + 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) HandleArtifactGeneration(IConfiguration configuration, string id) +{ + Init init = new Init(); + id.AddMessage("Template Details"); + + var (accessToken, organizationName, authScheme) = AuthenticateUser(init, id); + if (string.IsNullOrWhiteSpace(accessToken) || string.IsNullOrWhiteSpace(organizationName)) return (false, string.Empty); + + 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); + + var model = new Project + { + accountName = organizationName, + ProjectName = projectName, + ProjectId = projectDetails[0], + accessToken = accessToken, + adoAuthScheme = authScheme, + id = id + }; + + ITemplateService templateService = new TemplateService(configuration); + var analyzed = templateService.AnalyzeProject(model); + if (analyzed) + { + model.id.AddMessage("Artifacts analyzed successfully."); + } + else + { + model.id.ErrorId().AddMessage("Artifacts analysis failed."); + } + Console.WriteLine("Do you want to create artifacts yes/no:"); + string response = Console.ReadLine(); + if (response == "yes") + { + (bool isArtifactsGenerated, string template) = templateService.GenerateTemplateArtifacts(model); + if(isArtifactsGenerated) { - 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) - { - id.AddMessage("Exiting the application."); - return 0; - } - continue; + model.id.AddMessage("Artifacts generated successfully."); + return (true, template); } else { - id.AddMessage($"Selected template: {selectedTemplateName}"); - ValidateExtensions(templateFolder, id); + model.id.ErrorId().AddMessage("Artifacts generation failed."); } + } + else + { + model.id.AddMessage("Artifacts generation skipped."); + } + return (false, string.Empty); +} + +JToken LoadTemplates(string templatePath, string id) +{ + var templateSettings = File.ReadAllText(templatePath); + var json = JObject.Parse(templateSettings); + var groupwiseTemplates = json["GroupwiseTemplates"]; + + if (groupwiseTemplates == null) + { + id.ErrorId().AddMessage("No templates found."); + return null; + } + + return groupwiseTemplates; +} - id.AddMessage("Choose authentication method: 1. Device Login using AD auth 2. Personal Access Token (PAT)"); - var authChoice = Console.ReadLine(); +string SelectTemplate(JToken groupwiseTemplates, string id) +{ + int templateIndex = 1; + var templateDictionary = new Dictionary(); - string accessToken = string.Empty; - string organizationName = string.Empty; - string authScheme = string.Empty; + foreach (var group in groupwiseTemplates) + { + var groupName = group["Groups"]?.ToString(); + Console.WriteLine(groupName); - if (authChoice == "1") + var templates = group["Template"]; + if (templates != null) { - var app = PublicClientApplicationBuilder.Create(AuthService.clientId) - .WithAuthority(AuthService.authority) - .WithDefaultRedirectUri() - .Build(); - AuthService authService = new AuthService(); - - var accounts = await app.GetAccountsAsync(); - if (accounts.Any()) + foreach (var template in templates) { - try - { - var result = await app.AcquireTokenSilent(AuthService.scopes, accounts.FirstOrDefault()) - .WithForceRefresh(true) - .ExecuteAsync(); - accessToken = result.AccessToken; - } - catch (MsalUiRequiredException) - { - var result = await authService.AcquireTokenAsync(app); - accessToken = result.AccessToken; - } + var templateName = template["Name"]?.ToString(); + Console.WriteLine($" {templateIndex}. {templateName}"); + templateDictionary.Add(templateIndex, templateName); + templateIndex++; } - 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"; - } - else if (authChoice == "2") - { - id.AddMessage("Enter your Azure DevOps organization name:"); - organizationName = Console.ReadLine(); + } - id.AddMessage("Enter your Azure DevOps personal access token:"); - accessToken = init.ReadSecret(); + 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)) + { + id.AddMessage("Invalid template number entered."); + return null; + } - authScheme = "Basic"; - } - string projectName = ""; - do + return selectedTemplateName.Trim(); +} +(string accessToken, string organizationName, string authScheme) AuthenticateUser(Init init, string id) +{ + string organizationName = string.Empty; + if (authenticationDetails.HasValue) + { + if (string.IsNullOrWhiteSpace(authenticationDetails.Value.organizationName)) { - id.AddMessage("Enter the new project name:"); - projectName = Console.ReadLine(); - if (!init.CheckProjectName(projectName)) + if (authChoice == "1") { - 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; + 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; } - } while (string.IsNullOrWhiteSpace(projectName)); - - if (string.IsNullOrWhiteSpace(organizationName) || string.IsNullOrWhiteSpace(accessToken) || string.IsNullOrWhiteSpace(projectName)) - { - id.ErrorId().AddMessage("Validation error: All inputs must be provided. Exiting.."); - Environment.Exit(1); + else if(authChoice == "2") + { + id.AddMessage("Enter your Azure DevOps organization name:"); + organizationName = Console.ReadLine(); + } + authenticationDetails = (authenticationDetails.Value.accessToken, organizationName, authenticationDetails.Value.authScheme); } - - 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); + return authenticationDetails.Value; } - else if (userChoiceTemplate == "2") - { - string id = Guid.NewGuid().ToString().Split('-')[0]; - Init init = new Init(); - id.AddMessage("Template Details"); - id.AddMessage("Choose authentication method: 1. Device Login using AD auth 2. Personal Access Token (PAT)"); - var authChoice = Console.ReadLine(); + id.AddMessage("Choose authentication method: 1. Device Login using AD auth 2. Personal Access Token (PAT)"); + authChoice = Console.ReadLine(); - string accessToken = string.Empty; - string organizationName = string.Empty; - string authScheme = string.Empty; + string accessToken = string.Empty; + string authScheme = string.Empty; - if (authChoice == "1") + 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()) { - var app = PublicClientApplicationBuilder.Create(AuthService.clientId) - .WithAuthority(AuthService.authority) - .WithDefaultRedirectUri() - .Build(); - AuthService authService = new AuthService(); - - var accounts = await app.GetAccountsAsync(); - if (accounts.Any()) + try { - try - { - var result = await app.AcquireTokenSilent(AuthService.scopes, accounts.FirstOrDefault()) - .WithForceRefresh(true) - .ExecuteAsync(); - accessToken = result.AccessToken; - } - catch (MsalUiRequiredException) - { - var result = await authService.AcquireTokenAsync(app); - accessToken = result.AccessToken; - } + var result = app.AcquireTokenSilent(AuthService.scopes, accounts.FirstOrDefault()) + .WithForceRefresh(true) + .ExecuteAsync().Result; + accessToken = result.AccessToken; } - else + catch (MsalUiRequiredException) { - var result = await authService.AcquireTokenAsync(app); + var result = authService.AcquireTokenAsync(app).Result; accessToken = result.AccessToken; } - - var memberId = await authService.GetProfileInfoAsync(accessToken); - var organizations = await authService.GetOrganizationsAsync(accessToken, memberId); - organizationName = await authService.SelectOrganization(accessToken, organizations); - authScheme = "Bearer"; } - else if (authChoice == "2") + else { - id.AddMessage("Enter your Azure DevOps organization name:"); - organizationName = Console.ReadLine(); - - id.AddMessage("Enter your Azure DevOps personal access token:"); - accessToken = init.ReadSecret(); - - authScheme = "Basic"; + var result = authService.AcquireTokenAsync(app).Result; + accessToken = result.AccessToken; } - string projectName = ""; - IProjectService projService = new ProjectService(configuration); - var projects = projService.GetProjects(organizationName, accessToken, authScheme); - var projectDetails = await projService.SelectProject(accessToken, projects); - projectName = projectDetails[1]; + 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("Enter your Azure DevOps organization name:"); + organizationName = Console.ReadLine(); - if (string.IsNullOrWhiteSpace(organizationName) || string.IsNullOrWhiteSpace(accessToken) || string.IsNullOrWhiteSpace(projectName)) - { - id.ErrorId().AddMessage("Validation error: All inputs must be provided. Exiting.."); - Environment.Exit(1); - } + id.AddMessage("Enter your Azure DevOps personal access token:"); + accessToken = init.ReadSecret(); - //var trackingId = Guid.NewGuid().ToString().Split('-')[0]; - Project model = new Project(); - model.accountName = organizationName; - model.ProjectName = projectName; - model.ProjectId = projectDetails[0]; - model.accessToken = accessToken; - model.adoAuthScheme = authScheme; - model.id = id; - ITemplateService templateService = new TemplateService(configuration); - var analyzed = templateService.AnalyzeProject(model); - if (analyzed) - { - id.AddMessage("Artifacts generated successfully."); - //templateService.StartEnvironmentSetupProcess(model); + authScheme = "Basic"; + } - } - else + authenticationDetails = (accessToken, organizationName, authScheme); + return authenticationDetails.Value; +} + +string GetValidProjectName(Init init, string id) +{ + string projectName = ""; + do + { + id.AddMessage("Enter the new project name:"); + projectName = Console.ReadLine(); + if (!init.CheckProjectName(projectName)) { - id.ErrorId().AddMessage("Artifacts generation failed."); + 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 = ""; } - } - else - { - Console.WriteLine("Invalid choice. Please select either 1 or 2."); - continue; - } -} while (true); + } while (string.IsNullOrWhiteSpace(projectName)); + + return projectName; +} bool TryGetTemplateDetails(JToken groupwiseTemplates, string selectedTemplateName, out string templateFolder, out bool confirmedExtension, string id) { @@ -394,21 +506,4 @@ void CreateProjectEnvironment(Project model) { 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)) - { - model.id.AddMessage("Exiting the application."); - Environment.Exit(0); - } -} -return 0; \ No newline at end of file +} \ No newline at end of file diff --git a/src/ADOGenerator/Services/ExtractorService.cs b/src/ADOGenerator/Services/ExtractorService.cs index 042035a..f3858dd 100644 --- a/src/ADOGenerator/Services/ExtractorService.cs +++ b/src/ADOGenerator/Services/ExtractorService.cs @@ -23,6 +23,7 @@ using Configuration = RestAPI.ADOConfiguration; using RestAPI.QueriesAndWidgets; using Microsoft.Extensions.Configuration; +using static ADOGenerator.Models.TemplateSelection; //using ADOProjectConfigurations = ADOGenerator.Models.ADOProjectConfigurations; namespace ADOGenerator.Services @@ -37,7 +38,7 @@ public class ExtractorService : IExtractorService 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 static string currentPath = Directory.GetCurrentDirectory().Replace("bin\\Debug\\net8.0", "").Replace("binRelease\\net8.0", "").Replace("bin\\Debug", "").Replace("bin\\Release", ""); public ExtractorService(IConfiguration config) { @@ -235,7 +236,7 @@ public string[] GenerateTemplateArifacts(Project model) { AddMessage(model.id, "Iterations Definition"); } - string extractedFolderName = extractedTemplatePath + model.ProjectName; + string extractedFolderName = extractedTemplatePath + $"CT-{model.ProjectName.Replace(" ", "-")}"; string filePathToRead = currentPath + @"\\PreSetting"; string projectSetting = ""; @@ -278,7 +279,40 @@ public string[] GenerateTemplateArifacts(Project model) } StatusMessages[model.id] = "100"; - return new string[] { model.id, "" }; + // Generate custom template JSON + var customTemplateJson = new + { + Groups = new[] + { + "Custom Templates" + }, + GroupwiseTemplates = new[] + { + new + { + Groups = "Custom Templates", + Template = new[] + { + new + { + Key = Guid.NewGuid().ToString(), + Name = model.ProjectName, + TemplateFolder = $"CT-{model.ProjectName.Replace(" ", "-")}", + ShortName = model.ProjectName.Replace(" ", ""), + Description = $"This is a custom template for the project '{model.ProjectName}'.", + //Tags = new[] { "custom", "template", "generated" }, + Author = "Generated by Azure DevOps Demo Generator", + //Image = "/Templates/TemplateImages/CustomTemplate.png", + //PreviewImages = new[] { "/Templates/CustomTemplate/Images/Preview1.png", "/Templates/CustomTemplate/Images/Preview2.png" }, + Message = "This template was generated based on the artifacts of the selected project." + } + } + } + } + }; + Console.WriteLine("JSON block to be added to TemplateSettings:"); + Console.WriteLine(JsonConvert.SerializeObject(customTemplateJson, Formatting.Indented)); + return new string[] { model.id, JsonConvert.SerializeObject(customTemplateJson) }; } public Dictionary GetWorkItemsCount(ProjectConfigurations appConfig) @@ -338,13 +372,13 @@ public Dictionary GetWorkItemsCount(ProjectConfigurations appConfig if (extensionList.Count > 0) { listExtension.Extensions = extensionList; - if (!Directory.Exists(extractedTemplatePath + appConfig.ExtensionConfig.Project)) + if (!Directory.Exists(extractedTemplatePath + $"CT-{appConfig.ExtensionConfig.Project.Replace(" ", "-")}")) { - Directory.CreateDirectory(extractedTemplatePath + appConfig.ExtensionConfig.Project); + Directory.CreateDirectory(extractedTemplatePath + $"CT-{appConfig.ExtensionConfig.Project.Replace(" ", "-")}"); } string fetchedJson = JsonConvert.SerializeObject(listExtension, Formatting.Indented); - File.WriteAllText(extractedTemplatePath + appConfig.ExtensionConfig.Project + "\\Extensions.json", JsonConvert.SerializeObject(listExtension, Formatting.Indented)); + File.WriteAllText(extractedTemplatePath + $"CT-{appConfig.ExtensionConfig.Project.Replace(" ", "-")}" + "\\Extensions.json", JsonConvert.SerializeObject(listExtension, Formatting.Indented)); } } else if (!string.IsNullOrEmpty(listExtenison.LastFailureMessage)) diff --git a/src/ADOGenerator/Services/TemplateService.cs b/src/ADOGenerator/Services/TemplateService.cs index f7f394a..037e1f2 100644 --- a/src/ADOGenerator/Services/TemplateService.cs +++ b/src/ADOGenerator/Services/TemplateService.cs @@ -1,34 +1,42 @@ using ADOGenerator.IServices; using ADOGenerator.Models; +using ADOGenerator.Services; using Microsoft.Extensions.Configuration; -using RestAPI; using RestAPI.Extractor; using RestAPI.ProjectsAndTeams; -using System.Configuration; +using RestAPI; -namespace ADOGenerator.Services +public class TemplateService : ITemplateService { - public class TemplateService : ITemplateService + private readonly IConfiguration _config; + private readonly IExtractorService extractorService; + + public TemplateService(IConfiguration config) { - private readonly IConfiguration _config; - IExtractorService extractorService; - public TemplateService(IConfiguration config) { - _config = config; - extractorService = new ExtractorService(_config); - } + _config = config; + extractorService = new ExtractorService(_config); + } - public bool AnalyzeProject(Project model) + public bool AnalyzeProject(Project model) + { + try { string defaultHost = _config["AppSettings:DefaultHost"]; - string ProjectPropertyVersion = _config["AppSettings:ProjectPropertyVersion"]; - //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}; + 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 + }; - ProjectProperties.Properties load = new ProjectProperties.Properties(); Projects projects = new Projects(config); - load = projects.GetProjectProperties(); + ProjectProperties.Properties load = projects.GetProjectProperties(); model.ProcessTemplate = load.value[4].value; - //model.ProjectId = load.value[0].value; ExtractorService es = new ExtractorService(_config); ExtractorAnalysis analysis = new ExtractorAnalysis(); ProjectConfigurations appConfig = extractorService.ProjectConfiguration(model); @@ -39,40 +47,55 @@ public bool AnalyzeProject(Project model) analysis.ReleaseDefCount = extractorService.GetReleaseDefinitionCount(appConfig); analysis.ErrorMessages = es.errorMessages; - Console.WriteLine("Analysis of the project"); - Console.WriteLine("Project Name: " + model.ProjectName); - Console.WriteLine("Processtemplate Type: " + model.ProcessTemplate); - Console.WriteLine("Teams Count: " + analysis.teamCount); - Console.WriteLine("Iterations Count: " + analysis.IterationCount); - Console.WriteLine("Work Items Count: "); - foreach (var item in analysis.WorkItemCounts) - { - Console.WriteLine(item.Key + " : " + item.Value); - } - Console.WriteLine("Build Definitions Count: " + analysis.BuildDefCount); - Console.WriteLine("Release Definitions Count: " + analysis.ReleaseDefCount); - Console.WriteLine("Errors: "); - foreach (var item in analysis.ErrorMessages) - { - Console.WriteLine(item); - } + LogAnalysisResults(model, analysis); - Console.WriteLine("Do you want to create artifacts yes/no:"); - string response = Console.ReadLine(); - if (response == "yes") - { - return StartEnvironmentSetupProcess(model); - } - else + return true; + } + catch (Exception ex) + { + Console.WriteLine("Error during project analysis: " + ex.Message); + return false; + } + } + + public (bool,string) GenerateTemplateArtifacts(Project model) + { + try + { + string[] createdTemplate = extractorService.GenerateTemplateArifacts(model); + if (createdTemplate == null || createdTemplate.Length == 0) { - return false; + Console.WriteLine("No artifacts were generated."); + return (false, string.Empty); // No artifacts generated } + string template = createdTemplate[1]; + return (true,template); // Artifact generation completed successfully } + catch (Exception ex) + { + Console.WriteLine("Error during artifact generation: " + ex.Message); + return (false,string.Empty); // Artifact generation failed + } + } - public bool StartEnvironmentSetupProcess(Project model) + private void LogAnalysisResults(Project model, ExtractorAnalysis analysis) + { + Console.WriteLine("Analysis of the project"); + Console.WriteLine("Project Name: " + model.ProjectName); + Console.WriteLine("Process Template Type: " + model.ProcessTemplate); + Console.WriteLine("Teams Count: " + analysis.teamCount); + Console.WriteLine("Iterations Count: " + analysis.IterationCount); + Console.WriteLine("Work Items Count: "); + foreach (var item in analysis.WorkItemCounts) { - extractorService.GenerateTemplateArifacts(model); - return true; - } + Console.WriteLine(item.Key + " : " + item.Value); + } + Console.WriteLine("Build Definitions Count: " + analysis.BuildDefCount); + Console.WriteLine("Release Definitions Count: " + analysis.ReleaseDefCount); + Console.WriteLine("Errors: "); + foreach (var item in analysis.ErrorMessages) + { + Console.WriteLine(item); + } } } From 6aa541b9936fec84702bdf8362a25b968c5dabc5 Mon Sep 17 00:00:00 2001 From: Akshay H Date: Thu, 17 Apr 2025 19:01:16 +0530 Subject: [PATCH 3/4] Refactor file management and user interaction - Updated `ADOGenerator.csproj` to ensure `appsettings.json` is always copied to the output directory. - Modified `IExtractorService` to remove several methods and add `extractedFolderName` parameter for improved file handling. - Enhanced `GenerateTemplateArtifacts` in `ITemplateService` to return the location of the generated template. - Restructured `Program.cs` for better user interaction and added color-coded console messages. - Introduced new helper methods for file management and template updates. - Updated `ExtractorService` and export methods to utilize the new folder structure. - Improved error handling and user feedback in `TemplateService`. - Overall, these changes streamline the generation and export of Azure DevOps templates and artifacts. --- src/ADOGenerator/ADOGenerator.csproj | 3 - .../IServices/IExtractorService.cs | 21 +- .../IServices/ITemplateService.cs | 2 +- src/ADOGenerator/Program.cs | 398 ++++++++++++------ src/ADOGenerator/ServiceExtensions.cs | 4 + src/ADOGenerator/Services/AuthService.cs | 10 +- src/ADOGenerator/Services/ExtractorService.cs | 215 +++++----- src/ADOGenerator/Services/ProjectService.cs | 14 +- src/ADOGenerator/Services/TemplateService.cs | 48 ++- src/API/Extractor/GetWorkItemsCount.cs | 2 +- 10 files changed, 428 insertions(+), 289 deletions(-) diff --git a/src/ADOGenerator/ADOGenerator.csproj b/src/ADOGenerator/ADOGenerator.csproj index ff06325..e1fc806 100644 --- a/src/ADOGenerator/ADOGenerator.csproj +++ b/src/ADOGenerator/ADOGenerator.csproj @@ -86,9 +86,6 @@ Always - - Always - diff --git a/src/ADOGenerator/IServices/IExtractorService.cs b/src/ADOGenerator/IServices/IExtractorService.cs index 1546ba1..3ec0127 100644 --- a/src/ADOGenerator/IServices/IExtractorService.cs +++ b/src/ADOGenerator/IServices/IExtractorService.cs @@ -17,16 +17,15 @@ public interface IExtractorService int GetReleaseDefinitionCount(ProjectConfigurations appConfig); string[] GenerateTemplateArifacts(Project model); Dictionary GetWorkItemsCount(ProjectConfigurations appConfig); - List GetInstalledExtensions(ProjectConfigurations appConfig); - void ExportQuries(ProjectConfigurations appConfig); - bool ExportTeams(RestAPI.ADOConfiguration con, Project model); - bool ExportIterations(ProjectConfigurations appConfig); - void ExportWorkItems(ProjectConfigurations appConfig); - void ExportRepositoryList(ProjectConfigurations appConfig); - int GetBuildDefinitions(ProjectConfigurations appConfig); - int GeneralizingGetReleaseDefinitions(ProjectConfigurations appConfig); - void GetServiceEndpoints(ProjectConfigurations appConfig); - void ExportDeliveryPlans(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); + void ExportWorkItems(ProjectConfigurations appConfig, string extractedFolderName); + void ExportRepositoryList(ProjectConfigurations appConfig, string extractedFolderName); + int GetBuildDefinitions(ProjectConfigurations appConfig, string extractedFolderName); + int GeneralizingGetReleaseDefinitions(ProjectConfigurations appConfig, string extractedFolderName); + void GetServiceEndpoints(ProjectConfigurations appConfig, string extractedFolderName); + void ExportDeliveryPlans(ProjectConfigurations appConfig, string extractedFolderName); } } diff --git a/src/ADOGenerator/IServices/ITemplateService.cs b/src/ADOGenerator/IServices/ITemplateService.cs index 831ba54..ef98d68 100644 --- a/src/ADOGenerator/IServices/ITemplateService.cs +++ b/src/ADOGenerator/IServices/ITemplateService.cs @@ -5,6 +5,6 @@ namespace ADOGenerator.IServices public interface ITemplateService { bool AnalyzeProject(Project model); - (bool,string) GenerateTemplateArtifacts(Project model); + (bool,string,string) GenerateTemplateArtifacts(Project model); } } diff --git a/src/ADOGenerator/Program.cs b/src/ADOGenerator/Program.cs index f62203b..6697384 100644 --- a/src/ADOGenerator/Program.cs +++ b/src/ADOGenerator/Program.cs @@ -4,6 +4,7 @@ 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; @@ -17,138 +18,183 @@ Console.WriteLine("Welcome to Azure DevOps Demo Generator! This tool will help you generate a demo environment for Azure DevOps."); (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 { 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 a new Artifacts using existing project."); - id.AddMessage("Enter the option number from the list of options above:"); + 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(); - if (userChoiceTemplate == "1") - { - HandleNewProjectCreation(configuration,id); - } - else if (userChoiceTemplate == "2") + switch (userChoiceTemplate) { - (bool isArtifactsGenerated,string template) = HandleArtifactGeneration(configuration,id); - if (isArtifactsGenerated) - { - id.AddMessage("Do you want to update the template settings? (yes/no): press enter to confirm"); - var updateTemplateSettings = Console.ReadLine(); - if (string.IsNullOrWhiteSpace(updateTemplateSettings) || updateTemplateSettings.Equals("yes", StringComparison.OrdinalIgnoreCase) || updateTemplateSettings.Equals("y", StringComparison.OrdinalIgnoreCase)) + case "1": + HandleNewProjectCreation(configuration, id); + break; + + case "2": + var (isArtifactsGenerated, template, model) = HandleArtifactGeneration(configuration, id); + if (isArtifactsGenerated) { - id.AddMessage("Updating template settings..."); - var templatePath = Path.Combine(Directory.GetCurrentDirectory(), "Templates", "TemplateSetting.json"); - if (!File.Exists(templatePath)) - { - id.ErrorId().AddMessage("TemplateSettings.json file not found."); - } - else - { - var templateSettings = File.ReadAllText(templatePath); - var json = JObject.Parse(templateSettings); - 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) - { - groups.Add(item); - } - json["Groups"] = groups; - } - else - { - id.AddMessage("Custom Templates group already exists."); - } - 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) - { - var existingTemplates = existingGroup["Template"] as JArray ?? new JArray(existingGroup["Template"]); - var newTemplates = group["Template"] as JArray ?? new JArray(group["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."); - } - if (newTemplates != null) - { - foreach (var templateCustom in newTemplates) - { - existingTemplates.Add(templateCustom); - } - } - } - else - { - groupwiseTemplates.Add(group); - } - } - } - File.WriteAllText(templatePath, JsonConvert.SerializeObject(json,Formatting.Indented)); - } + HandleTemplateAndArtifactsUpdate(template, id, model, currentPath); } else { - Console.WriteLine("Copy the generated template json and update the template settings manually."); - id.AddMessage("Template settings update skipped."); + id.ErrorId().AddMessage(Environment.NewLine + "Artifacts generation failed."); + } + break; + + default: + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine("Invalid choice. Please select either 1 or 2."); + Console.ResetColor(); + continue; + } + + Console.ForegroundColor = ConsoleColor.Green; + Console.WriteLine("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)) + { + 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)) + { + if (authenticationDetails != null) + { + authenticationDetails = (authenticationDetails.Value.accessToken, null, authenticationDetails.Value.authScheme); } - } else { - id.ErrorId().AddMessage("Artifacts generation failed."); + authenticationDetails = null; } + continue; } - else + + 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)) { - Console.WriteLine("Invalid choice. Please select either 1 or 2."); - continue; + 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("Template settings updated successfully at " + templatePathOriginal); + } + else + { + id.ErrorId().AddMessage("Template settings update failed at " + templatePathOriginal); + } + + CopyFileIfExists(id,templatePathOriginal, templatePathBin); + + id.AddMessage("Template settings copied to the current directory and updated successfully."); + id.AddMessage("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); } - if (authenticationDetails.HasValue) + else { - authenticationDetails = (authenticationDetails.Value.accessToken, null, authenticationDetails.Value.authScheme); + SkipTemplateAndArtifactsUpdate(id, currentPath, model, template); } - 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)) +} + +void CopyFileIfExists(string id,string sourcePath, string destinationPath) +{ + if (File.Exists(sourcePath)) { - createAnotherProject = "yes"; + File.Copy(sourcePath, destinationPath, true); } else { - createAnotherProject = "no"; + Console.ForegroundColor = ConsoleColor.Green; + id.AddMessage($"Source file '{sourcePath}' does not exist. Creating a new file at the destination."); + string fileContents = File.ReadAllText(sourcePath); + File.WriteAllText(destinationPath, fileContents); + id.AddMessage($"New file created at '{destinationPath}'."); + Console.ResetColor(); } - Console.WriteLine(); - if (createAnotherProject.Equals("no", StringComparison.OrdinalIgnoreCase)) +} + +void MoveArtifacts(string sourcePath, string destinationPath, string id) +{ + if (Directory.Exists(sourcePath)) { - id.AddMessage("Exiting the application."); - Environment.Exit(0); + if (Directory.Exists(destinationPath)) + { + Directory.Delete(destinationPath, true); + } + Directory.CreateDirectory(destinationPath); + + foreach (var directory in Directory.GetDirectories(sourcePath, "*", SearchOption.AllDirectories)) + { + 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("Artifacts moved to the current directory."); } -} while (true); -return 0; + else + { + id.ErrorId().AddMessage("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(" ", "-")}"); + Console.ForegroundColor = ConsoleColor.Cyan; + id.AddMessage(Environment.NewLine+template); + Console.ResetColor(); + Console.ForegroundColor = ConsoleColor.Green; + id.AddMessage("Copy the generated template JSON and update the template settings manually in the following file location: " + templatePathBin); + id.AddMessage("Template settings update and copying artifacts skipped."); + id.AddMessage("Copy the generated artifacts directory from " + artifactsPathOriginal + " and update the artifacts manually in the following directory location: " + artifactsPath); + Console.ResetColor(); +} + void HandleNewProjectCreation(IConfiguration configuration, string id) { Init init = new Init(); - id.AddMessage("Template Details"); + id.AddMessage(Environment.NewLine+"Template Details"); var templatePath = Path.Combine(Directory.GetCurrentDirectory(), "Templates", "TemplateSetting.json"); if (!File.Exists(templatePath)) @@ -195,20 +241,18 @@ void HandleNewProjectCreation(IConfiguration configuration, string id) CreateProjectEnvironment(project); } -(bool,string) HandleArtifactGeneration(IConfiguration configuration, string id) +(bool,string,Project) HandleArtifactGeneration(IConfiguration configuration, string id) { Init init = new Init(); - id.AddMessage("Template Details"); - var (accessToken, organizationName, authScheme) = AuthenticateUser(init, id); - if (string.IsNullOrWhiteSpace(accessToken) || string.IsNullOrWhiteSpace(organizationName)) return (false, string.Empty); + 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); + if (string.IsNullOrWhiteSpace(projectName)) return (false, string.Empty, null); var model = new Project { @@ -230,26 +274,28 @@ void HandleNewProjectCreation(IConfiguration configuration, string id) { model.id.ErrorId().AddMessage("Artifacts analysis failed."); } - Console.WriteLine("Do you want to create artifacts yes/no:"); + 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) = templateService.GenerateTemplateArtifacts(model); + (bool isArtifactsGenerated, string template, string templateLocation) = templateService.GenerateTemplateArtifacts(model); if(isArtifactsGenerated) { - model.id.AddMessage("Artifacts generated successfully."); - return (true, template); + model.id.AddMessage(Environment.NewLine+"Artifacts has been generated sccessfully at the location: " + templateLocation); + return (true, template,model); } else { - model.id.ErrorId().AddMessage("Artifacts generation failed."); + model.id.ErrorId().AddMessage(Environment.NewLine + "Artifacts generation failed."); } } else { - model.id.AddMessage("Artifacts generation skipped."); + model.id.AddMessage(Environment.NewLine + "Artifacts generation skipped."); } - return (false, string.Empty); + return (false, string.Empty, null); } JToken LoadTemplates(string templatePath, string id) @@ -260,7 +306,7 @@ JToken LoadTemplates(string templatePath, string id) if (groupwiseTemplates == null) { - id.ErrorId().AddMessage("No templates found."); + id.ErrorId().AddMessage(Environment.NewLine + "No templates found."); return null; } @@ -275,11 +321,13 @@ string SelectTemplate(JToken groupwiseTemplates, string id) 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) { + Console.ForegroundColor = ConsoleColor.Yellow; foreach (var template in templates) { var templateName = template["Name"]?.ToString(); @@ -287,13 +335,14 @@ string SelectTemplate(JToken groupwiseTemplates, string id) templateDictionary.Add(templateIndex, templateName); templateIndex++; } + Console.ResetColor(); } } - id.AddMessage("Enter the template number from the list of templates above:"); + 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("Invalid template number entered."); + id.AddMessage(Environment.NewLine+"Invalid template number entered."); return null; } @@ -302,7 +351,10 @@ string SelectTemplate(JToken groupwiseTemplates, string id) (string accessToken, string organizationName, string authScheme) AuthenticateUser(Init init, string id) { string organizationName = string.Empty; - if (authenticationDetails.HasValue) + if (authenticationDetails.HasValue && + (!string.IsNullOrWhiteSpace(authenticationDetails.Value.organizationName) || + !string.IsNullOrWhiteSpace(authenticationDetails.Value.accessToken) || + !string.IsNullOrWhiteSpace(authenticationDetails.Value.authScheme))) { if (string.IsNullOrWhiteSpace(authenticationDetails.Value.organizationName)) { @@ -313,16 +365,16 @@ string SelectTemplate(JToken groupwiseTemplates, string id) var organizations = authService.GetOrganizationsAsync(authenticationDetails.Value.accessToken, memberId).Result; organizationName = authService.SelectOrganization(authenticationDetails.Value.accessToken, organizations).Result; } - else if(authChoice == "2") + else if (authChoice == "2") { - id.AddMessage("Enter your Azure DevOps organization name:"); + id.AddMessage(Environment.NewLine + "Enter your Azure DevOps organization name:"); organizationName = Console.ReadLine(); } authenticationDetails = (authenticationDetails.Value.accessToken, organizationName, authenticationDetails.Value.authScheme); } return authenticationDetails.Value; } - id.AddMessage("Choose authentication method: 1. Device Login using AD auth 2. Personal Access Token (PAT)"); + id.AddMessage(Environment.NewLine + "Choose authentication method: 1. Device Login using AD auth 2. Personal Access Token (PAT)"); authChoice = Console.ReadLine(); string accessToken = string.Empty; @@ -365,10 +417,10 @@ string SelectTemplate(JToken groupwiseTemplates, string id) } else if (authChoice == "2") { - id.AddMessage("Enter your Azure DevOps organization name:"); + id.AddMessage(Environment.NewLine + "Enter your Azure DevOps organization name:"); organizationName = Console.ReadLine(); - id.AddMessage("Enter your Azure DevOps personal access token:"); + id.AddMessage(Environment.NewLine + "Enter your Azure DevOps personal access token:"); accessToken = init.ReadSecret(); authScheme = "Basic"; @@ -383,16 +435,16 @@ string GetValidProjectName(Init init, string id) string projectName = ""; do { - id.AddMessage("Enter the new project name:"); + id.AddMessage(Environment.NewLine + "Enter the new project name:"); projectName = Console.ReadLine(); if (!init.CheckProjectName(projectName)) { - 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):"); + 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("Exiting the application."); + id.AddMessage(Environment.NewLine + "Exiting the application."); Environment.Exit(1); } projectName = ""; @@ -470,23 +522,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."); } } @@ -500,10 +552,98 @@ void CreateProjectEnvironment(Project model) var result = projectService.CreateProjectEnvironment(model); if (result) { + 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) + { + groups.Add(item); + } + json["Groups"] = groups; + } + else + { + id.AddMessage("Custom Templates group already exists."); + } +} + +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 3400cc0..e7e72eb 100644 --- a/src/ADOGenerator/ServiceExtensions.cs +++ b/src/ADOGenerator/ServiceExtensions.cs @@ -40,6 +40,10 @@ public static void AddMessage(this string id, string message) string fileName = $"{DateTime.Now.ToString("yyyy-MM-dd")}-{id}.txt"; if (id.EndsWith("_Errors")) { + if(!Directory.Exists(Path.Combine(logFilePath, "Errors"))) + { + Directory.CreateDirectory(Path.Combine(logFilePath, "Errors")); + } // Create Log file if (!File.Exists(Path.Combine(logFilePath, "Errors", fileName))) { diff --git a/src/ADOGenerator/Services/AuthService.cs b/src/ADOGenerator/Services/AuthService.cs index bccf493..eeedeef 100644 --- a/src/ADOGenerator/Services/AuthService.cs +++ b/src/ADOGenerator/Services/AuthService.cs @@ -69,17 +69,23 @@ 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; for (int i = 0; i < accounts.Count(); i++) { Console.WriteLine($"{i + 1}. {accounts[i]["accountName"]} (ID: {accounts[i]["accountId"]})"); } + 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 index f3858dd..46c4248 100644 --- a/src/ADOGenerator/Services/ExtractorService.cs +++ b/src/ADOGenerator/Services/ExtractorService.cs @@ -38,7 +38,7 @@ public class ExtractorService : IExtractorService 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("binRelease\\net8.0", "").Replace("bin\\Debug", "").Replace("bin\\Release", ""); + 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) { @@ -209,34 +209,24 @@ public int GetReleaseDefinitionCount(ProjectConfigurations appConfig) #region GENERATE ARTIFACTS public string[] GenerateTemplateArifacts(Project model) { - extractedTemplatePath = currentPath + @"ExtractedTemplate\"; - - if (Directory.Exists(extractedTemplatePath)) + extractedTemplatePath = currentPath + @"Templates\"; + string extractedFolderName = extractedTemplatePath + $"CT-{model.ProjectName.Replace(" ", "-")}"; + if (Directory.Exists(extractedFolderName)) { - string[] subdirs = Directory.GetDirectories(extractedTemplatePath) - .Select(Path.GetFileName) - .ToArray(); - foreach (string folderName in subdirs) - { - DirectoryInfo d = new DirectoryInfo(extractedTemplatePath + folderName); - if (d.CreationTime < DateTime.Now.AddHours(-1)) - Directory.Delete(extractedTemplatePath + folderName, true); - } + Directory.Delete(extractedFolderName, true); } - AddMessage(model.id, ""); ProjectConfigurations appConfig = ProjectConfiguration(model); - GetInstalledExtensions(appConfig); + GetInstalledExtensions(appConfig, extractedFolderName); - ExportQuries(appConfig); - ExportTeams(appConfig.BoardConfig, model); + ExportQuries(appConfig, extractedFolderName); + ExportTeams(appConfig.BoardConfig, model, extractedFolderName); - if (ExportIterations(appConfig)) + if (ExportIterations(appConfig, extractedFolderName)) { AddMessage(model.id, "Iterations Definition"); } - string extractedFolderName = extractedTemplatePath + $"CT-{model.ProjectName.Replace(" ", "-")}"; string filePathToRead = currentPath + @"\\PreSetting"; string projectSetting = ""; @@ -256,23 +246,23 @@ public string[] GenerateTemplateArifacts(Project model) File.WriteAllText(extractedFolderName + "\\TeamArea.json", teamArea); AddMessage(model.id, "Team Areas"); - ExportWorkItems(appConfig); + ExportWorkItems(appConfig, extractedFolderName); AddMessage(model.id, "Work Items"); - ExportDeliveryPlans(appConfig); + ExportDeliveryPlans(appConfig, extractedFolderName); AddMessage(model.id, "Delivery Plans"); - ExportRepositoryList(appConfig); + ExportRepositoryList(appConfig, extractedFolderName); AddMessage(model.id, "Repository and Service Endpoint"); - GetServiceEndpoints(appConfig); - int count = GetBuildDefinitions(appConfig); + GetServiceEndpoints(appConfig, extractedFolderName); + int count = GetBuildDefinitions(appConfig, extractedFolderName); if (count >= 1) { AddMessage(model.id, "Build Definition"); } - int relCount = GeneralizingGetReleaseDefinitions(appConfig); + int relCount = GeneralizingGetReleaseDefinitions(appConfig, extractedFolderName); if (relCount >= 1) { AddMessage(model.id, "Release Definition"); @@ -295,24 +285,15 @@ public string[] GenerateTemplateArifacts(Project model) { new { - Key = Guid.NewGuid().ToString(), Name = model.ProjectName, TemplateFolder = $"CT-{model.ProjectName.Replace(" ", "-")}", - ShortName = model.ProjectName.Replace(" ", ""), Description = $"This is a custom template for the project '{model.ProjectName}'.", - //Tags = new[] { "custom", "template", "generated" }, - Author = "Generated by Azure DevOps Demo Generator", - //Image = "/Templates/TemplateImages/CustomTemplate.png", - //PreviewImages = new[] { "/Templates/CustomTemplate/Images/Preview1.png", "/Templates/CustomTemplate/Images/Preview2.png" }, - Message = "This template was generated based on the artifacts of the selected project." } } } } }; - Console.WriteLine("JSON block to be added to TemplateSettings:"); - Console.WriteLine(JsonConvert.SerializeObject(customTemplateJson, Formatting.Indented)); - return new string[] { model.id, JsonConvert.SerializeObject(customTemplateJson) }; + return new string[] { model.id, JsonConvert.SerializeObject(customTemplateJson, Formatting.Indented) , extractedFolderName }; } public Dictionary GetWorkItemsCount(ProjectConfigurations appConfig) @@ -340,7 +321,7 @@ public Dictionary GetWorkItemsCount(ProjectConfigurations appConfig return fetchedWorkItemsCount; } - public List GetInstalledExtensions(ProjectConfigurations appConfig) + public List GetInstalledExtensions(ProjectConfigurations appConfig,string extractedFolderName) { try { @@ -372,13 +353,13 @@ public Dictionary GetWorkItemsCount(ProjectConfigurations appConfig if (extensionList.Count > 0) { listExtension.Extensions = extensionList; - if (!Directory.Exists(extractedTemplatePath + $"CT-{appConfig.ExtensionConfig.Project.Replace(" ", "-")}")) + if (!Directory.Exists(extractedFolderName)) { - Directory.CreateDirectory(extractedTemplatePath + $"CT-{appConfig.ExtensionConfig.Project.Replace(" ", "-")}"); + Directory.CreateDirectory(extractedFolderName); } string fetchedJson = JsonConvert.SerializeObject(listExtension, Formatting.Indented); - File.WriteAllText(extractedTemplatePath + $"CT-{appConfig.ExtensionConfig.Project.Replace(" ", "-")}" + "\\Extensions.json", JsonConvert.SerializeObject(listExtension, Formatting.Indented)); + File.WriteAllText(extractedFolderName + "\\Extensions.json", JsonConvert.SerializeObject(listExtension, Formatting.Indented)); } } else if (!string.IsNullOrEmpty(listExtenison.LastFailureMessage)) @@ -394,7 +375,7 @@ public Dictionary GetWorkItemsCount(ProjectConfigurations appConfig return new List(); } - public void ExportQuries(ProjectConfigurations appConfig) + public void ExportQuries(ProjectConfigurations appConfig, string extractedFolderName) { try { @@ -416,19 +397,19 @@ public void ExportQuries(ProjectConfigurations appConfig) JObject jobj = new JObject(); jobj["name"] = query.name; jobj["wiql"] = query.wiql; - if (!Directory.Exists(extractedTemplatePath + appConfig.QueriesConfig.Project + "\\Dashboard\\Queries")) + if (!Directory.Exists(extractedFolderName + "\\Dashboard\\Queries")) { - Directory.CreateDirectory(extractedTemplatePath + appConfig.QueriesConfig.Project + "\\Dashboard"); - File.WriteAllText(extractedTemplatePath + appConfig.QueriesConfig.Project + "\\Dashboard\\Dashboard.json", JsonConvert.SerializeObject("text", Formatting.Indented)); + Directory.CreateDirectory(extractedFolderName + "\\Dashboard"); + File.WriteAllText(extractedFolderName + "\\Dashboard\\Dashboard.json", JsonConvert.SerializeObject("text", Formatting.Indented)); } - if (!Directory.Exists(extractedTemplatePath + appConfig.QueriesConfig.Project + "\\Dashboard\\Queries")) + if (!Directory.Exists(extractedFolderName + "\\Dashboard\\Queries")) { - Directory.CreateDirectory(extractedTemplatePath + appConfig.QueriesConfig.Project + "\\Dashboard\\Queries"); - File.WriteAllText(extractedTemplatePath + appConfig.QueriesConfig.Project + "\\Dashboard\\Queries\\" + query.name + ".json", JsonConvert.SerializeObject(jobj, Formatting.Indented)); + Directory.CreateDirectory(extractedFolderName + "\\Dashboard\\Queries"); + File.WriteAllText(extractedFolderName + "\\Dashboard\\Queries\\" + query.name + ".json", JsonConvert.SerializeObject(jobj, Formatting.Indented)); } else { - File.WriteAllText(extractedTemplatePath + appConfig.QueriesConfig.Project + "\\Dashboard\\Queries\\" + query.name + ".json", JsonConvert.SerializeObject(jobj, Formatting.Indented)); + File.WriteAllText(extractedFolderName + "\\Dashboard\\Queries\\" + query.name + ".json", JsonConvert.SerializeObject(jobj, Formatting.Indented)); } } } @@ -442,15 +423,15 @@ public void ExportQuries(ProjectConfigurations appConfig) JObject jobj = new JObject(); jobj["name"] = child1.name; jobj["wiql"] = child1.wiql; - if (!Directory.Exists(extractedTemplatePath + appConfig.QueriesConfig.Project + "\\Dashboard\\Queries")) + if (!Directory.Exists(extractedFolderName + "\\Dashboard\\Queries")) { - Directory.CreateDirectory(extractedTemplatePath + appConfig.QueriesConfig.Project + "\\Dashboard\\Queries"); + Directory.CreateDirectory(extractedFolderName + "\\Dashboard\\Queries"); - File.WriteAllText(extractedTemplatePath + appConfig.QueriesConfig.Project + "\\Dashboard\\Queries\\" + child1.name + ".json", JsonConvert.SerializeObject(jobj, Formatting.Indented)); + File.WriteAllText(extractedFolderName + "\\Dashboard\\Queries\\" + child1.name + ".json", JsonConvert.SerializeObject(jobj, Formatting.Indented)); } else { - File.WriteAllText(extractedTemplatePath + appConfig.QueriesConfig.Project + "\\Dashboard\\Queries\\" + child1.name + ".json", JsonConvert.SerializeObject(jobj, Formatting.Indented)); + File.WriteAllText(extractedFolderName + "\\Dashboard\\Queries\\" + child1.name + ".json", JsonConvert.SerializeObject(jobj, Formatting.Indented)); } } } @@ -471,7 +452,7 @@ public void ExportQuries(ProjectConfigurations appConfig) } - public bool ExportTeams(Configuration con, Project model) + public bool ExportTeams(Configuration con, Project model,string extractedFolderName) { try { @@ -498,11 +479,11 @@ public bool ExportTeams(Configuration con, Project model) string fetchedJson = JsonConvert.SerializeObject(_team.value, Formatting.Indented); if (fetchedJson != "") { - if (!Directory.Exists(extractedTemplatePath + con.Project + "\\Teams")) + if (!Directory.Exists(extractedFolderName + "\\Teams")) { - Directory.CreateDirectory(extractedTemplatePath + con.Project + "\\Teams"); + Directory.CreateDirectory(extractedFolderName + "\\Teams"); } - File.WriteAllText(extractedTemplatePath + con.Project + "\\Teams\\Teams.json", fetchedJson); + File.WriteAllText(extractedFolderName + "\\Teams\\Teams.json", fetchedJson); List boardTypes = new List(); boardTypes.Add("Epics"); @@ -532,7 +513,7 @@ public bool ExportTeams(Configuration con, Project model) List jObjCardFieldList = new List(); List jObjcardStyleList = new List(); - string teamFolderPath = extractedTemplatePath + con.Project + "\\Teams\\" + team.name; + string teamFolderPath = extractedFolderName + "\\Teams\\" + team.name; if (!Directory.Exists(teamFolderPath)) { Directory.CreateDirectory(teamFolderPath); @@ -711,7 +692,7 @@ public bool ExportTeams(Configuration con, Project model) return false; } - public bool ExportIterations(ProjectConfigurations appConfig) + public bool ExportIterations(ProjectConfigurations appConfig,string extractedFolderName) { try { @@ -720,11 +701,11 @@ public bool ExportIterations(ProjectConfigurations appConfig) string fetchedJson = JsonConvert.SerializeObject(viewModel, Formatting.Indented); if (fetchedJson != "") { - if (!Directory.Exists(extractedTemplatePath + appConfig.BoardConfig.Project)) + if (!Directory.Exists(extractedFolderName)) { - Directory.CreateDirectory(extractedTemplatePath + appConfig.BoardConfig.Project); + Directory.CreateDirectory(extractedFolderName); } - File.WriteAllText(extractedTemplatePath + appConfig.BoardConfig.Project + "\\Iterations.json", fetchedJson); + File.WriteAllText(extractedFolderName + "\\Iterations.json", fetchedJson); return true; } else @@ -741,12 +722,12 @@ public bool ExportIterations(ProjectConfigurations appConfig) return false; } - public void ExportWorkItems(ProjectConfigurations appConfig) + public void ExportWorkItems(ProjectConfigurations appConfig, string extractedFolderName) { string[] workItemtypes = GetAllWorkItemsName(appConfig);//{ "Epic", "Feature", "Product Backlog Item", "Task", "Test Case", "Bug", "User Story", "Test Suite", "Test Plan", "Issue" }; - if (!Directory.Exists(extractedTemplatePath + appConfig.WorkItemConfig.Project)) + if (!Directory.Exists(extractedFolderName)) { - Directory.CreateDirectory(extractedTemplatePath + appConfig.WorkItemConfig.Project); + Directory.CreateDirectory(extractedFolderName); } if (workItemtypes.Length > 0) @@ -760,11 +741,11 @@ public void ExportWorkItems(ProjectConfigurations appConfig) { workItemJson = workItemJson.Replace(appConfig.WorkItemConfig.Project + "\\", "$ProjectName$\\"); string item = WIT; - if (!Directory.Exists(extractedTemplatePath + appConfig.WorkItemConfig.Project + "\\WorkItems")) + if (!Directory.Exists(extractedFolderName + "\\WorkItems")) { - Directory.CreateDirectory(extractedTemplatePath + appConfig.WorkItemConfig.Project + "\\WorkItems"); + Directory.CreateDirectory(extractedFolderName + "\\WorkItems"); } - File.WriteAllText(extractedTemplatePath + appConfig.WorkItemConfig.Project + "\\WorkItems\\" + item + ".json", workItemJson); + File.WriteAllText(extractedFolderName + "\\WorkItems\\" + item + ".json", workItemJson); } else if (!string.IsNullOrEmpty(WorkitemsCount.LastFailureMessage)) { @@ -774,7 +755,7 @@ public void ExportWorkItems(ProjectConfigurations appConfig) } } - public void ExportRepositoryList(ProjectConfigurations appConfig) + public void ExportRepositoryList(ProjectConfigurations appConfig, string extractedFolderName) { BuildandReleaseDefs repolist = new BuildandReleaseDefs(appConfig.RepoConfig); RepositoryList.Repository repos = repolist.GetRepoList(); @@ -783,29 +764,28 @@ public void ExportRepositoryList(ProjectConfigurations appConfig) foreach (var repo in repos.value) { string preSettingPath = currentPath + @"\PreSetting"; - string templateFolderPath = extractedTemplatePath + appConfig.RepoConfig.Project; 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(templateFolderPath + "\\ImportSourceCode")) + if (!Directory.Exists(extractedFolderName + "\\ImportSourceCode")) { - Directory.CreateDirectory(templateFolderPath + "\\ImportSourceCode"); - File.WriteAllText(templateFolderPath + "\\ImportSourceCode\\" + repo.name + ".json", sourceCodeJson); + Directory.CreateDirectory(extractedFolderName + "\\ImportSourceCode"); + File.WriteAllText(extractedFolderName + "\\ImportSourceCode\\" + repo.name + ".json", sourceCodeJson); } else { - File.WriteAllText(templateFolderPath + "\\ImportSourceCode\\" + repo.name + ".json", sourceCodeJson); + File.WriteAllText(extractedFolderName + "\\ImportSourceCode\\" + repo.name + ".json", sourceCodeJson); } - if (!Directory.Exists(templateFolderPath + "\\ServiceEndpoints")) + if (!Directory.Exists(extractedFolderName + "\\ServiceEndpoints")) { - Directory.CreateDirectory(templateFolderPath + "\\ServiceEndpoints"); - File.WriteAllText(templateFolderPath + "\\ServiceEndpoints\\" + repo.name + "-code.json", endPointJson); + Directory.CreateDirectory(extractedFolderName + "\\ServiceEndpoints"); + File.WriteAllText(extractedFolderName + "\\ServiceEndpoints\\" + repo.name + "-code.json", endPointJson); } else { - File.WriteAllText(templateFolderPath + "\\ServiceEndpoints\\" + repo.name + "-code.json", endPointJson); + File.WriteAllText(extractedFolderName + "\\ServiceEndpoints\\" + repo.name + "-code.json", endPointJson); } } } @@ -816,20 +796,19 @@ public void ExportRepositoryList(ProjectConfigurations appConfig) /// /// /// - public int GetBuildDefinitions(ProjectConfigurations appConfig) + 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); + Dictionary variableGroupNameId = GetVariableGroups(appConfig, extractedFolderName); RepositoryList.Repository repo = repoDefs.GetRepoList(); if (builds.Count > 0) { int count = 1; //creating ImportCode Json file - string templatePath = extractedTemplatePath + appConfig.BuildDefinitionConfig.Project; foreach (JObject def in builds) { string repoID = ""; @@ -871,21 +850,21 @@ public int GetBuildDefinitions(ProjectConfigurations appConfig) #region YML PIPELINES OF TYPE AZURE REPOS if (yamalfilename != null && type.ToString().ToLower() == "tfsgit") { - count = YmlWithAzureRepos(appConfig, count, templatePath, def, fileName, type); + 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, templatePath, def, fileName, type); + count = YmlWithGitHub(appConfig, count, extractedFolderName, def, fileName, type); } #endregion #region OTHER else if (yamalfilename == null) { - count = NormalPipeline(appConfig, count, templatePath, def, fileName, repoName, type); + count = NormalPipeline(appConfig, count, extractedFolderName, def, fileName, repoName, type); } #endregion } @@ -1069,9 +1048,9 @@ private static int YmlWithGitHub(ProjectConfigurations appConfig, int count, str Guid g = Guid.NewGuid(); string randStr = g.ToString().Substring(0, 8); var ymlRepoUrl = def["repository"]["url"].ToString(); - if (!Directory.Exists(extractedTemplatePath + appConfig.BuildDefinitionConfig.Project + "\\ImportSourceCode")) + if (!Directory.Exists(templatePath + "\\ImportSourceCode")) { - Directory.CreateDirectory(extractedTemplatePath + appConfig.BuildDefinitionConfig.Project + "\\ImportSourceCode"); + Directory.CreateDirectory(templatePath + "\\ImportSourceCode"); } if (type.ToString().ToLower() == "github") { @@ -1082,9 +1061,9 @@ private static int YmlWithGitHub(ProjectConfigurations appConfig, int count, str ForkRepos.Fork gitHubRepoList = new ForkRepos.Fork(); gitHubRepoList.repositories = new List(); - if (File.Exists(extractedTemplatePath + appConfig.BuildDefinitionConfig.Project + "\\ImportSourceCode\\GitRepository.json")) + if (File.Exists(templatePath + "\\ImportSourceCode\\GitRepository.json")) { - string readrepo = File.ReadAllText(extractedTemplatePath + appConfig.BuildDefinitionConfig.Project + "\\ImportSourceCode\\GitRepository.json"); + string readrepo = File.ReadAllText(templatePath + "\\ImportSourceCode\\GitRepository.json"); gitHubRepoList = JsonConvert.DeserializeObject(readrepo); } ForkRepos.Repository repoName = new ForkRepos.Repository @@ -1094,7 +1073,7 @@ private static int YmlWithGitHub(ProjectConfigurations appConfig, int count, str }; gitHubRepoList.repositories.Add(repoName); - File.WriteAllText(extractedTemplatePath + appConfig.BuildDefinitionConfig.Project + "\\ImportSourceCode\\GitRepository.json", JsonConvert.SerializeObject(gitHubRepoList, Formatting.Indented)); + 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"; @@ -1111,14 +1090,14 @@ private static int YmlWithGitHub(ProjectConfigurations appConfig, int count, str string endPointString = File.ReadAllText(currentPath + @"PreSetting\\GitHubEndPoint.json"); endPointString = endPointString.Replace("$GitHubURL$", ymlRepoUrl).Replace("$Name$", "GitHub_" + randStr); - if (!Directory.Exists(extractedTemplatePath + appConfig.BuildDefinitionConfig.Project + "\\ServiceEndpoints")) + if (!Directory.Exists(templatePath + "\\ServiceEndpoints")) { - Directory.CreateDirectory(extractedTemplatePath + appConfig.BuildDefinitionConfig.Project + "\\ServiceEndpoints"); - File.WriteAllText(extractedTemplatePath + appConfig.BuildDefinitionConfig.Project + "\\ServiceEndpoints\\GitHub_" + randStr + "-EndPoint.json", endPointString); + Directory.CreateDirectory(templatePath + "\\ServiceEndpoints"); + File.WriteAllText(templatePath + "\\ServiceEndpoints\\GitHub_" + randStr + "-EndPoint.json", endPointString); } else { - File.WriteAllText(extractedTemplatePath + appConfig.BuildDefinitionConfig.Project + "\\ServiceEndpoints\\GitHub_" + randStr + "-EndPoint.json", endPointString); + File.WriteAllText(templatePath + "\\ServiceEndpoints\\GitHub_" + randStr + "-EndPoint.json", endPointString); } } count = count + 1; @@ -1168,14 +1147,14 @@ private static int YmlWithAzureRepos(ProjectConfigurations appConfig, int count, { string endPointString = File.ReadAllText(currentPath + @"PreSetting\\GitHubEndPoint.json"); endPointString = endPointString.Replace("$GitHubURL$", ymlRepoUrl).Replace("$Name$", "GitHub_" + randStr); - if (!Directory.Exists(extractedTemplatePath + appConfig.BuildDefinitionConfig.Project + "\\ServiceEndpoints")) + if (!Directory.Exists(templatePath + "\\ServiceEndpoints")) { - Directory.CreateDirectory(extractedTemplatePath + appConfig.BuildDefinitionConfig.Project + "\\ServiceEndpoints"); - File.WriteAllText(extractedTemplatePath + appConfig.BuildDefinitionConfig.Project + "\\ServiceEndpoints\\GitHub-" + randStr + "-EndPoint.json", endPointString); + Directory.CreateDirectory(templatePath + "\\ServiceEndpoints"); + File.WriteAllText(templatePath + "\\ServiceEndpoints\\GitHub-" + randStr + "-EndPoint.json", endPointString); } else { - File.WriteAllText(extractedTemplatePath + appConfig.BuildDefinitionConfig.Project + "\\ServiceEndpoints\\GitHub-" + randStr + "-EndPoint.json", endPointString); + File.WriteAllText(templatePath + "\\ServiceEndpoints\\GitHub-" + randStr + "-EndPoint.json", endPointString); } } string[] splitYmlRepoUrl = ymlRepoUrl.Split('/'); @@ -1227,7 +1206,7 @@ private static int YmlWithAzureRepos(ProjectConfigurations appConfig, int count, /// /// /// - public int GeneralizingGetReleaseDefinitions(ProjectConfigurations appConfig) + public int GeneralizingGetReleaseDefinitions(ProjectConfigurations appConfig, string extractedFolderName) { try { @@ -1235,9 +1214,8 @@ public int GeneralizingGetReleaseDefinitions(ProjectConfigurations appConfig) List releases = releaseDefs.GetReleaseDefs(); string rells = JsonConvert.SerializeObject(releases); BuildandReleaseDefs agent = new BuildandReleaseDefs(appConfig.AgentQueueConfig); - Dictionary variableGroupNameId = GetVariableGroups(appConfig); + Dictionary variableGroupNameId = GetVariableGroups(appConfig, extractedFolderName); Dictionary queue = agent.GetQueues(); - string templatePath = extractedTemplatePath + appConfig.ReleaseDefinitionConfig.Project; int releasecount = 1; if (releases.Count > 0) { @@ -1377,14 +1355,14 @@ public int GeneralizingGetReleaseDefinitions(ProjectConfigurations appConfig) } } } - if (!(Directory.Exists(templatePath + "\\ReleaseDefinitions"))) + if (!(Directory.Exists(extractedFolderName + "\\ReleaseDefinitions"))) { - Directory.CreateDirectory(templatePath + "\\ReleaseDefinitions"); - File.WriteAllText(templatePath + "\\ReleaseDefinitions\\" + name + ".json", JsonConvert.SerializeObject(rel, Formatting.Indented)); + Directory.CreateDirectory(extractedFolderName + "\\ReleaseDefinitions"); + File.WriteAllText(extractedFolderName + "\\ReleaseDefinitions\\" + name + ".json", JsonConvert.SerializeObject(rel, Formatting.Indented)); } else { - File.WriteAllText(templatePath + "\\ReleaseDefinitions\\" + name + ".json", JsonConvert.SerializeObject(rel, Formatting.Indented)); + File.WriteAllText(extractedFolderName + "\\ReleaseDefinitions\\" + name + ".json", JsonConvert.SerializeObject(rel, Formatting.Indented)); } releasecount++; } @@ -1402,7 +1380,7 @@ public int GeneralizingGetReleaseDefinitions(ProjectConfigurations appConfig) /// Get different kinds of service endpoints and format it into POST json format /// /// - public void GetServiceEndpoints(ProjectConfigurations appConfig) + public void GetServiceEndpoints(ProjectConfigurations appConfig, string extractedFolderName) { try { @@ -1554,14 +1532,14 @@ public void GetServiceEndpoints(ProjectConfigurations appConfig) } string endpointString = JsonConvert.SerializeObject(endpoint); - if (!Directory.Exists(extractedTemplatePath + appConfig.EndpointConfig.Project + "\\ServiceEndpoints")) + if (!Directory.Exists(extractedFolderName + "\\ServiceEndpoints")) { - Directory.CreateDirectory(extractedTemplatePath + appConfig.EndpointConfig.Project + "\\ServiceEndpoints"); - File.WriteAllText(extractedTemplatePath + appConfig.EndpointConfig.Project + "\\ServiceEndpoints\\", JsonConvert.SerializeObject(endpoint, Formatting.Indented, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore })); + Directory.CreateDirectory(extractedFolderName + "\\ServiceEndpoints"); + File.WriteAllText(extractedFolderName + "\\ServiceEndpoints\\", JsonConvert.SerializeObject(endpoint, Formatting.Indented, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore })); } else { - File.WriteAllText(extractedTemplatePath + appConfig.EndpointConfig.Project + "\\ServiceEndpoints\\" + endpoint.name + ".json", JsonConvert.SerializeObject(endpoint, Formatting.Indented, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore })); + File.WriteAllText(extractedFolderName + "\\ServiceEndpoints\\" + endpoint.name + ".json", JsonConvert.SerializeObject(endpoint, Formatting.Indented, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore })); } } } @@ -1595,22 +1573,21 @@ private string[] GetAllWorkItemsName(ProjectConfigurations appConfig) return workItemNames.ToArray(); } - private Dictionary GetVariableGroups(ProjectConfigurations appConfig) + private Dictionary GetVariableGroups(ProjectConfigurations appConfig, string extractedFolderName) { VariableGroups variableGroups = new VariableGroups(appConfig.VariableGroupConfig); GetVariableGroups.Groups groups = variableGroups.GetVariableGroups(); Dictionary varibaleGroupDictionary = new Dictionary(); - string templatePath = extractedTemplatePath + appConfig.ReleaseDefinitionConfig.Project; if (groups.count > 0) { - if (!(Directory.Exists(templatePath + "\\VariableGroups"))) + if (!(Directory.Exists(extractedFolderName + "\\VariableGroups"))) { - Directory.CreateDirectory(templatePath + "\\VariableGroups"); - File.WriteAllText(templatePath + "\\VariableGroups\\VariableGroup.json", JsonConvert.SerializeObject(groups, Formatting.Indented)); + Directory.CreateDirectory(extractedFolderName + "\\VariableGroups"); + File.WriteAllText(extractedFolderName + "\\VariableGroups\\VariableGroup.json", JsonConvert.SerializeObject(groups, Formatting.Indented)); } else { - File.WriteAllText(templatePath + "\\VariableGroups\\VariableGroup.json", JsonConvert.SerializeObject(groups, Formatting.Indented)); + File.WriteAllText(extractedFolderName + "\\VariableGroups\\VariableGroup.json", JsonConvert.SerializeObject(groups, Formatting.Indented)); } foreach (var vg in groups.value) { @@ -1623,7 +1600,7 @@ private Dictionary GetVariableGroups(ProjectConfigurations appCo return varibaleGroupDictionary; } - public void ExportDeliveryPlans(ProjectConfigurations appConfig) + public void ExportDeliveryPlans(ProjectConfigurations appConfig, string extractedFolderName) { try { @@ -1631,8 +1608,6 @@ public void ExportDeliveryPlans(ProjectConfigurations appConfig) GetPlans.Root plansList = plans.GetDeliveryPlans(appConfig.WorkItemConfig.AccountName, appConfig.WorkItemConfig.Project); if (plansList.count > 0) { - string templatePath = extractedTemplatePath + appConfig.WorkItemConfig.Project; - RestAPI.Extractor.ClassificationNodes nodes = new RestAPI.Extractor.ClassificationNodes(appConfig.BoardConfig); string defaultTeamID = string.Empty; @@ -1665,14 +1640,14 @@ public void ExportDeliveryPlans(ProjectConfigurations appConfig) } if (!string.IsNullOrEmpty(aplan.id)) { - if (!(Directory.Exists(templatePath + "\\DeliveryPlans"))) + if (!(Directory.Exists(extractedFolderName + "\\DeliveryPlans"))) { - Directory.CreateDirectory(templatePath + "\\DeliveryPlans"); - File.WriteAllText(templatePath + $"\\DeliveryPlans\\{aplan.name}.json", JsonConvert.SerializeObject(aplan, Formatting.Indented)); + Directory.CreateDirectory(extractedFolderName + "\\DeliveryPlans"); + File.WriteAllText(extractedFolderName + $"\\DeliveryPlans\\{aplan.name}.json", JsonConvert.SerializeObject(aplan, Formatting.Indented)); } else { - File.WriteAllText(templatePath + $"\\DeliveryPlans\\{aplan.name}.json", JsonConvert.SerializeObject(aplan, Formatting.Indented)); + File.WriteAllText(extractedFolderName + $"\\DeliveryPlans\\{aplan.name}.json", JsonConvert.SerializeObject(aplan, Formatting.Indented)); } } } diff --git a/src/ADOGenerator/Services/ProjectService.cs b/src/ADOGenerator/Services/ProjectService.cs index 7fd3e68..4e29ed3 100644 --- a/src/ADOGenerator/Services/ProjectService.cs +++ b/src/ADOGenerator/Services/ProjectService.cs @@ -102,17 +102,25 @@ public async Task> SelectProject(string accessToken, HttpResponseMe { if (projectsJson["count"].Value() > 0) { - Console.WriteLine("Select an Project:"); + Console.ForegroundColor = ConsoleColor.Green; + Console.WriteLine(Environment.NewLine + "Select an Project:"); + Console.ResetColor(); var projects = projectsJson["value"]; + Console.ForegroundColor = ConsoleColor.Yellow; for (int i = 0; i < projects.Count(); i++) { Console.WriteLine($"{i + 1}. {projects[i]["name"]} (ID: {projects[i]["id"]})"); } + 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.Write("Enter the number of the project: "); + 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()); @@ -121,7 +129,9 @@ public async Task> SelectProject(string accessToken, HttpResponseMe } else { + Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("No organizations found."); + Console.ResetColor(); } return null; }); diff --git a/src/ADOGenerator/Services/TemplateService.cs b/src/ADOGenerator/Services/TemplateService.cs index 037e1f2..fc4c960 100644 --- a/src/ADOGenerator/Services/TemplateService.cs +++ b/src/ADOGenerator/Services/TemplateService.cs @@ -5,6 +5,7 @@ using RestAPI.Extractor; using RestAPI.ProjectsAndTeams; using RestAPI; +using ADOGenerator; public class TemplateService : ITemplateService { @@ -53,49 +54,56 @@ public bool AnalyzeProject(Project model) } catch (Exception ex) { - Console.WriteLine("Error during project analysis: " + ex.Message); + model.id.ErrorId().AddMessage("Error during project analysis: " + ex.Message); return false; } } - public (bool,string) GenerateTemplateArtifacts(Project model) + public (bool,string,string) GenerateTemplateArtifacts(Project model) { try { string[] createdTemplate = extractorService.GenerateTemplateArifacts(model); if (createdTemplate == null || createdTemplate.Length == 0) { - Console.WriteLine("No artifacts were generated."); - return (false, string.Empty); // No artifacts generated + model.id.AddMessage("No artifacts were generated."); + return (false, string.Empty,string.Empty); // No artifacts generated } string template = createdTemplate[1]; - return (true,template); // Artifact generation completed successfully + string templateLocation = createdTemplate[2]; + return (true,template, templateLocation); // Artifact generation completed successfully } catch (Exception ex) { - Console.WriteLine("Error during artifact generation: " + ex.Message); - return (false,string.Empty); // Artifact generation failed + 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) { - Console.WriteLine("Analysis of the project"); - Console.WriteLine("Project Name: " + model.ProjectName); - Console.WriteLine("Process Template Type: " + model.ProcessTemplate); - Console.WriteLine("Teams Count: " + analysis.teamCount); - Console.WriteLine("Iterations Count: " + analysis.IterationCount); - Console.WriteLine("Work Items Count: "); - foreach (var item in analysis.WorkItemCounts) + model.id.AddMessage("Analysis of the project"); + model.id.AddMessage("Project Name: " + model.ProjectName); + model.id.AddMessage("Process Template Type: " + model.ProcessTemplate); + model.id.AddMessage("Teams Count: " + analysis.teamCount); + model.id.AddMessage("Iterations Count: " + analysis.IterationCount); + if(analysis.WorkItemCounts.Count > 0) { - Console.WriteLine(item.Key + " : " + item.Value); + model.id.AddMessage("Work Items Count: "); + foreach (var item in analysis.WorkItemCounts) + { + model.id.AddMessage(item.Key + " : " + item.Value); + } } - Console.WriteLine("Build Definitions Count: " + analysis.BuildDefCount); - Console.WriteLine("Release Definitions Count: " + analysis.ReleaseDefCount); - Console.WriteLine("Errors: "); - foreach (var item in analysis.ErrorMessages) + model.id.AddMessage("Build Definitions Count: " + analysis.BuildDefCount); + model.id.AddMessage("Release Definitions Count: " + analysis.ReleaseDefCount); + if(analysis.ErrorMessages.Count>0) { - Console.WriteLine(item); + model.id.AddMessage("Errors: "); + foreach (var item in analysis.ErrorMessages) + { + model.id.AddMessage(item); + } } } } 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 { From 69e560e4995efec1d7438bdfae49ce208c6a38ae Mon Sep 17 00:00:00 2001 From: Akshay H Date: Mon, 21 Apr 2025 10:26:11 +0530 Subject: [PATCH 4/4] Enhance services and improve user interaction Refactored `IExtractorService` and `ITemplateService` to return `bool` for success/failure, added methods to check template existence. Improved user prompts and error handling in `Program.cs`, including better message formatting and clarity. Enhanced logging capabilities in `ServiceExtensions.cs`. Updated project selection output in `ProjectService.cs` for improved readability. Overall, these changes focus on enhancing user experience and error management throughout the application. --- .../IServices/IExtractorService.cs | 7 +- .../IServices/ITemplateService.cs | 1 + src/ADOGenerator/Program.cs | 177 ++++++--- src/ADOGenerator/ServiceExtensions.cs | 7 +- src/ADOGenerator/Services/AuthService.cs | 26 +- src/ADOGenerator/Services/ExtractorService.cs | 366 +++++++++--------- src/ADOGenerator/Services/ProjectService.cs | 39 +- src/ADOGenerator/Services/TemplateService.cs | 57 ++- 8 files changed, 422 insertions(+), 258 deletions(-) diff --git a/src/ADOGenerator/IServices/IExtractorService.cs b/src/ADOGenerator/IServices/IExtractorService.cs index 3ec0127..f52425b 100644 --- a/src/ADOGenerator/IServices/IExtractorService.cs +++ b/src/ADOGenerator/IServices/IExtractorService.cs @@ -21,11 +21,12 @@ public interface IExtractorService void ExportQuries(ProjectConfigurations appConfig, string extractedFolderName); bool ExportTeams(RestAPI.ADOConfiguration con, Project model, string extractedFolderName); bool ExportIterations(ProjectConfigurations appConfig, string extractedFolderName); - void ExportWorkItems(ProjectConfigurations appConfig, string extractedFolderName); - void ExportRepositoryList(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); - void ExportDeliveryPlans(ProjectConfigurations appConfig, string extractedFolderName); + bool ExportDeliveryPlans(ProjectConfigurations appConfig, string extractedFolderName); + bool IsTemplateExists(string templateName); } } diff --git a/src/ADOGenerator/IServices/ITemplateService.cs b/src/ADOGenerator/IServices/ITemplateService.cs index ef98d68..4e497d7 100644 --- a/src/ADOGenerator/IServices/ITemplateService.cs +++ b/src/ADOGenerator/IServices/ITemplateService.cs @@ -5,6 +5,7 @@ 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/Program.cs b/src/ADOGenerator/Program.cs index 6697384..82a08a8 100644 --- a/src/ADOGenerator/Program.cs +++ b/src/ADOGenerator/Program.cs @@ -8,6 +8,7 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; using RestAPI; +using System.Text; using System.Text.RegularExpressions; var configuration = new ConfigurationBuilder() @@ -49,7 +50,10 @@ } else { - id.ErrorId().AddMessage(Environment.NewLine + "Artifacts generation failed."); + if(!template.Equals("skipped", StringComparison.OrdinalIgnoreCase)) + { + id.ErrorId().AddMessage(Environment.NewLine + "Artifacts generation failed."); + } } break; @@ -61,25 +65,32 @@ } Console.ForegroundColor = ConsoleColor.Green; - Console.WriteLine("Do you want to create another project? (yes/no): press enter to confirm"); + 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)) { - 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)) + if (authChoice == "2") { - if (authenticationDetails != null) - { - authenticationDetails = (authenticationDetails.Value.accessToken, null, authenticationDetails.Value.authScheme); - } + authenticationDetails = null; } else { - authenticationDetails = 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)) + { + if (authenticationDetails != null) + { + authenticationDetails = (authenticationDetails.Value.accessToken, null, authenticationDetails.Value.authScheme); + } + } + else + { + authenticationDetails = null; + } } continue; } @@ -103,17 +114,17 @@ void HandleTemplateAndArtifactsUpdate(string template, string id, Project model, if (UpdateTemplateSettings(template, id, templatePathOriginal)) { - id.AddMessage("Template settings updated successfully at " + templatePathOriginal); + id.AddMessage(Environment.NewLine+"Template settings updated successfully at " + templatePathOriginal); } else { - id.ErrorId().AddMessage("Template settings update failed at " + templatePathOriginal); + id.ErrorId().AddMessage(Environment.NewLine+"Template settings update failed at " + templatePathOriginal); } CopyFileIfExists(id,templatePathOriginal, templatePathBin); - id.AddMessage("Template settings copied to the current directory and updated successfully."); - id.AddMessage("Moving artifacts to the current directory..."); + 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(" ", "-")}"); @@ -135,10 +146,10 @@ void CopyFileIfExists(string id,string sourcePath, string destinationPath) else { Console.ForegroundColor = ConsoleColor.Green; - id.AddMessage($"Source file '{sourcePath}' does not exist. Creating a new file at the destination."); + 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($"New file created at '{destinationPath}'."); + id.AddMessage(Environment.NewLine + $"New file created at '{destinationPath}'."); Console.ResetColor(); } } @@ -167,11 +178,11 @@ void MoveArtifacts(string sourcePath, string destinationPath, string id) File.Copy(file, destinationFile, true); } - id.AddMessage("Artifacts moved to the current directory."); + id.AddMessage(Environment.NewLine+"Artifacts moved to the current directory."); } else { - id.ErrorId().AddMessage("Artifacts directory not found at " + sourcePath); + id.ErrorId().AddMessage(Environment.NewLine+"Artifacts directory not found at " + sourcePath); } } @@ -180,14 +191,10 @@ void SkipTemplateAndArtifactsUpdate(string id, string currentPath, Project model 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(" ", "-")}"); - Console.ForegroundColor = ConsoleColor.Cyan; - id.AddMessage(Environment.NewLine+template); - Console.ResetColor(); - Console.ForegroundColor = ConsoleColor.Green; - id.AddMessage("Copy the generated template JSON and update the template settings manually in the following file location: " + templatePathBin); - id.AddMessage("Template settings update and copying artifacts skipped."); - id.AddMessage("Copy the generated artifacts directory from " + artifactsPathOriginal + " and update the artifacts manually in the following directory location: " + artifactsPath); - Console.ResetColor(); + 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); } @@ -265,36 +272,53 @@ void HandleNewProjectCreation(IConfiguration configuration, string id) }; ITemplateService templateService = new TemplateService(configuration); - var analyzed = templateService.AnalyzeProject(model); - if (analyzed) - { - model.id.AddMessage("Artifacts analyzed successfully."); - } - else - { - model.id.ErrorId().AddMessage("Artifacts analysis failed."); - } - 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 isTemplateExists = templateService.CheckTemplateExists(model); + if (isTemplateExists) { - (bool isArtifactsGenerated, string template, string templateLocation) = templateService.GenerateTemplateArtifacts(model); - if(isArtifactsGenerated) + 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) { - model.id.AddMessage(Environment.NewLine+"Artifacts has been generated sccessfully at the location: " + templateLocation); - return (true, template,model); + var analyzed = templateService.AnalyzeProject(model); + if (analyzed) + { + model.id.AddMessage("Artifacts analyzed successfully."); + } + else + { + model.id.ErrorId().AddMessage("Artifacts analysis failed."); + } + 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); + } } else { - model.id.ErrorId().AddMessage(Environment.NewLine + "Artifacts generation failed."); + id.AddMessage(Environment.NewLine + "Template update skipped."); + return (false, "skipped", null); } } - else - { - model.id.AddMessage(Environment.NewLine + "Artifacts generation skipped."); - } return (false, string.Empty, null); } @@ -328,17 +352,44 @@ string SelectTemplate(JToken groupwiseTemplates, string id) if (templates != null) { 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 templateName = template["Name"]?.ToString(); - Console.WriteLine($" {templateIndex}. {templateName}"); + 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); + + for (int i = 0; i < maxLines; i++) + { + var nameLine = i < templateNameLines.Length ? templateNameLines[i] : string.Empty; + var descriptionLine = i < templateDescriptionLines.Length ? templateDescriptionLines[i] : string.Empty; + + if (i == 0) + { + Console.WriteLine($"| {templateIndex,-10} | {nameLine,-30} | {descriptionLine,-50} |"); + } + else + { + Console.WriteLine($"| {"",-10} | {nameLine,-30} | {descriptionLine,-50} |"); + } + } + + Console.WriteLine(new string('-', 100)); // Divider after each row templateDictionary.Add(templateIndex, templateName); templateIndex++; } Console.ResetColor(); } } - 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)) { @@ -348,6 +399,26 @@ string SelectTemplate(JToken groupwiseTemplates, string id) 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(); +} + (string accessToken, string organizationName, string authScheme) AuthenticateUser(Init init, string id) { string organizationName = string.Empty; diff --git a/src/ADOGenerator/ServiceExtensions.cs b/src/ADOGenerator/ServiceExtensions.cs index e7e72eb..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) { @@ -62,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 eeedeef..0c0ae0b 100644 --- a/src/ADOGenerator/Services/AuthService.cs +++ b/src/ADOGenerator/Services/AuthService.cs @@ -74,10 +74,34 @@ public async Task SelectOrganization(string accessToken, JObject account 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; diff --git a/src/ADOGenerator/Services/ExtractorService.cs b/src/ADOGenerator/Services/ExtractorService.cs index 46c4248..6a431e8 100644 --- a/src/ADOGenerator/Services/ExtractorService.cs +++ b/src/ADOGenerator/Services/ExtractorService.cs @@ -44,68 +44,6 @@ public ExtractorService(IConfiguration config) { _config = config; } - - public static void AddMessage(string id, string message) - { - lock (objLock) - { - if (id.EndsWith("_Errors")) - { - StatusMessages[id] = (StatusMessages.ContainsKey(id) ? StatusMessages[id] : string.Empty) + message; - } - else - { - StatusMessages[id] = message; - } - } - } - public static Dictionary StatusMessages - { - get - { - if (statusMessages == null) - { - statusMessages = new Dictionary(); - } - - return statusMessages; - } - set - { - statusMessages = value; - } - } - public static string GetStatusMessage(string id) - { - lock (objLock) - { - string message = string.Empty; - if (StatusMessages.Keys.Count(x => x == id) == 1) - { - message = StatusMessages[id]; - } - else - { - return "100"; - } - - if (id.EndsWith("_Errors")) - { - RemoveKey(id); - } - - return message; - } - } - - public static void RemoveKey(string id) - { - lock (objLock) - { - StatusMessages.Remove(id); - } - } - #endregion STATIC DECLARATIONS #region ANALYSIS - GET COUNTS @@ -143,6 +81,50 @@ public ProjectConfigurations ProjectConfiguration(Project model) 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); @@ -204,18 +186,42 @@ public int GetReleaseDefinitionCount(ProjectConfigurations appConfig) } 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); } - AddMessage(model.id, ""); ProjectConfigurations appConfig = ProjectConfiguration(model); GetInstalledExtensions(appConfig, extractedFolderName); @@ -225,7 +231,7 @@ public string[] GenerateTemplateArifacts(Project model) if (ExportIterations(appConfig, extractedFolderName)) { - AddMessage(model.id, "Iterations Definition"); + model.id.AddMessage("Iterations Definition Exported"); } string filePathToRead = currentPath + @"\\PreSetting"; @@ -244,31 +250,30 @@ public string[] GenerateTemplateArifacts(Project model) teamArea = filePathToRead + "\\TeamArea.json"; teamArea = File.ReadAllText(teamArea); File.WriteAllText(extractedFolderName + "\\TeamArea.json", teamArea); - AddMessage(model.id, "Team Areas"); + model.id.AddMessage("Team Areas Exported"); - ExportWorkItems(appConfig, extractedFolderName); - AddMessage(model.id, "Work Items"); + if(ExportWorkItems(appConfig, extractedFolderName)) + model.id.AddMessage("Work Items Exported"); - ExportDeliveryPlans(appConfig, extractedFolderName); - AddMessage(model.id, "Delivery Plans"); + if(ExportDeliveryPlans(appConfig, extractedFolderName)) + model.id.AddMessage("Delivery Plans Exported"); - ExportRepositoryList(appConfig, extractedFolderName); - AddMessage(model.id, "Repository and Service Endpoint"); + if(ExportRepositoryList(appConfig, extractedFolderName)) + model.id.AddMessage("Repository Exported"); GetServiceEndpoints(appConfig, extractedFolderName); int count = GetBuildDefinitions(appConfig, extractedFolderName); if (count >= 1) { - AddMessage(model.id, "Build Definition"); + model.id.AddMessage("Build Definition Exported"); } int relCount = GeneralizingGetReleaseDefinitions(appConfig, extractedFolderName); if (relCount >= 1) { - AddMessage(model.id, "Release Definition"); + model.id.AddMessage("Release Definition Exported"); } - StatusMessages[model.id] = "100"; // Generate custom template JSON var customTemplateJson = new { @@ -295,32 +300,6 @@ public string[] GenerateTemplateArifacts(Project model) }; return new string[] { model.id, JsonConvert.SerializeObject(customTemplateJson, Formatting.Indented) , extractedFolderName }; } - - 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; - } - public List GetInstalledExtensions(ProjectConfigurations appConfig,string extractedFolderName) { try @@ -360,21 +339,21 @@ public Dictionary GetWorkItemsCount(ProjectConfigurations appConfig 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)) { - AddMessage(appConfig.ExtensionConfig.Id.ErrorId(), "Some error occured while fetching extensions"); + appConfig.ExtensionConfig.Id.ErrorId().AddMessage("Some error occured while fetching extensions"+Environment.NewLine+listExtenison.LastFailureMessage); } return extensionList; } catch (Exception ex) { - logger.Info(DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss") + "\t" + ex.Message + "\n" + ex.StackTrace + "\n"); + 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 @@ -439,19 +418,19 @@ public void ExportQuries(ProjectConfigurations appConfig, string extractedFolder } } } + appConfig.QueriesConfig.Id.AddMessage("Queries Exported"); } else if (!string.IsNullOrEmpty(queries.LastFailureMessage)) { - AddMessage(appConfig.QueriesConfig.Id.ErrorId(), "Error while fetching queries"); + appConfig.QueriesConfig.Id.ErrorId().AddMessage("Error while fetching queries"); } } catch (Exception ex) { - logger.Info(DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss") + "\t" + ex.Message + "\n" + ex.StackTrace + "\n"); + 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 @@ -474,7 +453,7 @@ public bool ExportTeams(Configuration con, Project model,string extractedFolderN _team = nodes.ExportTeamList(defaultTeamID); if (_team.value != null) { - AddMessage(con.Id, "Teams"); + con.Id.AddMessage("Teams"); string fetchedJson = JsonConvert.SerializeObject(_team.value, Formatting.Indented); if (fetchedJson != "") @@ -548,7 +527,7 @@ public bool ExportTeams(Configuration con, Project model,string extractedFolderN basicColumns.BoardName = boardType; columnResponsesBasic.Add(basicColumns); } - AddMessage(con.Id, "Board Columns"); + con.Id.AddMessage("Board Columns Exported for "+ boardType); Thread.Sleep(2000); } else @@ -556,7 +535,7 @@ public bool ExportTeams(Configuration con, Project model,string extractedFolderN var errorMessage = response.Content.ReadAsStringAsync(); string error = RestAPI.Utility.GeterroMessage(errorMessage.Result.ToString()); teamNodes.LastFailureMessage = error; - AddMessage(con.Id.ErrorId(), "Error occured while exporting Board Columns: " + teamNodes.LastFailureMessage); + con.Id.ErrorId().AddMessage("Error occured while exporting Board Columns: " + teamNodes.LastFailureMessage); } //Export board rows for each team @@ -565,12 +544,12 @@ public bool ExportTeams(Configuration con, Project model,string extractedFolderN { rows.BoardName = boardType; boardRows.Add(rows); - AddMessage(con.Id, "Board Rows"); + con.Id.AddMessage("Board Rows Exported for "+boardType); Thread.Sleep(2000); } else if (!string.IsNullOrEmpty(teamNodes.LastFailureMessage)) { - AddMessage(con.Id.ErrorId(), "Error occured while exporting Board Rows: " + teamNodes.LastFailureMessage); + con.Id.ErrorId().AddMessage("Error occured while exporting Board Rows: " + teamNodes.LastFailureMessage); } @@ -582,7 +561,7 @@ public bool ExportTeams(Configuration con, Project model,string extractedFolderN JObject jObj = JsonConvert.DeserializeObject(res); jObj["BoardName"] = boardType; jObjCardFieldList.Add(jObj); - AddMessage(con.Id, "Card fields Definition"); + con.Id.AddMessage("Card fields Definition Exported for "+ boardType); } else @@ -590,10 +569,10 @@ public bool ExportTeams(Configuration con, Project model,string extractedFolderN var errorMessage = cardFieldResponse.Content.ReadAsStringAsync(); string error = RestAPI.Utility.GeterroMessage(errorMessage.Result.ToString()); teamNodes.LastFailureMessage = error; - AddMessage(con.Id.ErrorId(), "Error occured while exporting Card Fields: " + teamNodes.LastFailureMessage); + con.Id.ErrorId().AddMessage("Error occured while exporting Card Fields: " + teamNodes.LastFailureMessage); } - //// Export card styles for each team + // Export card styles for each team var cardStyleResponse = teamNodes.ExportCardStyle(boardType); if (cardStyleResponse.IsSuccessStatusCode && cardStyleResponse.StatusCode == System.Net.HttpStatusCode.OK) { @@ -610,7 +589,7 @@ public bool ExportTeams(Configuration con, Project model,string extractedFolderN style["rules"]["tagStyle"] = new JArray(); } jObjcardStyleList.Add(jObj); - AddMessage(con.Id, "Card style"); + con.Id.AddMessage("Card style exported for "+ boardType); } else @@ -618,7 +597,7 @@ public bool ExportTeams(Configuration con, Project model,string extractedFolderN var errorMessage = cardStyleResponse.Content.ReadAsStringAsync(); string error = RestAPI.Utility.GeterroMessage(errorMessage.Result.ToString()); teamNodes.LastFailureMessage = error; - AddMessage(con.Id.ErrorId(), "Error occured while exporting Card Styles: " + teamNodes.LastFailureMessage); + con.Id.ErrorId().AddMessage("Error occured while exporting Card Styles: " + teamNodes.LastFailureMessage); } } //Export Team Setting for each team @@ -628,12 +607,12 @@ public bool ExportTeams(Configuration con, Project model,string extractedFolderN if (teamSetting.backlogVisibilities != null) { listTeamSetting = teamSetting; - AddMessage(con.Id, "Team Settings Definition"); + con.Id.AddMessage("Team Settings Definition Exported"); } } else if (!string.IsNullOrEmpty(teamNodes.LastFailureMessage)) { - AddMessage(con.Id.ErrorId(), "Error occured while exporting Team Setting: " + teamNodes.LastFailureMessage); + con.Id.ErrorId().AddMessage("Error occured while exporting Team Setting: " + teamNodes.LastFailureMessage); } if (columnResponsesAgile.Count > 0) @@ -670,7 +649,7 @@ public bool ExportTeams(Configuration con, Project model,string extractedFolderN } else if (!string.IsNullOrEmpty(nodes.LastFailureMessage)) { - AddMessage(con.Id.ErrorId(), nodes.LastFailureMessage); + con.Id.ErrorId().AddMessage(nodes.LastFailureMessage); string error = nodes.LastFailureMessage; return false; } @@ -681,17 +660,16 @@ public bool ExportTeams(Configuration con, Project model,string extractedFolderN } else { - AddMessage(con.Id.ErrorId(), nodes.LastFailureMessage); + con.Id.ErrorId().AddMessage(nodes.LastFailureMessage); return false; } } catch (Exception ex) { - logger.Info(DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss") + "\t" + ex.Message + "\n" + ex.StackTrace + "\n"); + 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 @@ -711,86 +689,103 @@ public bool ExportIterations(ProjectConfigurations appConfig,string extractedFol else { string error = nodes.LastFailureMessage; - AddMessage(appConfig.BoardConfig.Id.ErrorId(), error); + appConfig.BoardConfig.Id.ErrorId().AddMessage(error); return false; } } catch (Exception ex) { - logger.Info(DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss") + "\t" + ex.Message + "\n" + ex.StackTrace + "\n"); + appConfig.BoardConfig.Id.ErrorId().AddMessage(DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss") + "\t" + ex.Message + "\n" + ex.StackTrace + "\n"); } return false; } - - public void ExportWorkItems(ProjectConfigurations appConfig, string extractedFolderName) + public bool ExportWorkItems(ProjectConfigurations appConfig, string extractedFolderName) { - string[] workItemtypes = GetAllWorkItemsName(appConfig);//{ "Epic", "Feature", "Product Backlog Item", "Task", "Test Case", "Bug", "User Story", "Test Suite", "Test Plan", "Issue" }; - if (!Directory.Exists(extractedFolderName)) + bool isWorkItemExported = false; + try { - Directory.CreateDirectory(extractedFolderName); - } + 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) + if (workItemtypes.Length > 0) { - GetWorkItemsCount WorkitemsCount = new GetWorkItemsCount(appConfig.WorkItemConfig); - WorkItemFetchResponse.WorkItems fetchedWorkItem = WorkitemsCount.GetWorkItemsfromSource(WIT); - string workItemJson = JsonConvert.SerializeObject(fetchedWorkItem, Formatting.Indented); - if (fetchedWorkItem.count > 0) + foreach (var WIT in workItemtypes) { - workItemJson = workItemJson.Replace(appConfig.WorkItemConfig.Project + "\\", "$ProjectName$\\"); - string item = WIT; - if (!Directory.Exists(extractedFolderName + "\\WorkItems")) + GetWorkItemsCount WorkitemsCount = new GetWorkItemsCount(appConfig.WorkItemConfig); + WorkItemFetchResponse.WorkItems fetchedWorkItem = WorkitemsCount.GetWorkItemsfromSource(WIT); + string workItemJson = JsonConvert.SerializeObject(fetchedWorkItem, Formatting.Indented); + if (fetchedWorkItem.count > 0) { - Directory.CreateDirectory(extractedFolderName + "\\WorkItems"); + 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); } - File.WriteAllText(extractedFolderName + "\\WorkItems\\" + item + ".json", workItemJson); - } - else if (!string.IsNullOrEmpty(WorkitemsCount.LastFailureMessage)) - { - AddMessage(appConfig.WorkItemConfig.Id.ErrorId(), 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 void ExportRepositoryList(ProjectConfigurations appConfig, string extractedFolderName) + public bool ExportRepositoryList(ProjectConfigurations appConfig, string extractedFolderName) { - BuildandReleaseDefs repolist = new BuildandReleaseDefs(appConfig.RepoConfig); - RepositoryList.Repository repos = repolist.GetRepoList(); - if (repos.count > 0) + bool isRepoExported = false; + try { - foreach (var repo in repos.value) + BuildandReleaseDefs repolist = new BuildandReleaseDefs(appConfig.RepoConfig); + RepositoryList.Repository repos = repolist.GetRepoList(); + if (repos.count > 0) { - 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")) + foreach (var repo in repos.value) { - 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); + 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 /// @@ -1372,7 +1367,7 @@ public int GeneralizingGetReleaseDefinitions(ProjectConfigurations appConfig, st catch (Exception ex) { logger.Info(DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss") + "\t" + ex.Message + "\n" + ex.StackTrace + "\n"); - AddMessage(appConfig.ReleaseDefinitionConfig.Id.ErrorId(), ex.Message + Environment.NewLine + ex.StackTrace); + appConfig.ReleaseDefinitionConfig.Id.ErrorId().AddMessage(ex.Message + Environment.NewLine + ex.StackTrace); } return 0; } @@ -1542,15 +1537,16 @@ public void GetServiceEndpoints(ProjectConfigurations appConfig, string extracte 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)) { - AddMessage(appConfig.EndpointConfig.Id.ErrorId(), "Error occured while fetchin service endpoints"); + appConfig.EndpointConfig.Id.ErrorId().AddMessage("Error occured while fetching service endpoints"); } } catch (Exception ex) { - logger.Info(DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss") + "\t" + ex.Message + "\n" + ex.StackTrace + "\n"); + appConfig.EndpointConfig.Id.ErrorId().AddMessage(DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss") + "\t" + ex.Message + "\n" + ex.StackTrace + "\n"); } } /// @@ -1572,7 +1568,6 @@ private string[] GetAllWorkItemsName(ProjectConfigurations appConfig) } return workItemNames.ToArray(); } - private Dictionary GetVariableGroups(ProjectConfigurations appConfig, string extractedFolderName) { VariableGroups variableGroups = new VariableGroups(appConfig.VariableGroupConfig); @@ -1599,9 +1594,9 @@ private Dictionary GetVariableGroups(ProjectConfigurations appCo } return varibaleGroupDictionary; } - - public void ExportDeliveryPlans(ProjectConfigurations appConfig, string extractedFolderName) + public bool ExportDeliveryPlans(ProjectConfigurations appConfig, string extractedFolderName) { + bool isDeliveryPlanExported = false; try { Plans plans = new Plans(appConfig.WorkItemConfig); @@ -1651,16 +1646,15 @@ public void ExportDeliveryPlans(ProjectConfigurations appConfig, string extracte } } } + 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 - - + #endregion END GENERATE ARTIFACTS } } diff --git a/src/ADOGenerator/Services/ProjectService.cs b/src/ADOGenerator/Services/ProjectService.cs index 4e29ed3..331ec0f 100644 --- a/src/ADOGenerator/Services/ProjectService.cs +++ b/src/ADOGenerator/Services/ProjectService.cs @@ -107,10 +107,47 @@ public async Task> SelectProject(string accessToken, HttpResponseMe 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++) { - Console.WriteLine($"{i + 1}. {projects[i]["name"]} (ID: {projects[i]["id"]})"); + 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"); diff --git a/src/ADOGenerator/Services/TemplateService.cs b/src/ADOGenerator/Services/TemplateService.cs index fc4c960..608c8b3 100644 --- a/src/ADOGenerator/Services/TemplateService.cs +++ b/src/ADOGenerator/Services/TemplateService.cs @@ -58,6 +58,27 @@ public bool AnalyzeProject(Project model) 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) { @@ -82,28 +103,38 @@ public bool AnalyzeProject(Project model) private void LogAnalysisResults(Project model, ExtractorAnalysis analysis) { - model.id.AddMessage("Analysis of the project"); - model.id.AddMessage("Project Name: " + model.ProjectName); - model.id.AddMessage("Process Template Type: " + model.ProcessTemplate); - model.id.AddMessage("Teams Count: " + analysis.teamCount); - model.id.AddMessage("Iterations Count: " + analysis.IterationCount); - if(analysis.WorkItemCounts.Count > 0) + 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("Work Items Count: "); + model.id.AddMessage("|-----------------------------------------------------------------|"); + model.id.AddMessage("| Work Items Count: |"); + model.id.AddMessage("|-----------------------------------------------------------------|"); foreach (var item in analysis.WorkItemCounts) { - model.id.AddMessage(item.Key + " : " + item.Value); + model.id.AddMessage($"| {item.Key.PadRight(30)} | {item.Value.ToString().PadRight(30)} |"); } } - model.id.AddMessage("Build Definitions Count: " + analysis.BuildDefCount); - model.id.AddMessage("Release Definitions Count: " + analysis.ReleaseDefCount); - if(analysis.ErrorMessages.Count>0) + if (analysis.ErrorMessages.Count > 0) { - model.id.AddMessage("Errors: "); + model.id.AddMessage("|-----------------------------------------------------------------|"); + model.id.AddMessage("| Errors: |"); + model.id.AddMessage("|-----------------------------------------------------------------|"); foreach (var item in analysis.ErrorMessages) { - model.id.AddMessage(item); + model.id.AddMessage($"| {item.PadRight(60)} |"); } } + model.id.AddMessage("-------------------------------------------------------------------"); } }