Skip to content

Commit

Permalink
Proxy names should follow the same naming restrictions as function na…
Browse files Browse the repository at this point in the history
  • Loading branch information
safihamid committed Oct 17, 2017
1 parent 74829f3 commit c9ea20e
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 22 deletions.
56 changes: 36 additions & 20 deletions src/WebJobs.Script/Host/ScriptHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1119,7 +1119,7 @@ public static Collection<FunctionMetadata> ReadFunctionMetadata(ScriptHostConfig
continue;
}

ValidateFunctionName(functionName);
ValidateName(functionName);

string json = File.ReadAllText(functionConfigPath);
JObject functionConfig = JObject.Parse(json);
Expand Down Expand Up @@ -1196,27 +1196,38 @@ private Collection<FunctionMetadata> LoadProxyRoutes(string proxiesJson)

foreach (var route in routes.Routes)
{
var proxyMetadata = new FunctionMetadata();

var json = new JObject
try
{
{ "authLevel", "anonymous" },
{ "name", "req" },
{ "type", "httptrigger" },
{ "direction", "in" },
{ "Route", route.UrlTemplate.TrimStart('/') },
{ "Methods", new JArray(route.Methods.Select(m => m.Method.ToString()).ToArray()) }
};
// Proxy names should follow the same naming restrictions as in function names.
ValidateName(route.Name, true);

BindingMetadata bindingMetadata = BindingMetadata.Create(json);
var proxyMetadata = new FunctionMetadata();

proxyMetadata.Bindings.Add(bindingMetadata);
var json = new JObject
{
{ "authLevel", "anonymous" },
{ "name", "req" },
{ "type", "httptrigger" },
{ "direction", "in" },
{ "Route", route.UrlTemplate.TrimStart('/') },
{ "Methods", new JArray(route.Methods.Select(m => m.Method.ToString()).ToArray()) }
};

proxyMetadata.Name = route.Name;
proxyMetadata.ScriptType = ScriptType.Unknown;
proxyMetadata.IsProxy = true;
BindingMetadata bindingMetadata = BindingMetadata.Create(json);

proxies.Add(proxyMetadata);
proxyMetadata.Bindings.Add(bindingMetadata);

proxyMetadata.Name = route.Name;
proxyMetadata.ScriptType = ScriptType.Unknown;
proxyMetadata.IsProxy = true;

proxies.Add(proxyMetadata);
}
catch (Exception ex)
{
// log any unhandled exceptions and continue
AddFunctionError(FunctionErrors, route.Name, Utility.FlattenException(ex, includeSource: false), isFunctionShortName: true);
}
}

return proxies;
Expand Down Expand Up @@ -1263,11 +1274,11 @@ internal static bool TryParseFunctionMetadata(string functionName, JObject funct
return true;
}

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

Expand Down Expand Up @@ -1437,6 +1448,11 @@ internal static void ValidateFunction(FunctionDescriptor function, Dictionary<st
}
}

if (httpFunctions.ContainsKey(function.Name))
{
throw new InvalidOperationException($"The function or proxy name '{function.Name}' must be unique within the function app.");
}

httpFunctions.Add(function.Name, httpTrigger);
}
}
Expand Down
51 changes: 49 additions & 2 deletions test/WebJobs.Script.Tests/ScriptHostTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1230,12 +1230,25 @@ public void ValidateFunctionName_ThrowsOnInvalidName(string functionName)
{
var ex = Assert.Throws<InvalidOperationException>(() =>
{
ScriptHost.ValidateFunctionName(functionName);
ScriptHost.ValidateName(functionName);
});

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

[Theory]
[InlineData("bing.com")]
[InlineData("http://bing.com")]
public void ValidateProxyName_ThrowsOnInvalidName(string proxyName)
{
var ex = Assert.Throws<InvalidOperationException>(() =>
{
ScriptHost.ValidateName(proxyName, true);
});

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

[Theory]
[InlineData("testwithhost")]
[InlineData("hosts")]
Expand All @@ -1246,7 +1259,7 @@ public void ValidateFunctionName_DoesNotThrowOnValidName(string functionName)
{
try
{
ScriptHost.ValidateFunctionName(functionName);
ScriptHost.ValidateName(functionName);
}
catch (InvalidOperationException)
{
Expand Down Expand Up @@ -1394,6 +1407,40 @@ public void ValidateFunction_ValidatesHttpRoutes()
}
#endif

[Fact]
public void ValidateFunction_ThrowsOnDuplicateName()
{
var httpFunctions = new Dictionary<string, HttpTriggerAttribute>();
var name = "test";

// first add an http function
var metadata = new FunctionMetadata();
var function = new Mock<FunctionDescriptor>(MockBehavior.Strict, name, null, metadata, null, null, null, null);
var attribute = new HttpTriggerAttribute(AuthorizationLevel.Function, "get");
function.Setup(p => p.GetTriggerAttributeOrNull<HttpTriggerAttribute>()).Returns(() => attribute);

ScriptHost.ValidateFunction(function.Object, httpFunctions);

// add a proxy with same name
metadata = new FunctionMetadata()
{
IsProxy = true
};
function = new Mock<FunctionDescriptor>(MockBehavior.Strict, name, null, metadata, null, null, null, null);
attribute = new HttpTriggerAttribute(AuthorizationLevel.Function, "get")
{
Route = "proxyRoute"
};
function.Setup(p => p.GetTriggerAttributeOrNull<HttpTriggerAttribute>()).Returns(() => attribute);

var ex = Assert.Throws<InvalidOperationException>(() =>
{
ScriptHost.ValidateFunction(function.Object, httpFunctions);
});

Assert.Equal(string.Format($"The function or proxy name '{name}' must be unique within the function app.", name), ex.Message);
}

[Fact]
public void IsFunction_ReturnsExpectedResult()
{
Expand Down

0 comments on commit c9ea20e

Please sign in to comment.