diff --git a/GoogleCloudExtension/GoogleCloudExtension.Deployment.UnitTests/FakeParsedProject.cs b/GoogleCloudExtension/GoogleCloudExtension.Deployment.UnitTests/FakeParsedProject.cs index 844f84bdf..a95909b87 100644 --- a/GoogleCloudExtension/GoogleCloudExtension.Deployment.UnitTests/FakeParsedProject.cs +++ b/GoogleCloudExtension/GoogleCloudExtension.Deployment.UnitTests/FakeParsedProject.cs @@ -35,5 +35,8 @@ public class FakeParsedProject : IParsedProject /// The type of the project. /// public KnownProjectTypes ProjectType { get; set; } + + /// The version of the framework used by the project. + public string FrameworkVersion { get; set; } } } diff --git a/GoogleCloudExtension/GoogleCloudExtension.Deployment/GoogleCloudExtension.Deployment.csproj b/GoogleCloudExtension/GoogleCloudExtension.Deployment/GoogleCloudExtension.Deployment.csproj index 766770a34..482794533 100644 --- a/GoogleCloudExtension/GoogleCloudExtension.Deployment/GoogleCloudExtension.Deployment.csproj +++ b/GoogleCloudExtension/GoogleCloudExtension.Deployment/GoogleCloudExtension.Deployment.csproj @@ -94,7 +94,6 @@ - diff --git a/GoogleCloudExtension/GoogleCloudExtension.Deployment/IParsedProject.cs b/GoogleCloudExtension/GoogleCloudExtension.Deployment/IParsedProject.cs index 932b6fcce..0102144d6 100644 --- a/GoogleCloudExtension/GoogleCloudExtension.Deployment/IParsedProject.cs +++ b/GoogleCloudExtension/GoogleCloudExtension.Deployment/IParsedProject.cs @@ -32,8 +32,11 @@ public interface IParsedProject string DirectoryPath { get; } /// - /// The type of the project. + /// The type (framework) of the project. /// KnownProjectTypes ProjectType { get; } + + /// The version of the framework used by the project. + string FrameworkVersion { get; } } } diff --git a/GoogleCloudExtension/GoogleCloudExtension.Deployment/IParsedProjectExtensions.cs b/GoogleCloudExtension/GoogleCloudExtension.Deployment/IParsedProjectExtensions.cs deleted file mode 100644 index f4a169b0b..000000000 --- a/GoogleCloudExtension/GoogleCloudExtension.Deployment/IParsedProjectExtensions.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2017 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - - -namespace GoogleCloudExtension.Deployment -{ - /// - /// Useful checks for projects. - /// - public static class IParsedProjectExtensions - { - /// - /// Determines if the given project is a .NET Core project or not. - /// - /// The project to check. - /// Returns true if the project is a .NET Core project, false otherwise. - public static bool IsAspNetCoreProject(this IParsedProject project) - => project.ProjectType == KnownProjectTypes.NetCoreWebApplication1_0 || - project.ProjectType == KnownProjectTypes.NetCoreWebApplication1_1 || - project.ProjectType == KnownProjectTypes.NetCoreWebApplication2_0; - } -} diff --git a/GoogleCloudExtension/GoogleCloudExtension.Deployment/KnownProjectTypes.cs b/GoogleCloudExtension/GoogleCloudExtension.Deployment/KnownProjectTypes.cs index 8a2169b3c..079f45399 100644 --- a/GoogleCloudExtension/GoogleCloudExtension.Deployment/KnownProjectTypes.cs +++ b/GoogleCloudExtension/GoogleCloudExtension.Deployment/KnownProjectTypes.cs @@ -30,18 +30,8 @@ public enum KnownProjectTypes WebApplication, /// - /// An ASP.NET Core 1.0 app + /// An ASP.NET Core app /// - NetCoreWebApplication1_0, - - /// - /// An ASP.NET Core 1.1 app - /// - NetCoreWebApplication1_1, - - /// - /// An ASP.NET Core 2.0 app - /// - NetCoreWebApplication2_0, + NetCoreWebApplication } } \ No newline at end of file diff --git a/GoogleCloudExtension/GoogleCloudExtension.Deployment/NetCoreAppUtils.cs b/GoogleCloudExtension/GoogleCloudExtension.Deployment/NetCoreAppUtils.cs index 23a8a3ed6..5e465dfa6 100644 --- a/GoogleCloudExtension/GoogleCloudExtension.Deployment/NetCoreAppUtils.cs +++ b/GoogleCloudExtension/GoogleCloudExtension.Deployment/NetCoreAppUtils.cs @@ -28,14 +28,7 @@ namespace GoogleCloudExtension.Deployment internal static class NetCoreAppUtils { internal const string DockerfileName = "Dockerfile"; - - // The mapping of supported .NET Core versions to the base images to use for the Docker image. - private static readonly Dictionary s_knownRuntimeImages = new Dictionary - { - [KnownProjectTypes.NetCoreWebApplication1_0] = "gcr.io/google-appengine/aspnetcore:1.0", - [KnownProjectTypes.NetCoreWebApplication1_1] = "gcr.io/google-appengine/aspnetcore:1.1", - [KnownProjectTypes.NetCoreWebApplication2_0] = "gcr.io/google-appengine/aspnetcore:2.0" - }; + private const string RuntimeImageFormat = "gcr.io/google-appengine/aspnetcore:{0}"; /// /// This template is the smallest possible Dockerfile needed to deploy an ASP.NET Core app to @@ -63,12 +56,12 @@ internal static async Task CreateAppBundleAsync( IToolsPathProvider pathsProvider, Action outputAction) { - var arguments = $"publish -o \"{stageDirectory}\" -c Release"; - var externalTools = pathsProvider.GetExternalToolsPath(); - var workingDir = project.DirectoryPath; + string arguments = $"publish -o \"{stageDirectory}\" -c Release"; + string externalTools = pathsProvider.GetExternalToolsPath(); + string workingDir = project.DirectoryPath; var env = new Dictionary { - { "PATH", $"{Environment.GetEnvironmentVariable("PATH")};{externalTools}" }, + { "PATH", $"{Environment.GetEnvironmentVariable("PATH")};{externalTools}" } }; Debug.WriteLine($"Using tools from {externalTools}"); @@ -93,10 +86,10 @@ internal static async Task CreateAppBundleAsync( /// The directory where to save the Dockerfile. internal static void CopyOrCreateDockerfile(IParsedProject project, string stageDirectory) { - var sourceDockerfile = Path.Combine(project.DirectoryPath, DockerfileName); - var targetDockerfile = Path.Combine(stageDirectory, DockerfileName); - var entryPointName = CommonUtils.GetEntrypointName(stageDirectory) ?? project.Name; - var baseImage = s_knownRuntimeImages[project.ProjectType]; + string sourceDockerfile = Path.Combine(project.DirectoryPath, DockerfileName); + string targetDockerfile = Path.Combine(stageDirectory, DockerfileName); + string entryPointName = CommonUtils.GetEntrypointName(stageDirectory) ?? project.Name; + string baseImage = string.Format(RuntimeImageFormat, project.FrameworkVersion); if (File.Exists(sourceDockerfile)) { @@ -104,7 +97,7 @@ internal static void CopyOrCreateDockerfile(IParsedProject project, string stage } else { - var content = String.Format(DockerfileDefaultContent, baseImage, entryPointName); + string content = string.Format(DockerfileDefaultContent, baseImage, entryPointName); File.WriteAllText(targetDockerfile, content); } } @@ -115,9 +108,9 @@ internal static void CopyOrCreateDockerfile(IParsedProject project, string stage /// The project. internal static void GenerateDockerfile(IParsedProject project) { - var targetDockerfile = Path.Combine(project.DirectoryPath, DockerfileName); - var baseImage = s_knownRuntimeImages[project.ProjectType]; - var content = String.Format(DockerfileDefaultContent, baseImage, project.Name); + string targetDockerfile = Path.Combine(project.DirectoryPath, DockerfileName); + string baseImage = string.Format(RuntimeImageFormat, project.FrameworkVersion); + string content = string.Format(DockerfileDefaultContent, baseImage, project.Name); File.WriteAllText(targetDockerfile, content); } @@ -128,7 +121,7 @@ internal static void GenerateDockerfile(IParsedProject project) /// True if the Dockerfile exists, false otherwise. internal static bool CheckDockerfile(IParsedProject project) { - var targetDockerfile = Path.Combine(project.DirectoryPath, DockerfileName); + string targetDockerfile = Path.Combine(project.DirectoryPath, DockerfileName); return File.Exists(targetDockerfile); } } diff --git a/GoogleCloudExtension/GoogleCloudExtension.TemplateWizards/DelegatingTemplateWizard.cs b/GoogleCloudExtension/GoogleCloudExtension.TemplateWizards/DelegatingTemplateWizard.cs index 73ea4297d..53cfa4d00 100644 --- a/GoogleCloudExtension/GoogleCloudExtension.TemplateWizards/DelegatingTemplateWizard.cs +++ b/GoogleCloudExtension/GoogleCloudExtension.TemplateWizards/DelegatingTemplateWizard.cs @@ -19,7 +19,6 @@ using Microsoft.VisualStudio.TemplateWizard; using System.Collections.Generic; using System.ComponentModel.Composition; -using System.ComponentModel.Composition.Hosting; namespace GoogleCloudExtension.TemplateWizards { @@ -41,10 +40,7 @@ public void RunStarted( { var provider = (IServiceProvider)automationObject; var model = (IComponentModel)provider.QueryService(); - using (var container = new CompositionContainer(model.DefaultExportProvider)) - { - container.ComposeParts(this); - } + _wizard = model.DefaultExportProvider.GetExportedValue(); _wizard.RunStarted(automationObject, replacementsDictionary, runKind, customParams); } diff --git a/GoogleCloudExtension/GoogleCloudExtension/GenerateConfigurationCommand/GenerateConfigurationContextMenuCommand.cs b/GoogleCloudExtension/GoogleCloudExtension/GenerateConfigurationCommand/GenerateConfigurationContextMenuCommand.cs index 56ac950d5..81f31e4cf 100644 --- a/GoogleCloudExtension/GoogleCloudExtension/GenerateConfigurationCommand/GenerateConfigurationContextMenuCommand.cs +++ b/GoogleCloudExtension/GoogleCloudExtension/GenerateConfigurationCommand/GenerateConfigurationContextMenuCommand.cs @@ -160,8 +160,8 @@ private void OnBeforeQueryStatus(object sender, EventArgs e) } // Ensure that the menu entry is only available for ASP.NET Core projects. - var selectedProject = SolutionHelper.CurrentSolution.SelectedProject?.ParsedProject; - if (selectedProject == null || !selectedProject.IsAspNetCoreProject()) + IParsedProject selectedProject = SolutionHelper.CurrentSolution.SelectedProject?.ParsedProject; + if (selectedProject?.ProjectType != KnownProjectTypes.NetCoreWebApplication) { menuCommand.Visible = false; } diff --git a/GoogleCloudExtension/GoogleCloudExtension/Projects/DotNet4/CsprojProject.cs b/GoogleCloudExtension/GoogleCloudExtension/Projects/DotNet4/CsprojProject.cs index 51d5ce6fa..c6e51c284 100644 --- a/GoogleCloudExtension/GoogleCloudExtension/Projects/DotNet4/CsprojProject.cs +++ b/GoogleCloudExtension/GoogleCloudExtension/Projects/DotNet4/CsprojProject.cs @@ -15,6 +15,7 @@ using EnvDTE; using GoogleCloudExtension.Deployment; using System.IO; +using System.Text.RegularExpressions; namespace GoogleCloudExtension.Projects.DotNet4 { @@ -23,6 +24,7 @@ namespace GoogleCloudExtension.Projects.DotNet4 /// internal class CsprojProject : IParsedProject { + private static readonly Regex s_frameworkVersionRegex = new Regex("(?<=Version=v)[\\d.]+"); private readonly Project _project; #region IParsedProject @@ -35,11 +37,16 @@ internal class CsprojProject : IParsedProject public KnownProjectTypes ProjectType => KnownProjectTypes.WebApplication; + /// The version of the framework used by the project. + public string FrameworkVersion { get; } + #endregion public CsprojProject(Project project) { _project = project; + string targetFramework = project.Properties.Item("TargetFrameworkMoniker").Value.ToString(); + FrameworkVersion = s_frameworkVersionRegex.Match(targetFramework).Value; } } } \ No newline at end of file diff --git a/GoogleCloudExtension/GoogleCloudExtension/Projects/DotNetCore/CsprojProject.cs b/GoogleCloudExtension/GoogleCloudExtension/Projects/DotNetCore/CsprojProject.cs index 0ee63c8ec..956762e66 100644 --- a/GoogleCloudExtension/GoogleCloudExtension/Projects/DotNetCore/CsprojProject.cs +++ b/GoogleCloudExtension/GoogleCloudExtension/Projects/DotNetCore/CsprojProject.cs @@ -14,8 +14,8 @@ using EnvDTE; using GoogleCloudExtension.Deployment; -using GoogleCloudExtension.Utils; using System.IO; +using System.Text.RegularExpressions; namespace GoogleCloudExtension.Projects.DotNetCore { @@ -24,6 +24,7 @@ namespace GoogleCloudExtension.Projects.DotNetCore /// internal class CsprojProject : IParsedProject { + private static readonly Regex s_frameworkVersionRegex = new Regex(@"(?<=^netcoreapp)[\d.]+$"); private readonly Project _project; #region IParsedProject @@ -34,34 +35,17 @@ internal class CsprojProject : IParsedProject public string Name => _project.Name; - public KnownProjectTypes ProjectType { get; } + public KnownProjectTypes ProjectType => KnownProjectTypes.NetCoreWebApplication; + + /// The version of the framework used by the project. + public string FrameworkVersion { get; } #endregion public CsprojProject(Project project, string targetFramework) { - GcpOutputWindow.OutputDebugLine($"Found project {project.FullName} targeting {targetFramework}"); - _project = project; - switch (targetFramework) - { - case "netcoreapp1.0": - ProjectType = KnownProjectTypes.NetCoreWebApplication1_0; - break; - - case "netcoreapp1.1": - ProjectType = KnownProjectTypes.NetCoreWebApplication1_1; - break; - - case "netcoreapp2.0": - ProjectType = KnownProjectTypes.NetCoreWebApplication2_0; - break; - - default: - GcpOutputWindow.OutputDebugLine($"Unsopported target framework {targetFramework}"); - ProjectType = KnownProjectTypes.None; - break; - } + FrameworkVersion = s_frameworkVersionRegex.Match(targetFramework).Value; } } } diff --git a/GoogleCloudExtension/GoogleCloudExtension/Projects/DotNetCore/JsonProject.cs b/GoogleCloudExtension/GoogleCloudExtension/Projects/DotNetCore/JsonProject.cs index 1e5c00cd6..d3fe15b6d 100644 --- a/GoogleCloudExtension/GoogleCloudExtension/Projects/DotNetCore/JsonProject.cs +++ b/GoogleCloudExtension/GoogleCloudExtension/Projects/DotNetCore/JsonProject.cs @@ -32,7 +32,10 @@ internal class JsonProject : IParsedProject public string Name => Path.GetFileName(Path.GetDirectoryName(_projectJsonPath)); - public KnownProjectTypes ProjectType => KnownProjectTypes.NetCoreWebApplication1_0; + public KnownProjectTypes ProjectType => KnownProjectTypes.NetCoreWebApplication; + + /// The version of the framework used by the project. + public string FrameworkVersion { get; } = "1.0.0-preview"; #endregion diff --git a/GoogleCloudExtension/GoogleCloudExtension/Properties/AssemblyInfo.cs b/GoogleCloudExtension/GoogleCloudExtension/Properties/AssemblyInfo.cs index 3f6d64039..d6de050f2 100644 --- a/GoogleCloudExtension/GoogleCloudExtension/Properties/AssemblyInfo.cs +++ b/GoogleCloudExtension/GoogleCloudExtension/Properties/AssemblyInfo.cs @@ -35,8 +35,8 @@ // This version number matches the version in the .vsixmanifest. Please update both versions at the // same time. -[assembly: AssemblyVersion("1.3.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyVersion("1.4.0.0")] +[assembly: AssemblyFileVersion("1.4.0.0")] [assembly: InternalsVisibleTo( "GoogleCloudExtensionUnitTests," + "PublicKey=0024000004800000940000000602000000240000525341310004000001000" + diff --git a/GoogleCloudExtension/GoogleCloudExtension/PublishDialog/PublishDialogWindow.cs b/GoogleCloudExtension/GoogleCloudExtension/PublishDialog/PublishDialogWindow.cs index 553867fba..eaea7d10b 100644 --- a/GoogleCloudExtension/GoogleCloudExtension/PublishDialog/PublishDialogWindow.cs +++ b/GoogleCloudExtension/GoogleCloudExtension/PublishDialog/PublishDialogWindow.cs @@ -15,7 +15,6 @@ using GoogleCloudExtension.Deployment; using GoogleCloudExtension.PublishDialogSteps.ChoiceStep; using GoogleCloudExtension.Theming; -using System; namespace GoogleCloudExtension.PublishDialog { @@ -54,9 +53,7 @@ public static bool CanPublish(IParsedProject project) { var projectType = project.ProjectType; return projectType == KnownProjectTypes.WebApplication || - projectType == KnownProjectTypes.NetCoreWebApplication1_0 || - projectType == KnownProjectTypes.NetCoreWebApplication1_1 || - projectType == KnownProjectTypes.NetCoreWebApplication2_0; + projectType == KnownProjectTypes.NetCoreWebApplication; } } } diff --git a/GoogleCloudExtension/GoogleCloudExtension/PublishDialogSteps/ChoiceStep/ChoiceStepViewModel.cs b/GoogleCloudExtension/GoogleCloudExtension/PublishDialogSteps/ChoiceStep/ChoiceStepViewModel.cs index 6514e4a87..74e13d2c7 100644 --- a/GoogleCloudExtension/GoogleCloudExtension/PublishDialogSteps/ChoiceStep/ChoiceStepViewModel.cs +++ b/GoogleCloudExtension/GoogleCloudExtension/PublishDialogSteps/ChoiceStep/ChoiceStepViewModel.cs @@ -100,7 +100,7 @@ private IEnumerable GetChoicesForCurrentProject() Name = Resources.PublishDialogChoiceStepAppEngineFlexName, Command = new ProtectedCommand( OnAppEngineChoiceCommand, - canExecuteCommand: PublishDialog.Project.IsAspNetCoreProject()), + PublishDialog.Project.ProjectType == KnownProjectTypes.NetCoreWebApplication), Icon = s_appEngineIcon.Value, ToolTip = Resources.PublishDialogChoiceStepAppEngineToolTip }, @@ -109,7 +109,7 @@ private IEnumerable GetChoicesForCurrentProject() Name = Resources.PublishDialogChoiceStepGkeName, Command = new ProtectedCommand( OnGkeChoiceCommand, - canExecuteCommand: PublishDialog.Project.IsAspNetCoreProject()), + PublishDialog.Project.ProjectType == KnownProjectTypes.NetCoreWebApplication), Icon = s_gkeIcon.Value, ToolTip = Resources.PublishDialogChoiceStepGkeToolTip }, @@ -118,7 +118,7 @@ private IEnumerable GetChoicesForCurrentProject() Name = Resources.PublishDialogChoiceStepGceName, Command = new ProtectedCommand( OnGceChoiceCommand, - canExecuteCommand: projectType == KnownProjectTypes.WebApplication), + projectType == KnownProjectTypes.WebApplication), Icon = s_gceIcon.Value, ToolTip = Resources.PublishDialogChoiceStepGceToolTip }, diff --git a/GoogleCloudExtension/GoogleCloudExtension/TemplateWizards/Dialogs/TemplateChooserDialog/AspNetVersion.cs b/GoogleCloudExtension/GoogleCloudExtension/TemplateWizards/Dialogs/TemplateChooserDialog/AspNetVersion.cs index 7096087bf..fa3b4eb91 100644 --- a/GoogleCloudExtension/GoogleCloudExtension/TemplateWizards/Dialogs/TemplateChooserDialog/AspNetVersion.cs +++ b/GoogleCloudExtension/GoogleCloudExtension/TemplateWizards/Dialogs/TemplateChooserDialog/AspNetVersion.cs @@ -45,13 +45,19 @@ public class AspNetVersion /// public static readonly AspNetVersion AspNetCore20 = new AspNetVersion("2.0"); + /// + /// ASP.NET Core 2.1. + /// + public static readonly AspNetVersion AspNetCore21 = new AspNetVersion("2.1"); + /// /// ASP.NET 4. /// public static readonly AspNetVersion AspNet4 = new AspNetVersion("4", false); - private static readonly Version s_sdkVersion1_0 = new Version(1, 0); - private static readonly Version s_sdkVersion2_0 = new Version(2, 0); + // Find these values at https://www.microsoft.com/net/download/all + internal static readonly Version s_firstSdkVersionWith11Runtime = new Version(1, 1, 4); + internal static readonly Version s_firstSdkVersionWith21Runtime = new Version(2, 1, 300); /// /// The version number of ASP.NET. This corresponds to the version of .NET Core used as well. @@ -99,19 +105,30 @@ private static IList GetVs2015AspNetCoreVersions() /// private static IList GetVs2017AspNetCoreVersions() { - List sdkVersions = GetParsedSdkVersions().ToList(); + ILookup sdkVersions = GetParsedSdkVersions().ToLookup(v => v.Major); + List majorVersion1Versions = sdkVersions[1].ToList(); + List majorVersion2Versions = sdkVersions[2].ToList(); var aspNetVersions = new List(); - if (sdkVersions.Any(version => version >= s_sdkVersion1_0 && version < s_sdkVersion2_0)) + if (majorVersion1Versions.Count > 0) { aspNetVersions.Add(AspNetCore10); + } + + if (majorVersion1Versions.Any(version => version >= s_firstSdkVersionWith11Runtime)) + { aspNetVersions.Add(AspNetCore11); } - if (sdkVersions.Any(version => version >= s_sdkVersion2_0)) + if (majorVersion2Versions.Count > 0) { aspNetVersions.Add(AspNetCore20); } + if (majorVersion2Versions.Any(version => version >= s_firstSdkVersionWith21Runtime)) + { + aspNetVersions.Add(AspNetCore21); + } + return aspNetVersions; } diff --git a/GoogleCloudExtension/GoogleCloudExtension/TemplateWizards/GoogleProjectTemplateSelectorWizard.cs b/GoogleCloudExtension/GoogleCloudExtension/TemplateWizards/GoogleProjectTemplateSelectorWizard.cs index ddd625b7f..bdb55170e 100644 --- a/GoogleCloudExtension/GoogleCloudExtension/TemplateWizards/GoogleProjectTemplateSelectorWizard.cs +++ b/GoogleCloudExtension/GoogleCloudExtension/TemplateWizards/GoogleProjectTemplateSelectorWizard.cs @@ -26,7 +26,7 @@ using System.IO; using System.Linq; using System.Runtime.InteropServices; -using System.Text.RegularExpressions; +using System.Xml.Linq; using IServiceProvider = Microsoft.VisualStudio.OLE.Interop.IServiceProvider; namespace GoogleCloudExtension.TemplateWizards @@ -37,10 +37,10 @@ namespace GoogleCloudExtension.TemplateWizards [Export(typeof(IGoogleProjectTemplateSelectorWizard))] public class GoogleProjectTemplateSelectorWizard : IGoogleProjectTemplateSelectorWizard { - // Find the AspNet or AspNetCore part of c:\path\to\template\Gcp.AspNet.vstemplate - private static readonly Regex s_templateTypeRegex = new Regex(@"(?<=Gcp\.)[^.]*(?=\.vstemplate$)"); // Mockable static methods for unit testing. - internal Func PromptUser = TemplateChooserWindow.PromptUser; + internal Func PromptUser = + TemplateChooserWindow.PromptUser; + internal Action> CleanupDirectories = GoogleTemplateWizardHelper.CleanupDirectories; /// @@ -64,7 +64,7 @@ public void RunStarted( } else { - TemplateType templateType = GetTemplateTypeFromPath(thisTemplatePath); + TemplateType templateType = GetTemplateTypeFromWizardData(replacements); result = PromptUser(projectName, templateType); } if (result == null) @@ -73,7 +73,7 @@ public void RunStarted( } string thisTemplateDirectory = - Path.GetFullPath(Path.Combine(thisTemplatePath, "..", "..", "..", "..")); + Path.GetFullPath(Path.Combine(thisTemplatePath, "..", "..", "..", "..")); var serviceProvider = automationObject as IServiceProvider; @@ -97,52 +97,57 @@ public void RunStarted( CleanupDirectories(replacements); throw; } + // Delegated wizard created the solution. Cancel repeated creation of the solution. throw new WizardCancelledException(); } - private TemplateType GetTemplateTypeFromPath(string thisTemplatePath) + private TemplateType GetTemplateTypeFromWizardData(IReadOnlyDictionary replacements) { - string templateTypeName = s_templateTypeRegex.Match(thisTemplatePath).Value; - TemplateType result; - if (Enum.TryParse(templateTypeName, out result)) - { - return result; - } - else + string wizardDataXml; + if (replacements.TryGetValue(ReplacementsKeys.WizardDataKey, out wizardDataXml)) { - return TemplateType.AspNetCore; + XElement wizardData = XElement.Parse(wizardDataXml); + string templateTypeName = wizardData.DescendantsAndSelf() + .SingleOrDefault(n => n.Name.LocalName == "TemplateType")?.Value; + TemplateType result; + if (Enum.TryParse(templateTypeName, out result)) + { + return result; + } } + + return TemplateType.AspNetCore; } /// public void ProjectFinishedGenerating(Project project) { - throw new NotImplementedException("This wizard should delegate to another template and cancel itself."); + throw new NotSupportedException("This wizard should delegate to another template and cancel itself."); } /// public void ProjectItemFinishedGenerating(ProjectItem projectItem) { - throw new NotImplementedException("This wizard should delegate to another template and cancel itself."); + throw new NotSupportedException("This wizard should delegate to another template and cancel itself."); } /// public bool ShouldAddProjectItem(string filePath) { - throw new NotImplementedException("This wizard should delegate to another template and cancel itself."); + throw new NotSupportedException("This wizard should delegate to another template and cancel itself."); } /// public void BeforeOpeningFile(ProjectItem projectItem) { - throw new NotImplementedException("This wizard should delegate to another template and cancel itself."); + throw new NotSupportedException("This wizard should delegate to another template and cancel itself."); } /// public void RunFinished() { - throw new NotImplementedException("This wizard should delegate to another wizard and cancel itself."); + throw new NotSupportedException("This wizard should delegate to another wizard and cancel itself."); } private static object[] GetNewCustomParams( @@ -183,6 +188,7 @@ private static object[] GetNewCustomParams( $"Invalid {nameof(FrameworkType)}: {result.SelectedFramework}"); } } + return customParams.Cast().Concat(additionalCustomParams).ToArray(); } } diff --git a/GoogleCloudExtension/GoogleCloudExtension/TemplateWizards/ReplacementsKeys.cs b/GoogleCloudExtension/GoogleCloudExtension/TemplateWizards/ReplacementsKeys.cs index c6ce5c44a..d0c8c1d90 100644 --- a/GoogleCloudExtension/GoogleCloudExtension/TemplateWizards/ReplacementsKeys.cs +++ b/GoogleCloudExtension/GoogleCloudExtension/TemplateWizards/ReplacementsKeys.cs @@ -28,5 +28,6 @@ public static class ReplacementsKeys public const string TemplateChooserResultKey = "$templateChooserResult$"; public const string SafeProjectNameKey = "$safeprojectname$"; public const string EmbeddableSafeProjectNameKey = "_safe_project_name_"; + public const string WizardDataKey = "$wizarddata$"; } } \ No newline at end of file diff --git a/GoogleCloudExtension/GoogleCloudExtension/source.extension.vsixmanifest b/GoogleCloudExtension/GoogleCloudExtension/source.extension.vsixmanifest index 6ababd05b..8b2533744 100644 --- a/GoogleCloudExtension/GoogleCloudExtension/source.extension.vsixmanifest +++ b/GoogleCloudExtension/GoogleCloudExtension/source.extension.vsixmanifest @@ -5,7 +5,7 @@ The Version attribute of the Identity element *must* match the version number in Properties\AssemblyInfo.cs, to ensure accurate metrics. --> - + Google Cloud Tools for Visual Studio Tools to develop applications for Google Cloud Platform. https://cloud.google.com/visual-studio/ diff --git a/GoogleCloudExtension/GoogleCloudExtensionUnitTests/GoogleCloudExtensionUnitTests.csproj b/GoogleCloudExtension/GoogleCloudExtensionUnitTests/GoogleCloudExtensionUnitTests.csproj index 0aaa658ca..0eb8b5118 100644 --- a/GoogleCloudExtension/GoogleCloudExtensionUnitTests/GoogleCloudExtensionUnitTests.csproj +++ b/GoogleCloudExtension/GoogleCloudExtensionUnitTests/GoogleCloudExtensionUnitTests.csproj @@ -258,12 +258,16 @@ + + + + @@ -279,6 +283,7 @@ + diff --git a/GoogleCloudExtension/GoogleCloudExtensionUnitTests/Projects/DotNet4/CsprojProjectTests.cs b/GoogleCloudExtension/GoogleCloudExtensionUnitTests/Projects/DotNet4/CsprojProjectTests.cs new file mode 100644 index 000000000..865135ad4 --- /dev/null +++ b/GoogleCloudExtension/GoogleCloudExtensionUnitTests/Projects/DotNet4/CsprojProjectTests.cs @@ -0,0 +1,36 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using EnvDTE; +using GoogleCloudExtension.Projects.DotNet4; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; + +namespace GoogleCloudExtensionUnitTests.Projects.DotNet4 +{ + [TestClass] + public class CsprojProjectTests + { + [TestMethod] + public void TestConstructor_SetsFrameworkVersion() + { + var mockedProject = Mock.Of( + p => p.Properties.Item("TargetFrameworkMoniker").Value.ToString() == + ".NETFramework,Version=v4.4.4"); + var objectUnderTest = new CsprojProject(mockedProject); + + Assert.AreEqual("4.4.4", objectUnderTest.FrameworkVersion); + } + } +} diff --git a/GoogleCloudExtension/GoogleCloudExtensionUnitTests/Projects/DotNetCore/CsprojProjectTests.cs b/GoogleCloudExtension/GoogleCloudExtensionUnitTests/Projects/DotNetCore/CsprojProjectTests.cs new file mode 100644 index 000000000..4a4adb0ac --- /dev/null +++ b/GoogleCloudExtension/GoogleCloudExtensionUnitTests/Projects/DotNetCore/CsprojProjectTests.cs @@ -0,0 +1,42 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using EnvDTE; +using GoogleCloudExtension.Deployment; +using GoogleCloudExtension.Projects.DotNetCore; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; + +namespace GoogleCloudExtensionUnitTests.Projects.DotNetCore +{ + [TestClass] + public class CsprojProjectTests + { + [TestMethod] + public void TestConstructor_SetsFrameworkVersion() + { + var objectUnderTest = new CsprojProject(Mock.Of(), "netcoreapp1.7"); + + Assert.AreEqual("1.7", objectUnderTest.FrameworkVersion); + } + + [TestMethod] + public void TestProjectType_IsNetCore() + { + var objectUnderTest = new CsprojProject(Mock.Of(), "netcoreapp1.7"); + + Assert.AreEqual(KnownProjectTypes.NetCoreWebApplication, objectUnderTest.ProjectType); + } + } +} diff --git a/GoogleCloudExtension/GoogleCloudExtensionUnitTests/Projects/DotNetCore/JsonProjectTests.cs b/GoogleCloudExtension/GoogleCloudExtensionUnitTests/Projects/DotNetCore/JsonProjectTests.cs new file mode 100644 index 000000000..84381f7ec --- /dev/null +++ b/GoogleCloudExtension/GoogleCloudExtensionUnitTests/Projects/DotNetCore/JsonProjectTests.cs @@ -0,0 +1,32 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using GoogleCloudExtension.Deployment; +using GoogleCloudExtension.Projects.DotNetCore; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace GoogleCloudExtensionUnitTests.Projects.DotNetCore +{ + [TestClass] + public class JsonProjectTests + { + [TestMethod] + public void TestProjectType_IsNetCore() + { + var objectUnderTest = new JsonProject(""); + + Assert.AreEqual(KnownProjectTypes.NetCoreWebApplication, objectUnderTest.ProjectType); + } + } +} diff --git a/GoogleCloudExtension/GoogleCloudExtensionUnitTests/PublishDialog/PublishDialogWindowTests.cs b/GoogleCloudExtension/GoogleCloudExtensionUnitTests/PublishDialog/PublishDialogWindowTests.cs new file mode 100644 index 000000000..02848c5d7 --- /dev/null +++ b/GoogleCloudExtension/GoogleCloudExtensionUnitTests/PublishDialog/PublishDialogWindowTests.cs @@ -0,0 +1,47 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using GoogleCloudExtension.Deployment; +using GoogleCloudExtension.PublishDialog; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; + +namespace GoogleCloudExtensionUnitTests.PublishDialog +{ + [TestClass] + public class PublishDialogWindowTests + { + [TestMethod] + [DataRow(KnownProjectTypes.WebApplication)] + [DataRow(KnownProjectTypes.NetCoreWebApplication)] + public void TestCanPublish_True(KnownProjectTypes validKnownProjectType) + { + bool result = PublishDialogWindow.CanPublish( + Mock.Of(p => p.ProjectType == validKnownProjectType)); + + Assert.IsTrue(result); + } + + [TestMethod] + [DataRow(KnownProjectTypes.None)] + [DataRow(3000)] + public void TestCanPublish_False(KnownProjectTypes invalidKnownProjectType) + { + bool result = PublishDialogWindow.CanPublish( + Mock.Of(p => p.ProjectType == invalidKnownProjectType)); + + Assert.IsFalse(result); + } + } +} diff --git a/GoogleCloudExtension/GoogleCloudExtensionUnitTests/PublishDialogSteps/ChoiceStep/ChoiceStepViewModelTests.cs b/GoogleCloudExtension/GoogleCloudExtensionUnitTests/PublishDialogSteps/ChoiceStep/ChoiceStepViewModelTests.cs index bd5762408..b762ad7cf 100644 --- a/GoogleCloudExtension/GoogleCloudExtensionUnitTests/PublishDialogSteps/ChoiceStep/ChoiceStepViewModelTests.cs +++ b/GoogleCloudExtension/GoogleCloudExtensionUnitTests/PublishDialogSteps/ChoiceStep/ChoiceStepViewModelTests.cs @@ -91,7 +91,7 @@ public void TestChoices_WebApplication() [TestMethod] public void TestChoices_DotnetCore() { - _parsedProject.ProjectType = KnownProjectTypes.NetCoreWebApplication1_0; + _parsedProject.ProjectType = KnownProjectTypes.NetCoreWebApplication; _objectUnderTest.OnVisible(_mockedPublishDialog); CollectionAssert.AreEqual( @@ -177,7 +177,7 @@ public void TestOnFlowFinished_RemovesHandlers() [TestMethod] public void TestOnAppEngineChoiceCommand() { - _parsedProject.ProjectType = KnownProjectTypes.NetCoreWebApplication1_0; + _parsedProject.ProjectType = KnownProjectTypes.NetCoreWebApplication; _objectUnderTest.OnVisible(_mockedPublishDialog); _objectUnderTest.Choices.Single(c => c.Name == Resources.PublishDialogChoiceStepAppEngineFlexName).Command @@ -190,7 +190,7 @@ public void TestOnAppEngineChoiceCommand() public void TestOnGkeChoiceCommand() { - _parsedProject.ProjectType = KnownProjectTypes.NetCoreWebApplication1_0; + _parsedProject.ProjectType = KnownProjectTypes.NetCoreWebApplication; _objectUnderTest.OnVisible(_mockedPublishDialog); _objectUnderTest.Choices.Single(c => c.Name == Resources.PublishDialogChoiceStepGkeName).Command diff --git a/GoogleCloudExtension/GoogleCloudExtensionUnitTests/TemplateWizards/Dialogs/AspNetCoreTemplateChooserViewModelTests.cs b/GoogleCloudExtension/GoogleCloudExtensionUnitTests/TemplateWizards/Dialogs/AspNetCoreTemplateChooserViewModelTests.cs index 8198266c7..48e7ee5a9 100644 --- a/GoogleCloudExtension/GoogleCloudExtensionUnitTests/TemplateWizards/Dialogs/AspNetCoreTemplateChooserViewModelTests.cs +++ b/GoogleCloudExtension/GoogleCloudExtensionUnitTests/TemplateWizards/Dialogs/AspNetCoreTemplateChooserViewModelTests.cs @@ -90,7 +90,7 @@ public void TestInitialConditionsVs2015() public void TestInitialConditionsVs2017WithNetCoreSdk10() { PackageMock.Setup(p => p.VsVersion).Returns(VsVersionUtils.VisualStudio2017Version); - _targetSdkVersions.Add("1.0.0"); + _targetSdkVersions.Add(AspNetVersion.s_firstSdkVersionWith11Runtime.ToString()); var objectUnderTest = new AspNetCoreTemplateChooserViewModel(_closeWindowMock.Object); @@ -127,7 +127,7 @@ public void TestInitialConditionsVs2017WithBothNetCoreSdk10And20() { PackageMock.Setup(p => p.VsVersion).Returns(VsVersionUtils.VisualStudio2017Version); _targetSdkVersions.Add("2.0.0"); - _targetSdkVersions.Add("1.0.0"); + _targetSdkVersions.Add(AspNetVersion.s_firstSdkVersionWith11Runtime.ToString()); var objectUnderTest = new AspNetCoreTemplateChooserViewModel(_closeWindowMock.Object); @@ -155,7 +155,7 @@ public void TestSetSelectedVersion() public void TestSetSelectedFrameworkKeepSelectedVersion() { _targetSdkVersions.Add("2.0.0"); - _targetSdkVersions.Add("1.0.0"); + _targetSdkVersions.Add(AspNetVersion.s_firstSdkVersionWith11Runtime.ToString()); var objectUnderTest = new AspNetCoreTemplateChooserViewModel(_closeWindowMock.Object); objectUnderTest.SelectedFramework = FrameworkType.NetFramework; diff --git a/GoogleCloudExtension/GoogleCloudExtensionUnitTests/TemplateWizards/Dialogs/AspNetVersionTests.cs b/GoogleCloudExtension/GoogleCloudExtensionUnitTests/TemplateWizards/Dialogs/AspNetVersionTests.cs new file mode 100644 index 000000000..1d0a42f83 --- /dev/null +++ b/GoogleCloudExtension/GoogleCloudExtensionUnitTests/TemplateWizards/Dialogs/AspNetVersionTests.cs @@ -0,0 +1,113 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using GoogleCloudExtension.Deployment; +using GoogleCloudExtension.TemplateWizards.Dialogs.TemplateChooserDialog; +using GoogleCloudExtension.VsVersion; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace GoogleCloudExtensionUnitTests.TemplateWizards.Dialogs +{ + [TestClass] + public class AspNetVersionTests : ExtensionTestBase + { + private Mock _toolsPathProviderMock; + private Lazy _toolsPathProviderToRestore; + + protected override void BeforeEach() + { + _toolsPathProviderMock = new Mock(); + _toolsPathProviderToRestore = VsVersionUtils.s_toolsPathProvider; + VsVersionUtils.s_toolsPathProvider = new Lazy(() => _toolsPathProviderMock.Object); + } + + protected override void AfterEach() + { + VsVersionUtils.s_toolsPathProvider = _toolsPathProviderToRestore; + } + + [TestMethod] + public void TestGetAvailableAspNetCoreVersions_ForVS2017NetCore10() + { + PackageMock.Setup(p => p.VsVersion).Returns(VsVersionUtils.VisualStudio2017Version); + _toolsPathProviderMock.Setup(p => p.GetNetCoreSdkVersions()).Returns(new[] { "1.0.35" }); + + IList results = AspNetVersion.GetAvailableAspNetCoreVersions(FrameworkType.NetCore); + + CollectionAssert.AreEqual(new[] { AspNetVersion.AspNetCore10 }, results.ToList()); + } + + [TestMethod] + public void TestGetAvailableAspNetCoreVersions_ForVS2017NetCore11() + { + PackageMock.Setup(p => p.VsVersion).Returns(VsVersionUtils.VisualStudio2017Version); + _toolsPathProviderMock.Setup(p => p.GetNetCoreSdkVersions()).Returns( + new[] { "1.0.35", AspNetVersion.s_firstSdkVersionWith11Runtime.ToString() }); + + IList results = AspNetVersion.GetAvailableAspNetCoreVersions(FrameworkType.NetCore); + + CollectionAssert.AreEqual(new[] { AspNetVersion.AspNetCore10, AspNetVersion.AspNetCore11 }, results.ToList()); + } + + [TestMethod] + public void TestGetAvailableAspNetCoreVersions_ForVS2017NetCore20() + { + PackageMock.Setup(p => p.VsVersion).Returns(VsVersionUtils.VisualStudio2017Version); + _toolsPathProviderMock.Setup(p => p.GetNetCoreSdkVersions()).Returns(new[] { "2.0.35" }); + + IList results = AspNetVersion.GetAvailableAspNetCoreVersions(FrameworkType.NetCore); + + CollectionAssert.AreEqual(new[] { AspNetVersion.AspNetCore20 }, results.ToList()); + } + + [TestMethod] + public void TestGetAvailableAspNetCoreVersions_ForVS2017NetCore21() + { + PackageMock.Setup(p => p.VsVersion).Returns(VsVersionUtils.VisualStudio2017Version); + _toolsPathProviderMock.Setup(p => p.GetNetCoreSdkVersions()) + .Returns(new[] { AspNetVersion.s_firstSdkVersionWith21Runtime.ToString() }); + + IList results = AspNetVersion.GetAvailableAspNetCoreVersions(FrameworkType.NetCore); + + CollectionAssert.AreEqual(new[] { AspNetVersion.AspNetCore20, AspNetVersion.AspNetCore21 }, results.ToList()); + } + + [TestMethod] + public void TestGetAvailableAspNetCoreVersions_ForVS2017NetCoreAll() + { + PackageMock.Setup(p => p.VsVersion).Returns(VsVersionUtils.VisualStudio2017Version); + _toolsPathProviderMock.Setup(p => p.GetNetCoreSdkVersions()).Returns( + new[] + { + AspNetVersion.s_firstSdkVersionWith11Runtime.ToString(), + AspNetVersion.s_firstSdkVersionWith21Runtime.ToString() + }); + + IList results = AspNetVersion.GetAvailableAspNetCoreVersions(FrameworkType.NetCore); + + CollectionAssert.AreEqual( + new[] + { + AspNetVersion.AspNetCore10, + AspNetVersion.AspNetCore11, + AspNetVersion.AspNetCore20, + AspNetVersion.AspNetCore21 + }, results.ToList()); + } + } +} diff --git a/GoogleCloudExtension/GoogleCloudExtensionUnitTests/TemplateWizards/GoogleProjectSelectorTemplateWizardTests.cs b/GoogleCloudExtension/GoogleCloudExtensionUnitTests/TemplateWizards/GoogleProjectSelectorTemplateWizardTests.cs index 61c838e2f..1376f7ea3 100644 --- a/GoogleCloudExtension/GoogleCloudExtensionUnitTests/TemplateWizards/GoogleProjectSelectorTemplateWizardTests.cs +++ b/GoogleCloudExtension/GoogleCloudExtensionUnitTests/TemplateWizards/GoogleProjectSelectorTemplateWizardTests.cs @@ -53,6 +53,8 @@ public class GoogleProjectTemplateSelectorWizardTests private const string DefaultProjectName = "DefaultProjectName"; private const string DefaultProjectId = "default-project-id"; private const FrameworkType DefaultFrameworkType = FrameworkType.NetCore; + private const string AspNetWizardData = "AspNet"; + private const string AspNetCoreWizardData = "AspNetCore"; private GoogleProjectTemplateSelectorWizard _objectUnderTest; private Dictionary _replacements; @@ -70,9 +72,10 @@ public void BeforeEach() { _replacements = new Dictionary { - {ReplacementsKeys.ProjectNameKey, DefaultProjectName}, - {ReplacementsKeys.DestinationDirectoryKey, DefaultDestinationDirectory}, - {ReplacementsKeys.SolutionDirectoryKey, DefaultSolutionDirectory } + [ReplacementsKeys.ProjectNameKey] = DefaultProjectName, + [ReplacementsKeys.DestinationDirectoryKey] = DefaultDestinationDirectory, + [ReplacementsKeys.SolutionDirectoryKey] = DefaultSolutionDirectory, + [ReplacementsKeys.WizardDataKey] = AspNetWizardData }; _customParams = new object[] { @@ -115,47 +118,48 @@ private bool AreExpectedCustomParams(IEnumerable customParams) } [TestMethod] - [ExpectedException(typeof(WizardCancelledException))] - public void TestPromptForAspDotNet() + public void TestWizardData_AspNetPromptsForAspDotNet() { const string newProjectName = "AspNetProjectName"; _replacements[ReplacementsKeys.ProjectNameKey] = newProjectName; - try - { - string templateFilePath = DefaultAspDotNetTemplatePath; - _objectUnderTest.RunStarted( - _dteMock.Object, _replacements, WizardRunKind.AsNewProject, - new object[] { templateFilePath }); - } - catch (WizardCancelledException) - { - _promptUserMock.Verify(p => p(newProjectName, TemplateType.AspNet), Times.Once); - throw; - } + _replacements[ReplacementsKeys.WizardDataKey] = AspNetWizardData; + + Assert.ThrowsException( + () => _objectUnderTest.RunStarted( + _dteMock.Object, _replacements, WizardRunKind.AsNewProject, _customParams)); + + _promptUserMock.Verify(p => p(newProjectName, TemplateType.AspNet), Times.Once); } [TestMethod] - [ExpectedException(typeof(WizardCancelledException))] - public void TestPromptForAspDotNetCore() + public void TestWizardData_AspNetCorePromptsForAspDotNetCore() { const string newProjectName = "AspNetCoreProjectName"; _replacements[ReplacementsKeys.ProjectNameKey] = newProjectName; - try - { - string templateFilePath = DefaultAspDotNetCoreTemplatePath; - _objectUnderTest.RunStarted( - _dteMock.Object, _replacements, WizardRunKind.AsNewProject, - new object[] { templateFilePath }); - } - catch (WizardCancelledException) - { - _promptUserMock.Verify(p => p(newProjectName, TemplateType.AspNetCore), Times.Once); - throw; - } + _replacements[ReplacementsKeys.WizardDataKey] = AspNetCoreWizardData; + + Assert.ThrowsException( + () => _objectUnderTest.RunStarted( + _dteMock.Object, _replacements, WizardRunKind.AsNewProject, _customParams)); + + _promptUserMock.Verify(p => p(newProjectName, TemplateType.AspNetCore), Times.Once); + } + + [TestMethod] + public void TestWizardData_MissingPromptsForAspDotNetCore() + { + const string newProjectName = "AspNetCoreProjectName"; + _replacements[ReplacementsKeys.ProjectNameKey] = newProjectName; + _replacements.Remove(ReplacementsKeys.WizardDataKey); + + Assert.ThrowsException( + () => _objectUnderTest.RunStarted( + _dteMock.Object, _replacements, WizardRunKind.AsNewProject, _customParams)); + + _promptUserMock.Verify(p => p(newProjectName, TemplateType.AspNetCore), Times.Once); } [TestMethod] - [ExpectedException(typeof(WizardCancelledException))] public void TestAddTemplateCallWithSameDestinationDirectory() { const string newProjectName = "SameDirectoryProject"; @@ -165,21 +169,18 @@ public void TestAddTemplateCallWithSameDestinationDirectory() _replacements[ReplacementsKeys.ProjectNameKey] = newProjectName; _replacements[ReplacementsKeys.SolutionDirectoryKey] = targetDirectory; _replacements[ReplacementsKeys.DestinationDirectoryKey] = targetDirectory; - try - { - _objectUnderTest.RunStarted(_dteMock.Object, _replacements, WizardRunKind.AsNewProject, _customParams); - } - catch (WizardCancelledException) - { - _expectedCustomParams[ReplacementsKeys.SolutionDirectoryKey] = targetDirectory; - _expectedCustomParams[ReplacementsKeys.PackagesPathKey] = packagesPath; - _solutionMock.Verify( - s => s.AddNewProjectFromTemplate( - It.IsAny(), It.Is(a => AreExpectedCustomParams(a)), It.IsAny(), - targetDirectory, newProjectName, null, out _newHierarchy), - Times.Once); - throw; - } + + Assert.ThrowsException( + () => _objectUnderTest.RunStarted( + _dteMock.Object, _replacements, WizardRunKind.AsNewProject, _customParams)); + + _expectedCustomParams[ReplacementsKeys.SolutionDirectoryKey] = targetDirectory; + _expectedCustomParams[ReplacementsKeys.PackagesPathKey] = packagesPath; + _solutionMock.Verify( + s => s.AddNewProjectFromTemplate( + It.IsAny(), It.Is(a => AreExpectedCustomParams(a)), It.IsAny(), + targetDirectory, newProjectName, null, out _newHierarchy), + Times.Once); } [TestMethod] @@ -383,38 +384,25 @@ public void TestRunStartedUnknownFrameworkType() } [TestMethod] - [ExpectedException(typeof(NotImplementedException))] - public void TestProjectFinishedGenerating() - { - _objectUnderTest.ProjectFinishedGenerating(Mock.Of()); - } + public void TestProjectFinishedGenerating() => + Assert.ThrowsException( + () => _objectUnderTest.ProjectFinishedGenerating(Mock.Of())); [TestMethod] - [ExpectedException(typeof(NotImplementedException))] - public void TestProjectItemFinishedGenerating() - { - _objectUnderTest.ProjectItemFinishedGenerating(Mock.Of()); - } + public void TestProjectItemFinishedGenerating() => Assert.ThrowsException( + () => _objectUnderTest.ProjectItemFinishedGenerating(Mock.Of())); [TestMethod] - [ExpectedException(typeof(NotImplementedException))] - public void TestShouldAddProjectItem() - { - _objectUnderTest.ShouldAddProjectItem(""); - } + public void TestShouldAddProjectItem() => + Assert.ThrowsException(() => _objectUnderTest.ShouldAddProjectItem("")); [TestMethod] - [ExpectedException(typeof(NotImplementedException))] - public void TestBeforeOpeningFile() - { - _objectUnderTest.BeforeOpeningFile(Mock.Of()); - } + public void TestBeforeOpeningFile() => + Assert.ThrowsException( + () => _objectUnderTest.BeforeOpeningFile(Mock.Of())); [TestMethod] - [ExpectedException(typeof(NotImplementedException))] - public void TestRunFinished() - { - _objectUnderTest.RunFinished(); - } + public void TestRunFinished() => + Assert.ThrowsException(() => _objectUnderTest.RunFinished()); } } diff --git a/GoogleCloudExtension/ProjectTemplates/GcpProjectTemplate/Core/MVC/2.1.csproj b/GoogleCloudExtension/ProjectTemplates/GcpProjectTemplate/Core/MVC/2.1.csproj new file mode 100644 index 000000000..258717e3c --- /dev/null +++ b/GoogleCloudExtension/ProjectTemplates/GcpProjectTemplate/Core/MVC/2.1.csproj @@ -0,0 +1,31 @@ + + + + netcoreapp2.1 + true + _safe_project_name_ + _safe_project_name_ + + + + + PreserveNewest + + + + + + + + + + + + + + + + + + + diff --git a/GoogleCloudExtension/ProjectTemplates/GcpProjectTemplate/Core/MVC/2.1.vstemplate b/GoogleCloudExtension/ProjectTemplates/GcpProjectTemplate/Core/MVC/2.1.vstemplate new file mode 100644 index 000000000..6eaff0ede --- /dev/null +++ b/GoogleCloudExtension/ProjectTemplates/GcpProjectTemplate/Core/MVC/2.1.vstemplate @@ -0,0 +1,71 @@ + + + Google ASP.NET Core MVC + ASP.NET Core MVC template for Google Cloud + CSharp + Web + 1000 + true + GoogleAspNetCoreMvc + true + Enabled + true + __TemplateIcon.png + true + true + 1 + true + + + + + launchSettings.json + + + + site.css + site.min.css + + + banner1.svg + banner2.svg + banner3.svg + banner4.svg + + + site.js + site.min.js + + favicon.ico + + + HomeController.cs + + + + About.cshtml + Contact.cshtml + Index.cshtml + + + _Layout.cshtml + Error.cshtml + + _ViewImports.cshtml + _ViewStart.cshtml + + appsettings.json + bower.json + .bowerrc + bundleconfig.json + Program.cs + Project_Readme.html + Startup.2.1.cs + web.config + + + + GoogleCloudExtension.TemplateWizards, Version=1.0.0.0, Culture=neutral, PublicKeyToken=919919421783e38f + GoogleCloudExtension.TemplateWizards.GoogleProjectTemplateWizard + + \ No newline at end of file diff --git a/GoogleCloudExtension/ProjectTemplates/GcpProjectTemplate/Core/MVC/Startup.2.1.cs b/GoogleCloudExtension/ProjectTemplates/GcpProjectTemplate/Core/MVC/Startup.2.1.cs new file mode 100644 index 000000000..a5a0cc983 --- /dev/null +++ b/GoogleCloudExtension/ProjectTemplates/GcpProjectTemplate/Core/MVC/Startup.2.1.cs @@ -0,0 +1,151 @@ +using Google.Cloud.Diagnostics.AspNetCore; +using System; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace _safe_project_name_ +{ + public class Startup + { + public IConfigurationRoot Configuration { get; } + + private readonly Lazy _projectIdLazy; + private string ProjectId => _projectIdLazy.Value ?? ""; + private bool HasProjectId => _projectIdLazy.Value != null; + + public Startup(IHostingEnvironment env) + { + _projectIdLazy = new Lazy(GetProjectId); + var builder = new ConfigurationBuilder() + .SetBasePath(env.ContentRootPath) + .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) + .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) + .AddEnvironmentVariables(); + Configuration = builder.Build(); + } + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + // Add framework services.Microsoft.VisualStudio.ExtensionManager.ExtensionManagerService + services.AddMvc(); + + if (HasProjectId) + { + // Enables Stackdriver Trace. + services.AddGoogleTrace(options => options.ProjectId = ProjectId); + // Sends Exceptions to Stackdriver Error Reporting. + services.AddGoogleExceptionLogging( + options => + { + options.ProjectId = ProjectId; + options.ServiceName = GetServiceName(); + options.Version = GetVersion(); + }); + } + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) + { + + if (env.IsDevelopment()) + { + // Only use Console and Debug logging during development. + loggerFactory.AddConsole(Configuration.GetSection("Logging")); + loggerFactory.AddDebug(); + + app.UseDeveloperExceptionPage(); + } + else + { + app.UseExceptionHandler("/Home/Error"); + if (HasProjectId) + { + // Send logs to Stackdriver Logging. + loggerFactory.AddGoogle(ProjectId); + // Sends logs to Stackdriver Error Reporting. + app.UseGoogleExceptionLogging(); + // Sends logs to Stackdriver Trace. + app.UseGoogleTrace(); + + var startupLogger = loggerFactory.CreateLogger(); + startupLogger.LogInformation( + "Stackdriver Logging enabled: https://console.cloud.google.com/logs/"); + startupLogger.LogInformation( + "Stackdriver Error Reporting enabled: https://console.cloud.google.com/errors/"); + startupLogger.LogInformation( + "Stackdriver Trace not enabled: https://console.cloud.google.com/traces/"); + } + else + { + var startupLogger = loggerFactory.CreateLogger(); + startupLogger.LogWarning( + "Stackdriver Logging not enabled. Missing Google:ProjectId in configuration."); + startupLogger.LogWarning( + "Stackdriver Error Reporting not enabled. Missing Google:ProjectId in configuration."); + startupLogger.LogWarning( + "Stackdriver Trace not enabled. Missing Google:ProjectId in configuration."); + } + } + + app.UseStaticFiles(); + + app.UseMvc(routes => + { + routes.MapRoute( + name: "default", + template: "{controller=Home}/{action=Index}/{id?}"); + }); + } + + private string GetProjectId() + { + var instance = Google.Api.Gax.Platform.Instance(); + var projectId = instance?.ProjectId ?? Configuration["Google:ProjectId"]; + if (string.IsNullOrEmpty(projectId)) + { + // Set Google:ProjectId in appsettings.json to enable stackdriver logging outside of GCP. + return null; + } + return projectId; + } + + private string GetServiceName() + { + var instance = Google.Api.Gax.Platform.Instance(); + // An identifier of the service. See https://cloud.google.com/error-reporting/docs/formatting-error-messages#FIELDS.service. + var serviceName = + instance?.GaeDetails?.ServiceId ?? + Configuration["Google:ErrorReporting:ServiceName"]; + if (string.IsNullOrEmpty(serviceName)) + { + throw new Exception( + "The error reporting library needs a service name. " + + "Update appsettings.json by setting the Google:ErrorReporting:ServiceName property with your " + + "Service Id, then recompile."); + } + return serviceName; + } + + private string GetVersion() + { + var instance = Google.Api.Gax.Platform.Instance(); + // The source version of the service. See https://cloud.google.com/error-reporting/docs/formatting-error-messages#FIELDS.version. + var versionId = + instance?.GaeDetails?.VersionId ?? + Configuration["Google:ErrorReporting:Version"]; + if (string.IsNullOrEmpty(versionId)) + { + throw new Exception( + "The error reporting library needs a version id. " + + "Update appsettings.json by setting the Google:ErrorReporting:Version property with your " + + "service version id, then recompile."); + } + return versionId; + } + } +} diff --git a/GoogleCloudExtension/ProjectTemplates/GcpProjectTemplate/Core/WebAPI/2.1.csproj b/GoogleCloudExtension/ProjectTemplates/GcpProjectTemplate/Core/WebAPI/2.1.csproj new file mode 100644 index 000000000..a763544e0 --- /dev/null +++ b/GoogleCloudExtension/ProjectTemplates/GcpProjectTemplate/Core/WebAPI/2.1.csproj @@ -0,0 +1,22 @@ + + + + netcoreapp2.1 + true + _safe_project_name_ + _safe_project_name_ + + + + + PreserveNewest + + + + + + + + + + diff --git a/GoogleCloudExtension/ProjectTemplates/GcpProjectTemplate/Core/WebAPI/2.1.vstemplate b/GoogleCloudExtension/ProjectTemplates/GcpProjectTemplate/Core/WebAPI/2.1.vstemplate new file mode 100644 index 000000000..08847be02 --- /dev/null +++ b/GoogleCloudExtension/ProjectTemplates/GcpProjectTemplate/Core/WebAPI/2.1.vstemplate @@ -0,0 +1,39 @@ + + + Google ASP.NET Core Web API + Template for an ASP.NET Core Web API application for Google Cloud Platform. + CSharp + Web + 1000 + true + GoogleAspNetCoreWebAPI + true + Enabled + true + __TemplateIcon.png + true + true + 1 + true + + + + + launchSettings.json + + + + ValuesController.cs + + appsettings.json + Program.cs + Project_Readme.html + Startup.cs + web.config + + + + GoogleCloudExtension.TemplateWizards, Version=1.0.0.0, Culture=neutral, PublicKeyToken=919919421783e38f + GoogleCloudExtension.TemplateWizards.GoogleProjectTemplateWizard + + diff --git a/GoogleCloudExtension/ProjectTemplates/GcpProjectTemplate/Gcp.AspNet.vstemplate b/GoogleCloudExtension/ProjectTemplates/GcpProjectTemplate/Gcp.AspNet.vstemplate index f9a9c7580..1ebe9512e 100644 --- a/GoogleCloudExtension/ProjectTemplates/GcpProjectTemplate/Gcp.AspNet.vstemplate +++ b/GoogleCloudExtension/ProjectTemplates/GcpProjectTemplate/Gcp.AspNet.vstemplate @@ -23,4 +23,7 @@ GoogleCloudExtension.TemplateWizards, Version=1.0.0.0, Culture=neutral, PublicKeyToken=919919421783e38f GoogleCloudExtension.TemplateWizards.GoogleProjectTemplateSelectorWizard + + AspNet + \ No newline at end of file diff --git a/GoogleCloudExtension/ProjectTemplates/GcpProjectTemplate/Gcp.AspNetCore.vstemplate b/GoogleCloudExtension/ProjectTemplates/GcpProjectTemplate/Gcp.AspNetCore.vstemplate index 3e3a2d3b5..93e649738 100644 --- a/GoogleCloudExtension/ProjectTemplates/GcpProjectTemplate/Gcp.AspNetCore.vstemplate +++ b/GoogleCloudExtension/ProjectTemplates/GcpProjectTemplate/Gcp.AspNetCore.vstemplate @@ -23,4 +23,7 @@ GoogleCloudExtension.TemplateWizards, Version=1.0.0.0, Culture=neutral, PublicKeyToken=919919421783e38f GoogleCloudExtension.TemplateWizards.GoogleProjectTemplateSelectorWizard + + AspNetCore + \ No newline at end of file diff --git a/GoogleCloudExtension/ProjectTemplates/GcpProjectTemplate/GcpProjectTemplate.csproj b/GoogleCloudExtension/ProjectTemplates/GcpProjectTemplate/GcpProjectTemplate.csproj index e7e790db5..ff5b283b8 100644 --- a/GoogleCloudExtension/ProjectTemplates/GcpProjectTemplate/GcpProjectTemplate.csproj +++ b/GoogleCloudExtension/ProjectTemplates/GcpProjectTemplate/GcpProjectTemplate.csproj @@ -101,6 +101,10 @@ Designer + + MVC + + @@ -118,6 +122,11 @@ WebAPI + + + Designer + WebAPI + @@ -352,7 +361,9 @@ - + + + diff --git a/GoogleCloudExtension/ProjectTemplates/ProjectTemplate.Tests/ProjectTemplatesTests.cs b/GoogleCloudExtension/ProjectTemplates/ProjectTemplate.Tests/ProjectTemplatesTests.cs index 159000d6e..c92756bcb 100644 --- a/GoogleCloudExtension/ProjectTemplates/ProjectTemplate.Tests/ProjectTemplatesTests.cs +++ b/GoogleCloudExtension/ProjectTemplates/ProjectTemplate.Tests/ProjectTemplatesTests.cs @@ -122,6 +122,8 @@ public void TestCompileAspNet(string version, string appType) [DataRow("1.1", "WebApi")] [DataRow("2.0", "Mvc")] [DataRow("2.0", "WebApi")] + [DataRow("2.1", "Mvc")] + [DataRow("2.1", "WebApi")] #endif public void TestCompileAspNetCore(string version, string appType) { diff --git a/appveyor.yml b/appveyor.yml index e50839241..03ba54d90 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,7 +1,7 @@ # Configuration for the build for the Visual Studio extension for Google Cloud. # Version for the build. -version: 1.0.{build}-{branch} +version: 1.4.0-{build}.{branch} # We're using Visual Studio 2015 image: Visual Studio 2015