diff --git a/.gitignore b/.gitignore index 1a4672f5..af8467dd 100644 --- a/.gitignore +++ b/.gitignore @@ -148,3 +148,4 @@ UpgradeLog.htm .cr/ /src/VstsDemoBuilder/Logs /src/VstsDemoBuilder/ExtractedTemplate/adqw +/src/AzureDevOpsDemoBuilder/log diff --git a/docs/Using-Private-template-URL.md b/docs/Using-Private-template-URL.md new file mode 100644 index 00000000..100bbbf8 --- /dev/null +++ b/docs/Using-Private-template-URL.md @@ -0,0 +1,27 @@ +# Using Private template URL + +Once you generate the custom tempalte using [Azure DevOps Demo Generator - Tempalte Builder](https://azuredevopsdemogenerator.azurewebsites.net/), you can use the same template as a master copy to provision the sample projects. + +## Provisioning your project from your custom template using public URL + +Using private template URL you can provisoin the project. You have multiple options here + + +1. Using the template URL from public GitHub repository, refer [Provisioning the project from your custom template](./Using-The-Template-Extractor.md) + +1. Use the template URL in the home page of [Azure DevOps Demo Generator](https://azuredevopsdemogenerator.azurewebsites.net/) with querystring parameter **?templateurl**. This supports only the public URL + + >```Ex: https://azuredevopsdemogenerator.azurewebsites.net/?templateurl=public_url ``` + + Once you format the URL, press enter to reload the page. +1. Click on **Sign In** button + +1. Once you navigate to **Create Project** page, you should see the private tempalte selected by default + +1. Select the **Organization**. Provide the **Project Name**. Choose **Create Project** to start provisioning a project + + > **Note**: Once the process completes, the template will be discarded by the system. If you want to reuse the tempalte, you can use the **Private** option in the **Choose Template** dialog box + + You can refer the document [Provisioning your project from your custom template](./Using-The-Template-Extractor.md) + +Previous: [Using the Extractor](./Using-The-Template-Extractor.md) \ No newline at end of file diff --git a/docs/Using-The-Template-Extractor.md b/docs/Using-The-Template-Extractor.md index 9d5b9264..1399a8cc 100644 --- a/docs/Using-The-Template-Extractor.md +++ b/docs/Using-The-Template-Extractor.md @@ -44,10 +44,20 @@ Setup an Azure DevOps project and make sure it is ready to be extracted. The ext 1. You will see a new tab labelled **Private**. Select the tab. -1. Select **Browse** and select the zip file you downloaded zip file. + Here you have three option -1. Click **OK** to close the dialog. Choose **Create Project** to start provisioning a project + 1. Select **Browse** and select the zip file you downloaded zip file. + + 1. Use the radio button **GitHub** to consume the private template from a github raw URL + + 1. Use the radio button **URL** to consume the private template from other sources + +1. Once you point the right template, click **Submit** to validate and pick the template for provisoning the project + +1. Choose **Create Project** to start provisioning a project ------------- Previous: [Using the Generator](./Using-The-Generator.md) + +Next: [Using Private template URL](./Using-Private-template-URL.md) \ No newline at end of file diff --git a/src/VstsDemoBuilder/Controllers/AccountController.cs b/src/VstsDemoBuilder/Controllers/AccountController.cs index e77aca48..9eb87edc 100644 --- a/src/VstsDemoBuilder/Controllers/AccountController.cs +++ b/src/VstsDemoBuilder/Controllers/AccountController.cs @@ -2,6 +2,8 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; +using System.IO; +using System.Web.Hosting; using System.Web.Mvc; using VstsDemoBuilder.Models; using VstsDemoBuilder.ServiceInterfaces; @@ -16,10 +18,12 @@ namespace VstsDemoBuilder.Controllers private TemplateSelection.Templates templates = new TemplateSelection.Templates(); private ILog logger = LogManager.GetLogger("ErrorLog"); private IProjectService projectService; + private ITemplateService templateService; - public AccountController(IProjectService _projectService) + public AccountController(IProjectService _projectService, ITemplateService _templateService) { projectService = _projectService; + templateService = _templateService; } [HttpGet] @@ -49,6 +53,7 @@ namespace VstsDemoBuilder.Controllers { Session["EnableExtractor"] = model.EnableExtractor; } + var browser = Request.Browser.Type; if (browser.Contains("InternetExplorer")) { @@ -101,6 +106,25 @@ namespace VstsDemoBuilder.Controllers } } } + + if (!string.IsNullOrEmpty(model.TemplateURL)) + { + if (model.TemplateURL.EndsWith(".zip")) + { + PrivateTemplate _privateTemplate = UploadPrivateTempalteFromHome(model.TemplateURL); + if (_privateTemplate.IsTemplateValid) + { + Session["PrivateTemplateURL"] = _privateTemplate.privateTemplatePath; + Session["PrivateTemplateName"] = _privateTemplate.privateTemplateName; + Session["PrivateTemplateOriginalName"] = _privateTemplate.privateTemplateOriginalName; + } + else + { + ViewBag.resMessage = _privateTemplate.responseMessage; + return View(new LoginModel()); + } + } + } } catch (Exception ex) { @@ -172,5 +196,48 @@ namespace VstsDemoBuilder.Controllers { return View(); } + + public PrivateTemplate UploadPrivateTempalteFromHome(string TemplateURL) + { + PrivateTemplate privateTemplate = new PrivateTemplate(); + string templatePath = string.Empty; + try + { + privateTemplate.IsTemplateValid = false; + string templateName = ""; + string fileName = Path.GetFileName(TemplateURL); + string extension = Path.GetExtension(TemplateURL); + privateTemplate.privateTemplateOriginalName = fileName.ToLower().Replace(".zip", "").Trim(); + templateName = fileName.ToLower().Replace(".zip", "").Trim() + "-" + Guid.NewGuid().ToString().Substring(0, 6) + extension.ToLower(); + privateTemplate.privateTemplateName = templateName.ToLower().Replace(".zip", "").Trim(); + privateTemplate.privateTemplatePath = templateService.GetTemplateFromPath(TemplateURL, templateName, "", "", ""); + + if (privateTemplate.privateTemplatePath != "") + { + privateTemplate.responseMessage = templateService.checkSelectedTemplateIsPrivate(privateTemplate.privateTemplatePath); + if (privateTemplate.responseMessage != "SUCCESS") + { + var templatepath = HostingEnvironment.MapPath("~") + @"\PrivateTemplates\" + templateName.ToLower().Replace(".zip", "").Trim(); + if (Directory.Exists(templatepath)) + Directory.Delete(templatepath, true); + } + if (privateTemplate.responseMessage == "SUCCESS") + { + privateTemplate.IsTemplateValid = true; + } + } + else + { + privateTemplate.responseMessage = "Unable to download file, please check the provided URL"; + privateTemplate.IsTemplateValid = false; + } + + } + catch (Exception ex) + { + ProjectService.logger.Info(DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss") + "\t" + "\t" + ex.Message + "\t" + "\n" + ex.StackTrace + "\n"); + } + return privateTemplate; + } } } diff --git a/src/VstsDemoBuilder/Controllers/EnvironmentController.cs b/src/VstsDemoBuilder/Controllers/EnvironmentController.cs index 3a3adebd..1200c332 100644 --- a/src/VstsDemoBuilder/Controllers/EnvironmentController.cs +++ b/src/VstsDemoBuilder/Controllers/EnvironmentController.cs @@ -168,11 +168,16 @@ namespace VstsDemoBuilder.Controllers model.TemplateName = Session["templateName"].ToString(); TemplateSelected = model.TemplateName; } + else if (Session["PrivateTemplateName"] != null) + { + model.TemplateName = Session["PrivateTemplateName"].ToString(); + TemplateSelected = model.TemplateName; + } else { TemplateSelected = System.Configuration.ConfigurationManager.AppSettings["DefaultTemplate"]; + model.TemplateName = TemplateSelected; } - if (Session["PAT"] != null) { _accessDetails.access_token = Session["PAT"].ToString(); @@ -229,25 +234,37 @@ namespace VstsDemoBuilder.Controllers //if exist, will append the template name to Selected template textbox, else will append the SmartHotel360 template if (!string.IsNullOrEmpty(TemplateSelected)) { - foreach (var grpTemplate in templates.GroupwiseTemplates) + if (Session["PrivateTemplateName"] == null) { - foreach (var template in grpTemplate.Template) + foreach (var grpTemplate in templates.GroupwiseTemplates) { - if (template.Name != null) + foreach (var template in grpTemplate.Template) { - if (template.Name.ToLower() == TemplateSelected.ToLower()) + if (template.Name != null) { - model.SelectedTemplate = template.Name; - model.Templates.Add(template.Name); - model.selectedTemplateDescription = template.Description == null ? string.Empty : template.Description; - model.selectedTemplateFolder = template.TemplateFolder == null ? string.Empty : template.TemplateFolder; - model.Message = template.Message == null ? string.Empty : template.Message; - model.ForkGitHubRepo = template.ForkGitHubRepo.ToString(); - model.templateImage = template.Image ?? "/Templates/TemplateImages/CodeFile.png"; + if (template.Name.ToLower() == TemplateSelected.ToLower()) + { + model.SelectedTemplate = template.Name; + model.Templates.Add(template.Name); + model.selectedTemplateDescription = template.Description == null ? string.Empty : template.Description; + model.selectedTemplateFolder = template.TemplateFolder == null ? string.Empty : template.TemplateFolder; + model.Message = template.Message == null ? string.Empty : template.Message; + model.ForkGitHubRepo = template.ForkGitHubRepo.ToString(); + model.templateImage = template.Image ?? "/Templates/TemplateImages/CodeFile.png"; + } } } } } + else + { + model.SelectedTemplate = Session["PrivateTemplateOriginalName"].ToString(); + model.Templates.Add(model.SelectedTemplate); + model.selectedTemplateDescription = "

