Skip to content

Commit

Permalink
Ensure the default deployment name is a valid name. (#959)
Browse files Browse the repository at this point in the history
* Ensure the default deployment name is a valid name.
  • Loading branch information
Jim Przybylinski authored May 17, 2018
1 parent c0100a4 commit 453a41b
Show file tree
Hide file tree
Showing 9 changed files with 178 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ public static string ThrowIfNullOrEmpty(this string arg, string message)
return arg;
}

public static T ThrowIfNull<T>(this T arg, string message)
public static T ThrowIfNull<T>(this T arg, string paramName)
{
if (arg == null)
{
throw new ArgumentNullException(message ?? "");
throw new ArgumentNullException(paramName ?? "");
}

return arg;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ protected override async Task InitializeDialogAsync()
Task initializeDialogTask = base.InitializeDialogAsync();

// In the meantime, set DeploymentName, which launches validations and updates the UI.
DeploymentName = PublishDialog.Project.Name.ToLower();
DeploymentName = GcpPublishStepsUtils.ToValidName(PublishDialog.Project.Name);

// Wait for the initialization task to be done.
await initializeDialogTask;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,9 @@ namespace GoogleCloudExtension.Utils
/// </summary>
public static class GcpPublishStepsUtils
{
/// <summary>
/// This regexp defines what names are valid for GCP deployments. Basically it defines a valid name
/// as only containing lowercase letters and numbers and optionally the - character. It also specifies
/// that the name has to be less than 100 chars.
/// This regexp is the same one used by gcloud to validate version names.
/// </summary>
private static readonly Regex s_validNamePattern = new Regex(@"^(?!-)[a-z\d\-]{1,100}$");

// Static properties for unit testing.
internal static DateTime? NowOverride { private get; set; }
private static DateTime Now => NowOverride ?? DateTime.Now;

/// <summary>
/// Returns a default version name suitable for publishing to GKE and Flex.
/// </summary>
Expand All @@ -51,8 +42,8 @@ public static string GetDefaultVersion()
/// Determines if the given name is a valid name.
/// </summary>
/// <param name="name">The name to check.</param>
/// <returns>True if the name is valid, false otherwise.</returns>
public static bool IsValidName(string name) => !string.IsNullOrEmpty(name) && s_validNamePattern.IsMatch(name);
/// <param name="fieldName">The name of the field being validated.</param>
/// <returns>The results of the validation.</returns>

public static IEnumerable<ValidationResult> ValidateName(string name, string fieldName)
{
Expand Down Expand Up @@ -80,6 +71,25 @@ public static IEnumerable<ValidationResult> ValidateName(string name, string fie
}
}

/// <summary>
/// Converts a possibly invalid name to one that will pass name validation.
/// </summary>
/// <param name="name">The name to converto to valid.</param>
/// <returns>A valid name built from the starting name.</returns>
public static string ToValidName(string name)
{
if (name == null)
{
return null;
}
string kebobedName = StringUtils.ToKebobCase(name);
string beginsWithLetterOrNumber = Regex.Replace(kebobedName, @"^[^a-z\d]+", "");
string invalidCharactersReplaced = Regex.Replace(beginsWithLetterOrNumber, @"[^a-z\d\-]", "-");
return invalidCharactersReplaced.Length > 100 ?
invalidCharactersReplaced.Substring(0, 100) :
invalidCharactersReplaced;
}

public static IEnumerable<ValidationResult> ValidatePositiveNonZeroInteger(string value, string fieldName)
{
int intValue;
Expand Down
39 changes: 39 additions & 0 deletions GoogleCloudExtension/GoogleCloudExtension/Utils/StringUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -174,5 +174,44 @@ public static int LastNonSpaceIndex(string text)

return -1;
}

/// <summary>
/// Converts the input CameCase text to lower-kebob-case
/// </summary>
/// <param name="camelCaseText">The input text to convert.</param>
/// <returns>The text converted to kebob-case.</returns>
public static string ToKebobCase(string camelCaseText)
{
if (camelCaseText == null)
{
return null;
}
var lastCharLowerLetter = false;
var lastCharLetter = false;
var lastCharDidget = false;
var result = new StringBuilder();
foreach (char c in camelCaseText)
{
if (char.IsUpper(c) && lastCharLowerLetter)
{
result.Append("-");
}
else if (char.IsLetter(c) && lastCharDidget)
{
result.Append("-");
}
else if (char.IsDigit(c) && lastCharLetter)
{
result.Append("-");
}

result.Append(char.ToLower(c));
lastCharLowerLetter = char.IsLower(c);
lastCharLetter = char.IsLetter(c);
lastCharDidget = char.IsDigit(c);
}

return result.ToString();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -276,10 +276,11 @@
<Compile Include="StackdriverLogsViewer\LogsViewerViewModelTests.cs" />
<Compile Include="StackdriverLogsViewer\SourceNavigation\LoggerTooltipViewModelTests.cs" />
<Compile Include="StackdriverLogsViewer\TreeViewConverters\ObjectNodeTreeTests.cs" />
<Compile Include="Utils\GcpPublishStepsUtilsTests.cs" />
<Compile Include="VsWindowFrameMocks.cs" />
<Compile Include="StackframeParserTests.cs" />
<Compile Include="SolutionUserOptionsTests.cs" />
<Compile Include="StringFormatUtilsTests.cs" />
<Compile Include="Utils\StringFormatUtilsTests.cs" />
<Compile Include="TemplateWizards\Dialogs\AspNetCoreTemplateChooserViewModelTests.cs" />
<Compile Include="TemplateWizards\Dialogs\AspNetTemplateChooserViewModelTests.cs" />
<Compile Include="ExtensionTestBase.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -352,13 +352,13 @@ public async Task TestRefreshClustersListCommand_SetsClustersInOrder()
}

[TestMethod]
public void TestInitializeDialogAsync_SetsDeploymentName()
public void TestInitializeDialogAsync_SetsValidDeploymentName()
{
Mock.Get(_mockedPublishDialog).Setup(pd => pd.Project.Name).Returns("AProjectName");
Mock.Get(_mockedPublishDialog).Setup(pd => pd.Project.Name).Returns("VisualStudioProjectName");

_objectUnderTest.OnVisible(_mockedPublishDialog);

Assert.AreEqual("aprojectname", _objectUnderTest.DeploymentName);
Assert.AreEqual("visual-studio-project-name", _objectUnderTest.DeploymentName);
}

[TestMethod]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// 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.Utils;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Linq;

namespace GoogleCloudExtensionUnitTests.Utils
{
[TestClass]
public class GcpPublishStepsUtilsTests
{
[TestMethod]
[DataRow(null)]
[DataRow("")]
[DataRow("already-kebob-case")]
[DataRow("end-with-dash-")]
public void TestToValid_NameUnchanged(string unchangingName)
{
string result = GcpPublishStepsUtils.ToValidName(unchangingName);

Assert.AreEqual(unchangingName, result);
}

[TestMethod]
[DataRow("UpperCamelCase", "upper-camel-case")]
[DataRow("many.(*)symbols!@$", "many----symbols---")]
[DataRow("-start-with-dash", "start-with-dash")]
[DataRow("@start-with-symbol", "start-with-symbol")]
public void TestToValid_NameChange(string invalidName, string expectedResult)
{
string result = GcpPublishStepsUtils.ToValidName(invalidName);

Assert.AreEqual(expectedResult, result);
}

[TestMethod]
public void TestToValid_NameTruncatesAt100Characters()
{
string longString = string.Join("", Enumerable.Range(1, 200));

string result = GcpPublishStepsUtils.ToValidName(longString);

Assert.IsTrue(result.Length == 100);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
using GoogleCloudExtension.Utils;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace GoogleCloudExtensionUnitTests
namespace GoogleCloudExtensionUnitTests.Utils
{
[TestClass]
public class StringFormatUtilsTests
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
// limitations under the License.

using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Linq;
using static GoogleCloudExtension.Utils.StringUtils;

namespace GoogleCloudExtensionUnitTests.Utils
Expand Down Expand Up @@ -58,5 +59,55 @@ public void LastNonSpaceIndexTests()
Assert.AreEqual(-1, LastNonSpaceIndex(null));
Assert.AreEqual(8, LastNonSpaceIndex(" uu pp "));
}

[TestMethod]
[DataRow(null)]
[DataRow("")]
[DataRow("word")]
[DataRow("already-kebob-case")]
[DataRow("%$#(*&)(@#$")]
public void TestToKebobCase_Unchanged(string unchangingArgument)
{
string result = ToKebobCase(unchangingArgument);

Assert.AreEqual(unchangingArgument, result);
}

[TestMethod]
[DataRow("ALLUPPERCASE", "alluppercase")]
[DataRow("UpperCamelCase", "upper-camel-case")]
[DataRow("lowerCamelCase", "lower-camel-case")]
[DataRow("Upper-Kebob-Case", "upper-kebob-case")]
[DataRow("20number2", "20-number-2")]
[DataRow("UpperCamelCaseWith2Numbers100", "upper-camel-case-with-2-numbers-100")]
[DataRow("CamelCase&Symbols!", "camel-case&symbols!")]
public void TestToKebobCase_Changes(string argument, string expectedResult)
{
string result = ToKebobCase(argument);

Assert.AreEqual(expectedResult, result);
}

[TestMethod]
public void TestToKebobCase_ExtremelyLongValueChanges()
{
string longNumberString = string.Join("", Enumerable.Range(1, 200));
string argument = "CamelCaseHead" + longNumberString + "CamelCaseTail";
string expected = "camel-case-head-" + longNumberString + "-camel-case-tail";

string result = ToKebobCase(argument);

Assert.AreEqual(expected, result);
}

[TestMethod]
public void TestToKebobCase_ExtremelyLongValueUnchanged()
{
string longNumberString = string.Join("", Enumerable.Range(1, 200));

string result = ToKebobCase(longNumberString);

Assert.AreEqual(longNumberString, result);
}
}
}

0 comments on commit 453a41b

Please sign in to comment.