Skip to content

Worker Indexing - Moving common validate logic to utility #7807

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 1 addition & 11 deletions src/WebJobs.Script/Description/FunctionDescriptorProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ namespace Microsoft.Azure.WebJobs.Script.Description
{
public abstract class FunctionDescriptorProvider
{
private static readonly Regex BindingNameValidationRegex = new Regex(string.Format("^([a-zA-Z][a-zA-Z0-9]{{0,127}}|{0})$", Regex.Escape(ScriptConstants.SystemReturnParameterBindingName)));

protected FunctionDescriptorProvider(ScriptHost host, ScriptJobHostOptions config, ICollection<IScriptBindingProvider> bindingProviders)
{
Host = host;
Expand Down Expand Up @@ -211,15 +209,7 @@ protected internal virtual void ValidateFunction(FunctionMetadata functionMetada

protected internal virtual void ValidateBinding(BindingMetadata bindingMetadata)
{
if (bindingMetadata.Name == null || !BindingNameValidationRegex.IsMatch(bindingMetadata.Name))
{
throw new ArgumentException($"The binding name {bindingMetadata.Name} is invalid. Please assign a valid name to the binding.");
}

if (bindingMetadata.IsReturn && bindingMetadata.Direction != BindingDirection.Out)
{
throw new ArgumentException($"{ScriptConstants.SystemReturnParameterBindingName} bindings must specify a direction of 'out'.");
}
Utility.ValidateBinding(bindingMetadata);

if (bindingMetadata.Type == null)
{
Expand Down
10 changes: 1 addition & 9 deletions src/WebJobs.Script/Host/HostFunctionMetadataProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ private FunctionMetadata ReadFunctionMetadata(string functionDirectory, IFileSys

functionName = Path.GetFileName(functionDirectory);

ValidateName(functionName);
Utility.ValidateName(functionName);

JObject functionConfig = JObject.Parse(json);

Expand All @@ -109,14 +109,6 @@ private FunctionMetadata ReadFunctionMetadata(string functionDirectory, IFileSys
}
}

internal void ValidateName(string name, bool isProxy = false)
{
if (!Utility.IsValidFunctionName(name))
{
throw new InvalidOperationException(string.Format("'{0}' is not a valid {1} name.", name, isProxy ? "proxy" : "function"));
}
}

private FunctionMetadata ParseFunctionMetadata(string functionName, JObject configMetadata, string scriptDirectory, IFileSystem fileSystem, IEnumerable<RpcWorkerConfig> workerConfigs)
{
var functionMetadata = new FunctionMetadata
Expand Down
23 changes: 2 additions & 21 deletions src/WebJobs.Script/Host/WorkerFunctionMetadataProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ namespace Microsoft.Azure.WebJobs.Script
{
public class WorkerFunctionMetadataProvider : IFunctionMetadataProvider
{
private static readonly Regex BindingNameValidationRegex = new Regex(string.Format("^([a-zA-Z][a-zA-Z0-9]{{0,127}}|{0})$", Regex.Escape(ScriptConstants.SystemReturnParameterBindingName)));
private readonly Dictionary<string, ICollection<string>> _functionErrors = new Dictionary<string, ICollection<string>>();
private readonly ILogger _logger;
private readonly IFunctionInvocationDispatcher _dispatcher;
Expand Down Expand Up @@ -85,7 +84,7 @@ internal IEnumerable<FunctionMetadata> ValidateMetadata(IEnumerable<RawFunctionM
var function = rawFunction.Metadata;
try
{
ValidateName(function.Name);
Utility.ValidateName(function.Name);

function.Language = SystemEnvironment.Instance.GetEnvironmentVariable(RpcWorkerConstants.FunctionWorkerRuntimeSettingName);

Expand Down Expand Up @@ -129,14 +128,6 @@ internal IEnumerable<FunctionMetadata> ValidateMetadata(IEnumerable<RawFunctionM
return validatedMetadata;
}

internal static void ValidateName(string name, bool isProxy = false)
{
if (!Utility.IsValidFunctionName(name))
{
throw new InvalidOperationException(string.Format("'{0}' is not a valid {1} name.", name, isProxy ? "proxy" : "function"));
}
}

internal static FunctionMetadata ValidateBindings(IEnumerable<string> rawBindings, FunctionMetadata function)
{
HashSet<string> bindingNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
Expand All @@ -145,17 +136,7 @@ internal static FunctionMetadata ValidateBindings(IEnumerable<string> rawBinding
{
var functionBinding = BindingMetadata.Create(JObject.Parse(binding));

// validate binding name
if (functionBinding.Name == null || !BindingNameValidationRegex.IsMatch(functionBinding.Name))
{
throw new ArgumentException($"The binding name {functionBinding.Name} is invalid. Please assign a valid name to the binding.");
}

// validate that output binding has correct direction
if (functionBinding.IsReturn && functionBinding.Direction != BindingDirection.Out)
{
throw new ArgumentException($"{ScriptConstants.SystemReturnParameterBindingName} bindings must specify a direction of 'out'.");
}
Utility.ValidateBinding(functionBinding);

// Ensure no duplicate binding names exist
if (bindingNames.Contains(functionBinding.Name))
Expand Down
22 changes: 22 additions & 0 deletions src/WebJobs.Script/Utility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public static class Utility
private const string SasVersionQueryParam = "sv";

private static readonly Regex FunctionNameValidationRegex = new Regex(@"^[a-z][a-z0-9_\-]{0,127}$(?<!^host$)", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
private static readonly Regex BindingNameValidationRegex = new Regex(string.Format("^([a-zA-Z][a-zA-Z0-9]{{0,127}}|{0})$", Regex.Escape(ScriptConstants.SystemReturnParameterBindingName)));

private static readonly string UTF8ByteOrderMark = Encoding.UTF8.GetString(Encoding.UTF8.GetPreamble());
private static readonly FilteredExpandoObjectConverter _filteredExpandoObjectConverter = new FilteredExpandoObjectConverter();
Expand Down Expand Up @@ -546,6 +547,27 @@ internal static void AddFunctionError(IDictionary<string, ICollection<string>> f
functionErrorCollection.Add(error);
}

public static void ValidateBinding(BindingMetadata bindingMetadata)
{
if (bindingMetadata.Name == null || !BindingNameValidationRegex.IsMatch(bindingMetadata.Name))
{
throw new ArgumentException($"The binding name {bindingMetadata.Name} is invalid. Please assign a valid name to the binding.");
}

if (bindingMetadata.IsReturn && bindingMetadata.Direction != BindingDirection.Out)
{
throw new ArgumentException($"{ScriptConstants.SystemReturnParameterBindingName} bindings must specify a direction of 'out'.");
}
}

public static void ValidateName(string name)
{
if (!IsValidFunctionName(name))
{
throw new InvalidOperationException(string.Format("'{0}' is not a valid {1} name.", name, "function"));
}
}

internal static bool TryReadFunctionConfig(string scriptDir, out string json, IFileSystem fileSystem = null)
{
json = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,8 @@ public void ValidateBinding_InvalidName_Throws(string bindingName)
{
BindingMetadata bindingMetadata = new BindingMetadata
{
Name = bindingName
Name = bindingName,
Type = null
};

var ex = Assert.Throws<ArgumentException>(() =>
Expand Down
47 changes: 0 additions & 47 deletions test/WebJobs.Script.Tests/HostFunctionMetadataProviderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,53 +87,6 @@ private bool AreRequiredMetricsEmitted(TestMetricsLogger metricsLogger)
&& metricsLogger.EventsEnded.Contains(MetricEventNames.ReadFunctionsMetadata));
}

[Theory]
[InlineData("")]
[InlineData("host")]
[InlineData("Host")]
[InlineData("-function")]
[InlineData("_function")]
[InlineData("function test")]
[InlineData("function.test")]
[InlineData("function0.1")]
public void ValidateFunctionName_ThrowsOnInvalidName(string functionName)
{
string functionsPath = "c:\testdir";
_scriptApplicationHostOptions.ScriptPath = functionsPath;
var optionsMonitor = TestHelpers.CreateOptionsMonitor(_scriptApplicationHostOptions);
var metadataProvider = new HostFunctionMetadataProvider(optionsMonitor, NullLogger<HostFunctionMetadataProvider>.Instance, _testMetricsLogger);

var ex = Assert.Throws<InvalidOperationException>(() =>
{
metadataProvider.ValidateName(functionName);
});

Assert.Equal(string.Format("'{0}' is not a valid function name.", functionName), ex.Message);
}

[Theory]
[InlineData("testwithhost")]
[InlineData("hosts")]
[InlineData("myfunction")]
[InlineData("myfunction-test")]
[InlineData("myfunction_test")]
public void ValidateFunctionName_DoesNotThrowOnValidName(string functionName)
{
string functionsPath = "c:\testdir";
_scriptApplicationHostOptions.ScriptPath = functionsPath;
var optionsMonitor = TestHelpers.CreateOptionsMonitor(_scriptApplicationHostOptions);
var metadataProvider = new HostFunctionMetadataProvider(optionsMonitor, NullLogger<HostFunctionMetadataProvider>.Instance, _testMetricsLogger);

try
{
metadataProvider.ValidateName(functionName);
}
catch (InvalidOperationException)
{
Assert.True(false, $"Valid function name {functionName} failed validation.");
}
}

[Theory]
[InlineData("node", "test.js", false)]
[InlineData("java", "test.jar", false)]
Expand Down
85 changes: 85 additions & 0 deletions test/WebJobs.Script.Tests/UtilityTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,91 @@ public void IsHttporManualTriggerTests(string triggerType, bool expectedResult)
Assert.Equal(expectedResult, Utility.IsHttporManualTrigger(triggerType));
}

[Theory]
[InlineData("")]
[InlineData("host")]
[InlineData("Host")]
[InlineData("-function")]
[InlineData("_function")]
[InlineData("function test")]
[InlineData("function.test")]
[InlineData("function0.1")]
public void ValidateFunctionName_ThrowsOnInvalidName(string functionName)
{
var ex = Assert.Throws<InvalidOperationException>(() =>
{
Utility.ValidateName(functionName);
});

Assert.Equal(string.Format("'{0}' is not a valid function name.", functionName), ex.Message);
}

[Theory]
[InlineData("testwithhost")]
[InlineData("hosts")]
[InlineData("myfunction")]
[InlineData("myfunction-test")]
[InlineData("myfunction_test")]
public void ValidateFunctionName_DoesNotThrowOnValidName(string functionName)
{
try
{
Utility.ValidateName(functionName);
}
catch (InvalidOperationException)
{
Assert.True(false, $"Valid function name {functionName} failed validation.");
}
}

[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData("_binding")]
[InlineData("binding-test")]
[InlineData("binding name")]
public void ValidateBinding_InvalidName_Throws(string bindingName)
{
BindingMetadata bindingMetadata = new BindingMetadata
{
Name = bindingName
};

var ex = Assert.Throws<ArgumentException>(() =>
{
Utility.ValidateBinding(bindingMetadata);
});

Assert.Equal($"The binding name {bindingName} is invalid. Please assign a valid name to the binding.", ex.Message);
}

[Theory]
[InlineData("bindingName")]
[InlineData("binding1")]
[InlineData(ScriptConstants.SystemReturnParameterBindingName)]
public void ValidateBinding_ValidName_DoesNotThrow(string bindingName)
{
BindingMetadata bindingMetadata = new BindingMetadata
{
Name = bindingName,
Type = "Blob"
};

if (bindingMetadata.IsReturn)
{
bindingMetadata.Direction = BindingDirection.Out;
}

try
{
Utility.ValidateBinding(bindingMetadata);
}
catch (ArgumentException)
{
Assert.True(false, $"Valid binding name '{bindingName}' failed validation.");
}
}

[Theory]
[InlineData("createIsolationEnvironment", "tr-TR", true)]
[InlineData("HttpTrigger2", "en-US", true)]
Expand Down
37 changes: 0 additions & 37 deletions test/WebJobs.Script.Tests/WorkerFunctionMetadataProviderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,43 +25,6 @@ public WorkerFunctionMetadataProviderTests()
_scriptApplicationHostOptions = new ScriptApplicationHostOptions();
}

[Theory]
[InlineData("")]
[InlineData("host")]
[InlineData("Host")]
[InlineData("-function")]
[InlineData("_function")]
[InlineData("function test")]
[InlineData("function.test")]
[InlineData("function0.1")]
public void ValidateFunctionName_ThrowsOnInvalidName(string functionName)
{
var ex = Assert.Throws<InvalidOperationException>(() =>
{
WorkerFunctionMetadataProvider.ValidateName(functionName);
});

Assert.Equal(string.Format("'{0}' is not a valid function name.", functionName), ex.Message);
}

[Theory]
[InlineData("testwithhost")]
[InlineData("hosts")]
[InlineData("myfunction")]
[InlineData("myfunction-test")]
[InlineData("myfunction_test")]
public void ValidateFunctionName_DoesNotThrowOnValidName(string functionName)
{
try
{
WorkerFunctionMetadataProvider.ValidateName(functionName);
}
catch (InvalidOperationException)
{
Assert.True(false, $"Valid function name {functionName} failed validation.");
}
}

[Fact]
public void ValidateBindings_NoBindings_Throws()
{
Expand Down