Note: Template will be discarded once the process completes. Please refersh the page to select other templates

"; + model.selectedTemplateFolder = Session["PrivateTemplateName"].ToString(); + model.ForkGitHubRepo = "false"; + model.templateImage = "/Templates/TemplateImages/CodeFile.png"; + } } return View(model); } @@ -518,9 +535,18 @@ namespace VstsDemoBuilder.Controllers { model.GitHubToken = Session["GitHubToken"].ToString(); } + if (Session["PrivateTemplateURL"] != null && Session["PrivateTemplateName"] != null) + { + model.PrivateTemplatePath = Session["PrivateTemplateURL"].ToString(); + Session["PrivateTemplateURL"] = null; + Session["PrivateTemplateName"] = null; + Session["PrivateTemplateOriginalName"] = null; + Session["templateName"] = System.Configuration.ConfigurationManager.AppSettings["DefaultTemplate"]; + } projectService.AddMessage(model.id, string.Empty); projectService.AddMessage(model.id.ErrorId(), string.Empty); - if (!string.IsNullOrEmpty(model.PrivateTemplatePath)) + bool whereIsTemplate = projectService.WhereDoseTemplateBelongTo(model.SelectedTemplate); // checking for private template existance + if (!string.IsNullOrEmpty(model.PrivateTemplatePath) && whereIsTemplate) // if the template path exist and tempalte is present in private fodler { model.IsPrivatePath = true; } @@ -620,6 +646,7 @@ namespace VstsDemoBuilder.Controllers { try { + bool isTemplateBelongToPrivateFolder = projectService.WhereDoseTemplateBelongTo(selectedTemplate); if (!string.IsNullOrEmpty(selectedTemplate) && !string.IsNullOrEmpty(account) && !string.IsNullOrEmpty(token)) { string accountName = string.Empty; @@ -629,7 +656,12 @@ namespace VstsDemoBuilder.Controllers pat = token; string templatesFolder = string.Empty; string extensionJsonFile = string.Empty; - if (string.IsNullOrEmpty(PrivatePath)) + if (isTemplateBelongToPrivateFolder) + { + templatesFolder = Session["PrivateTemplateURL"].ToString(); + extensionJsonFile = string.Format(templatesFolder + @"\Extensions.json"); + } + else if (string.IsNullOrEmpty(PrivatePath)) { templatesFolder = Server.MapPath("~") + @"\Templates\"; extensionJsonFile = string.Format(templatesFolder + @"\{0}\Extensions.json", selectedTemplate); @@ -640,8 +672,6 @@ namespace VstsDemoBuilder.Controllers extensionJsonFile = string.Format(templatesFolder + @"\Extensions.json"); } - - if (!(System.IO.File.Exists(extensionJsonFile))) { return Json(new { message = "Template not found", status = "false" }, JsonRequestBehavior.AllowGet); diff --git a/src/VstsDemoBuilder/Models/LoginModel.cs b/src/VstsDemoBuilder/Models/LoginModel.cs index 4370c3c9..b2f50fa5 100644 --- a/src/VstsDemoBuilder/Models/LoginModel.cs +++ b/src/VstsDemoBuilder/Models/LoginModel.cs @@ -13,5 +13,6 @@ public string name { get; set; } public string EnableExtractor { get; set; } + public string TemplateURL { get; set; } } } \ No newline at end of file diff --git a/src/VstsDemoBuilder/Models/Project.cs b/src/VstsDemoBuilder/Models/Project.cs index 56b802a6..b75a3b3d 100644 --- a/src/VstsDemoBuilder/Models/Project.cs +++ b/src/VstsDemoBuilder/Models/Project.cs @@ -223,8 +223,10 @@ namespace VstsDemoBuilder.Models public class PrivateTemplate { public string privateTemplateName { get; set; } + public string privateTemplateOriginalName { get; set; } public string privateTemplatePath { get; set; } public string responseMessage { get; set; } + public bool IsTemplateValid { get; set; } } } \ No newline at end of file diff --git a/src/VstsDemoBuilder/ServiceInterfaces/IProjectService.cs b/src/VstsDemoBuilder/ServiceInterfaces/IProjectService.cs index 03e1741a..08d106ee 100644 --- a/src/VstsDemoBuilder/ServiceInterfaces/IProjectService.cs +++ b/src/VstsDemoBuilder/ServiceInterfaces/IProjectService.cs @@ -21,5 +21,8 @@ namespace VstsDemoBuilder.ServiceInterfaces bool CheckForInstalledExtensions(string extensionJsonFile, string token, string account); bool InstallExtensions(Project model, string accountName, string PAT); + + bool WhereDoseTemplateBelongTo(string templatName); + } } diff --git a/src/VstsDemoBuilder/Services/ProjectService.cs b/src/VstsDemoBuilder/Services/ProjectService.cs index 0cd6e90d..5bb3ee6c 100644 --- a/src/VstsDemoBuilder/Services/ProjectService.cs +++ b/src/VstsDemoBuilder/Services/ProjectService.cs @@ -1576,7 +1576,7 @@ namespace VstsDemoBuilder.Services string repositoryId = string.Empty; if (model.SelectedTemplate == "MyHealthClinic") { repositoryId = model.Environment.repositoryIdList["MyHealthClinic"]; } if (model.SelectedTemplate == "SmartHotel360") { repositoryId = model.Environment.repositoryIdList["PublicWeb"]; } - else { repositoryId = model.Environment.repositoryIdList[model.SelectedTemplate]; } + else { repositoryId = model.Environment.repositoryIdList.ContainsKey(model.SelectedTemplate) ? model.Environment.repositoryIdList[model.SelectedTemplate] : ""; } pullRequestJsonPath = model.ReadJsonFile(pullRequestJsonPath); pullRequestJsonPath = pullRequestJsonPath.Replace("$reviewer$", model.Environment.UserUniqueId); @@ -2764,5 +2764,24 @@ namespace VstsDemoBuilder.Services } } } + + /// + /// Checkign for template existance - if template is present in private path, return true else return false + /// + /// + /// + public bool WhereDoseTemplateBelongTo(string templatName) + { + string privatePath = HostingEnvironment.MapPath("~") + @"\PrivateTemplates\"; + string privateTemplate = HostingEnvironment.MapPath("~") + @"\PrivateTemplates\" + templatName; + //string publicPath = HostingEnvironment.MapPath("~") + @"\Templates\"; + string[] privatedirs = Directory.GetDirectories(privatePath); + //string[] publicdirs = Directory.GetDirectories(privatePath); + if (privatedirs.Contains(privateTemplate)) + { + return true; + } + return false; + } } } \ No newline at end of file diff --git a/src/VstsDemoBuilder/Services/TemplateService.cs b/src/VstsDemoBuilder/Services/TemplateService.cs index dfaeb1d2..9f283576 100644 --- a/src/VstsDemoBuilder/Services/TemplateService.cs +++ b/src/VstsDemoBuilder/Services/TemplateService.cs @@ -187,7 +187,6 @@ namespace VstsDemoBuilder.Services webClient.DownloadFile(TemplateUrl, path); webClient.Dispose(); } - templatePath = ExtractZipFile(path, templateName); } diff --git a/src/VstsDemoBuilder/Views/Account/Verify.cshtml b/src/VstsDemoBuilder/Views/Account/Verify.cshtml index f7ddd75c..05189ed5 100644 --- a/src/VstsDemoBuilder/Views/Account/Verify.cshtml +++ b/src/VstsDemoBuilder/Views/Account/Verify.cshtml @@ -48,6 +48,10 @@ + @if (!string.IsNullOrEmpty(ViewBag.resMessage)) + { +

@ViewBag.resMessage

+ }