refactoring build; using Mono.Cecil for attribute discovery
This commit is contained in:
Родитель
d2e97355c3
Коммит
1922f7d495
|
@ -64,6 +64,7 @@ Target "GenerateZipToSign" (fun _ ->
|
|||
|> CreateZip "." (version + "net46.zip") "" 7 true
|
||||
|
||||
!! (generatorOutputPath @@ "net461\\Newtonsoft.Json.dll")
|
||||
++ (generatorOutputPath @@ "net461\\Mono.Cecil.dll")
|
||||
|> CreateZip "." (version + "net46thirdparty.zip") "" 7 true
|
||||
|
||||
!! (packOutputPath @@ "netstandard2.0\\Microsoft.NET.Sdk.Functions.dll")
|
||||
|
@ -72,6 +73,7 @@ Target "GenerateZipToSign" (fun _ ->
|
|||
|> CreateZip "." (version + "netstandard2.zip") "" 7 true
|
||||
|
||||
!! (generatorOutputPath @@ "netcoreapp2.1\\Newtonsoft.Json.dll")
|
||||
++ (generatorOutputPath @@ "netcoreapp2.1\\Mono.Cecil.dll")
|
||||
|> CreateZip "." (version + "netstandard2thidparty.zip") "" 7 true
|
||||
)
|
||||
|
||||
|
@ -146,6 +148,7 @@ Target "WaitForSigning" (fun _ ->
|
|||
| Success file ->
|
||||
Unzip "tmpBuild" file
|
||||
MoveFileTo ("tmpBuild" @@ "Newtonsoft.Json.dll", generatorOutputPath @@ "net461\\Newtonsoft.Json.dll")
|
||||
MoveFileTo ("tmpBuild" @@ "Mono.Cecil.dll", generatorOutputPath @@ "net461\\Mono.Cecil.dll")
|
||||
| Failure e -> targetError e null |> ignore
|
||||
|
||||
CleanDir "tmpBuild"
|
||||
|
@ -155,6 +158,7 @@ Target "WaitForSigning" (fun _ ->
|
|||
| Success file ->
|
||||
Unzip "tmpBuild" file
|
||||
MoveFileTo ("tmpBuild" @@ "Newtonsoft.Json.dll", generatorOutputPath @@ "netcoreapp2.1\\Newtonsoft.Json.dll")
|
||||
MoveFileTo ("tmpBuild" @@ "Mono.Cecil.dll", generatorOutputPath @@ "netcoreapp2.1\\Mono.Cecil.dll")
|
||||
| Failure e -> targetError e null |> ignore
|
||||
)
|
||||
|
||||
|
|
|
@ -63,6 +63,10 @@
|
|||
<None Include="$(FunctionsGeneratorOutputPath)\net461\System.ValueTuple.dll">
|
||||
<Pack>true</Pack>
|
||||
<PackagePath>tools\net46\</PackagePath>
|
||||
</None>
|
||||
<None Include="$(FunctionsGeneratorOutputPath)\net461\Mono.Cecil.dll">
|
||||
<Pack>true</Pack>
|
||||
<PackagePath>tools\net46\</PackagePath>
|
||||
</None>
|
||||
<None Include="$(FunctionsGeneratorOutputPath)\net461\Microsoft.NET.Sdk.Functions.Generator.exe">
|
||||
<Pack>true</Pack>
|
||||
|
@ -80,6 +84,10 @@
|
|||
<None Include="$(FunctionsGeneratorOutputPath)\netcoreapp2.1\Newtonsoft.Json.dll">
|
||||
<Pack>true</Pack>
|
||||
<PackagePath>tools\netcoreapp2.1\</PackagePath>
|
||||
</None>
|
||||
<None Include="$(FunctionsGeneratorOutputPath)\netcoreapp2.1\Mono.Cecil.dll">
|
||||
<Pack>true</Pack>
|
||||
<PackagePath>tools\netcoreapp2.1\</PackagePath>
|
||||
</None>
|
||||
<None Include="$(FunctionsGeneratorOutputPath)\netcoreapp2.1\Microsoft.NET.Sdk.Functions.Generator.dll">
|
||||
<Pack>true</Pack>
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.NET.Sdk.Functions.MakeFunction;
|
||||
using Mono.Cecil;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MakeFunctionJson
|
||||
{
|
||||
|
@ -15,9 +16,18 @@ namespace MakeFunctionJson
|
|||
/// <param name="attribute"></param>
|
||||
/// <returns></returns>
|
||||
public static string ToAttributeFriendlyName(this Attribute attribute)
|
||||
{
|
||||
return ToAttributeFriendlyName(attribute.GetType().Name);
|
||||
}
|
||||
|
||||
public static string ToAttributeFriendlyName(this CustomAttribute attribute)
|
||||
{
|
||||
return ToAttributeFriendlyName(attribute.AttributeType.Name);
|
||||
}
|
||||
|
||||
private static string ToAttributeFriendlyName(string name)
|
||||
{
|
||||
const string suffix = nameof(Attribute);
|
||||
var name = attribute.GetType().Name;
|
||||
name = name.Substring(0, name.Length - suffix.Length);
|
||||
return name.ToLowerFirstCharacter();
|
||||
}
|
||||
|
@ -37,15 +47,10 @@ namespace MakeFunctionJson
|
|||
/// </summary>
|
||||
/// <param name="attribute"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsWebJobsAttribute(this Attribute attribute)
|
||||
public static bool IsWebJobsAttribute(this CustomAttribute attribute)
|
||||
{
|
||||
#if NET46
|
||||
return attribute.GetType().GetCustomAttributes().Any(a => a.GetType().FullName == "Microsoft.Azure.WebJobs.Description.BindingAttribute")
|
||||
|| _supportedAttributes.Contains(attribute.GetType().Name);
|
||||
#else
|
||||
return attribute.GetType().GetTypeInfo().GetCustomAttributesData().Any(a => a.AttributeType.FullName == "Microsoft.Azure.WebJobs.Description.BindingAttribute")
|
||||
|| _supportedAttributes.Contains(attribute.GetType().Name);
|
||||
#endif
|
||||
return attribute.AttributeType.Resolve().CustomAttributes.Any(a => a.AttributeType.FullName == "Microsoft.Azure.WebJobs.Description.BindingAttribute")
|
||||
|| _supportedAttributes.Contains(attribute.AttributeType.FullName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -211,14 +216,6 @@ namespace MakeFunctionJson
|
|||
return "schedule";
|
||||
}
|
||||
}
|
||||
else if (attributeName == "EventHubTriggerAttribute" &&
|
||||
attribute.GetType().Assembly.GetName().Version.Major == 2)
|
||||
{
|
||||
if (propertyName == "EventHubName")
|
||||
{
|
||||
return "path";
|
||||
}
|
||||
}
|
||||
else if (attributeName == "ApiHubFileTrigger")
|
||||
{
|
||||
if (propertyName == "ConnectionStringSetting")
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Microsoft.NET.Sdk.Functions.Generator
|
||||
{
|
||||
public static class CustomAttributeDataExtensions
|
||||
{
|
||||
public static Attribute ConvertToAttribute(this CustomAttributeData data)
|
||||
{
|
||||
var attribute = data.Constructor.Invoke(data.ConstructorArguments.Select(arg => arg.Value).ToArray()) as Attribute;
|
||||
|
||||
foreach (var namedArgument in data.NamedArguments)
|
||||
{
|
||||
(namedArgument.MemberInfo as PropertyInfo)?.SetValue(attribute, namedArgument.TypedValue.Value, null);
|
||||
(namedArgument.MemberInfo as FieldInfo)?.SetValue(attribute, namedArgument.TypedValue.Value);
|
||||
}
|
||||
|
||||
return attribute;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Mono.Cecil;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace MakeFunctionJson
|
||||
|
@ -14,7 +15,7 @@ namespace MakeFunctionJson
|
|||
private bool _functionsInDependencies;
|
||||
private readonly HashSet<string> _excludedFunctionNames;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IDictionary<string, MethodInfo> _functionNamesSet;
|
||||
private readonly IDictionary<string, MethodDefinition> _functionNamesSet;
|
||||
|
||||
private static readonly IEnumerable<string> _functionsArtifacts = new[]
|
||||
{
|
||||
|
@ -34,7 +35,7 @@ namespace MakeFunctionJson
|
|||
{
|
||||
throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
|
||||
if (string.IsNullOrEmpty(assemblyPath))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(assemblyPath));
|
||||
|
@ -54,7 +55,7 @@ namespace MakeFunctionJson
|
|||
{
|
||||
_outputPath = Path.Combine(Directory.GetCurrentDirectory(), _outputPath);
|
||||
}
|
||||
_functionNamesSet = new Dictionary<string, MethodInfo>(StringComparer.OrdinalIgnoreCase);
|
||||
_functionNamesSet = new Dictionary<string, MethodDefinition>(StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -111,11 +112,11 @@ namespace MakeFunctionJson
|
|||
}
|
||||
}
|
||||
|
||||
public IEnumerable<(FunctionJsonSchema schema, FileInfo outputFile)?> GenerateFunctions(IEnumerable<Type> types)
|
||||
public IEnumerable<(FunctionJsonSchema schema, FileInfo outputFile)?> GenerateFunctions(IEnumerable<TypeDefinition> types)
|
||||
{
|
||||
foreach (var type in types)
|
||||
{
|
||||
foreach (var method in type.GetMethods())
|
||||
foreach (var method in type.Methods)
|
||||
{
|
||||
if (method.HasFunctionNameAttribute())
|
||||
{
|
||||
|
@ -129,7 +130,7 @@ namespace MakeFunctionJson
|
|||
var functionName = method.GetSdkFunctionName();
|
||||
var artifactName = Path.Combine(functionName, "function.json");
|
||||
var path = Path.Combine(_outputPath, artifactName);
|
||||
var relativeAssemblyPath = PathUtility.MakeRelativePath(Path.Combine(_outputPath, "dummyFunctionName"), type.Assembly.Location);
|
||||
var relativeAssemblyPath = PathUtility.MakeRelativePath(Path.Combine(_outputPath, "dummyFunctionName"), type.Module.FileName);
|
||||
var functionJson = method.ToFunctionJson(relativeAssemblyPath);
|
||||
if (CheckAppSettingsAndFunctionName(functionJson, method))
|
||||
{
|
||||
|
@ -144,42 +145,54 @@ namespace MakeFunctionJson
|
|||
{
|
||||
if (method.HasNoAutomaticTriggerAttribute() && method.HasTriggerAttribute())
|
||||
{
|
||||
_logger.LogWarning($"Method {method.ReflectedType?.FullName}.{method.Name} has both a 'NoAutomaticTrigger' attribute and a trigger attribute. Both can't be used together for an Azure function definition.");
|
||||
_logger.LogWarning($"Method {method.DeclaringType.GetReflectionFullName()}.{method.Name} has both a 'NoAutomaticTrigger' attribute and a trigger attribute. Both can't be used together for an Azure function definition.");
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning($"Method {method.ReflectedType?.FullName}.{method.Name} is missing a trigger attribute. Both a trigger attribute and FunctionName attribute are required for an Azure function definition.");
|
||||
_logger.LogWarning($"Method {method.DeclaringType.GetReflectionFullName()}.{method.Name} is missing a trigger attribute. Both a trigger attribute and FunctionName attribute are required for an Azure function definition.");
|
||||
}
|
||||
}
|
||||
else if (method.HasValidWebJobSdkTriggerAttribute())
|
||||
{
|
||||
_logger.LogWarning($"Method {method.ReflectedType?.FullName}.{method.Name} is missing the 'FunctionName' attribute. Both a trigger attribute and 'FunctionName' are required for an Azure function definition.");
|
||||
_logger.LogWarning($"Method {method.DeclaringType.GetReflectionFullName()}.{method.Name} is missing the 'FunctionName' attribute. Both a trigger attribute and 'FunctionName' are required for an Azure function definition.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private bool TryGenerateFunctionJsons()
|
||||
{
|
||||
var assembly = Assembly.LoadFrom(_assemblyPath);
|
||||
var assemblyRoot = Path.GetDirectoryName(_assemblyPath);
|
||||
|
||||
var exportedTypes = assembly.ExportedTypes;
|
||||
var resolver = new DefaultAssemblyResolver();
|
||||
resolver.AddSearchDirectory(assemblyRoot);
|
||||
|
||||
var readerParams = new ReaderParameters
|
||||
{
|
||||
AssemblyResolver = resolver
|
||||
};
|
||||
|
||||
var module = ModuleDefinition.ReadModule(_assemblyPath, readerParams);
|
||||
|
||||
IEnumerable<TypeDefinition> exportedTypes = module.Types;
|
||||
|
||||
if (_functionsInDependencies)
|
||||
{
|
||||
foreach (var referencedAssembly in assembly.GetReferencedAssemblies())
|
||||
foreach (var referencedAssembly in module.AssemblyReferences)
|
||||
{
|
||||
var tryPath = Path.Combine(assemblyRoot, $"{referencedAssembly.Name}.dll");
|
||||
try
|
||||
if (File.Exists(tryPath))
|
||||
{
|
||||
var loadedAssembly = Assembly.LoadFrom(tryPath);
|
||||
exportedTypes = exportedTypes.Concat(loadedAssembly.ExportedTypes);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning($"Could not evaluate '{referencedAssembly.Name}' for function types. Exception message: {ex.Message}");
|
||||
try
|
||||
{
|
||||
var loadedModule = ModuleDefinition.ReadModule(tryPath, readerParams);
|
||||
exportedTypes = exportedTypes.Concat(loadedModule.Types);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning($"Could not evaluate '{referencedAssembly.Name}' for function types. Exception message: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -192,7 +205,7 @@ namespace MakeFunctionJson
|
|||
return functions.All(f => f.HasValue);
|
||||
}
|
||||
|
||||
private bool CheckAppSettingsAndFunctionName(FunctionJsonSchema functionJson, MethodInfo method)
|
||||
private bool CheckAppSettingsAndFunctionName(FunctionJsonSchema functionJson, MethodDefinition method)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.NET.Sdk.Functions.Generator;
|
||||
using Mono.Cecil;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace MakeFunctionJson
|
||||
|
@ -13,37 +13,37 @@ namespace MakeFunctionJson
|
|||
/// </summary>
|
||||
/// <param name="method">method to check if an SDK method or not.</param>
|
||||
/// <returns>true if <paramref name="method"/> is a WebJobs SDK method. False otherwise.</returns>
|
||||
public static bool IsWebJobsSdkMethod(this MethodInfo method)
|
||||
public static bool IsWebJobsSdkMethod(this MethodDefinition method)
|
||||
{
|
||||
return method.HasFunctionNameAttribute() && method.HasValidWebJobSdkTriggerAttribute();
|
||||
}
|
||||
|
||||
public static bool HasValidWebJobSdkTriggerAttribute(this MethodInfo method)
|
||||
public static bool HasValidWebJobSdkTriggerAttribute(this MethodDefinition method)
|
||||
{
|
||||
var hasNoAutomaticTrigger = method.HasNoAutomaticTriggerAttribute();
|
||||
var hasTrigger = method.HasTriggerAttribute();
|
||||
return (hasNoAutomaticTrigger || hasTrigger) && !(hasNoAutomaticTrigger && hasTrigger);
|
||||
}
|
||||
|
||||
public static bool HasFunctionNameAttribute(this MethodInfo method)
|
||||
public static bool HasFunctionNameAttribute(this MethodDefinition method)
|
||||
{
|
||||
return method.GetCustomAttributesData().FirstOrDefault(d => d.AttributeType.FullName == "Microsoft.Azure.WebJobs.FunctionNameAttribute") != null;
|
||||
return method.CustomAttributes.FirstOrDefault(d => d.AttributeType.FullName == "Microsoft.Azure.WebJobs.FunctionNameAttribute") != null;
|
||||
}
|
||||
|
||||
public static bool HasNoAutomaticTriggerAttribute(this MethodInfo method)
|
||||
public static bool HasNoAutomaticTriggerAttribute(this MethodDefinition method)
|
||||
{
|
||||
return method.GetCustomAttributesData().FirstOrDefault(a => a.AttributeType.FullName == "Microsoft.Azure.WebJobs.NoAutomaticTriggerAttribute") != null;
|
||||
return method.CustomAttributes.FirstOrDefault(a => a.AttributeType.FullName == "Microsoft.Azure.WebJobs.NoAutomaticTriggerAttribute") != null;
|
||||
}
|
||||
|
||||
public static bool HasTriggerAttribute(this MethodInfo method)
|
||||
public static bool HasTriggerAttribute(this MethodDefinition method)
|
||||
{
|
||||
return method.GetParameters().Any(p => p.IsWebJobSdkTriggerParameter());
|
||||
return method.Parameters.Any(p => p.IsWebJobSdkTriggerParameter());
|
||||
}
|
||||
|
||||
public static JObject ManualTriggerBinding(this MethodInfo method)
|
||||
public static JObject ManualTriggerBinding(this MethodDefinition method)
|
||||
{
|
||||
var binding = new JObject { ["type"] = "manualTrigger", ["direction"] = "in" };
|
||||
var stringParameter = method.GetParameters().FirstOrDefault(p => p.ParameterType == typeof(string));
|
||||
var stringParameter = method.Parameters.FirstOrDefault(p => p.ParameterType.FullName == typeof(string).FullName);
|
||||
if (stringParameter != null)
|
||||
{
|
||||
binding["name"] = stringParameter.Name;
|
||||
|
@ -57,13 +57,13 @@ namespace MakeFunctionJson
|
|||
/// <param name="method">method to convert to a <see cref="FunctionJsonSchema"/> object. The method has to be <see cref="IsWebJobsSdkMethod(MethodInfo)"/> </param>
|
||||
/// <param name="assemblyPath">This will be the value of <see cref="FunctionJsonSchema.ScriptFile"/> on the returned value.</param>
|
||||
/// <returns><see cref="FunctionJsonSchema"/> object that represents the passed in <paramref name="method"/>.</returns>
|
||||
public static FunctionJsonSchema ToFunctionJson(this MethodInfo method, string assemblyPath)
|
||||
public static FunctionJsonSchema ToFunctionJson(this MethodDefinition method, string assemblyPath)
|
||||
{
|
||||
return new FunctionJsonSchema
|
||||
{
|
||||
// For every SDK parameter, convert it to a FunctionJson bindings.
|
||||
// Every parameter can potentially contain more than 1 attribute that will be converted into a binding object.
|
||||
Bindings = method.HasNoAutomaticTriggerAttribute() ? new[] { method.ManualTriggerBinding() } : method.GetParameters()
|
||||
Bindings = method.HasNoAutomaticTriggerAttribute() ? new[] { method.ManualTriggerBinding() } : method.Parameters
|
||||
.Where(p => p.IsWebJobSdkTriggerParameter())
|
||||
.Select(p => p.ToFunctionJsonBindings())
|
||||
.SelectMany(i => i)
|
||||
|
@ -82,17 +82,17 @@ namespace MakeFunctionJson
|
|||
/// </summary>
|
||||
/// <param name="method">method has to be a WebJobs SDK method. <see cref="IsWebJobsSdkMethod(MethodInfo)"/></param>
|
||||
/// <returns>Function name.</returns>
|
||||
public static string GetSdkFunctionName(this MethodInfo method)
|
||||
public static string GetSdkFunctionName(this MethodDefinition method)
|
||||
{
|
||||
if (!method.IsWebJobsSdkMethod())
|
||||
{
|
||||
throw new ArgumentException($"{nameof(method)} has to be a WebJob SDK function");
|
||||
}
|
||||
|
||||
var functionNameAttribute = method.GetCustomAttributesData().FirstOrDefault(a => a.AttributeType.Name == "FunctionNameAttribute")?.ConvertToAttribute();
|
||||
if (functionNameAttribute != null)
|
||||
string functionName = method.CustomAttributes.FirstOrDefault(a => a.AttributeType.Name == "FunctionNameAttribute")?.ConstructorArguments[0].Value.ToString();
|
||||
if (functionName != null)
|
||||
{
|
||||
return functionNameAttribute.GetType().GetProperty("Name").GetValue(functionNameAttribute).ToString();
|
||||
return functionName;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -107,13 +107,16 @@ namespace MakeFunctionJson
|
|||
/// </summary>
|
||||
/// <param name="method"></param>
|
||||
/// <returns>a boolean true or false if the outcome is fixed, a string if the ScriptHost should interpret it</returns>
|
||||
public static object GetDisabled(this MethodInfo method)
|
||||
public static object GetDisabled(this MethodDefinition method)
|
||||
{
|
||||
var attribute = method.GetParameters().Select(p => p.GetDisabledAttribute()).Where(a => a != null).FirstOrDefault() ??
|
||||
var customAttribute = method.Parameters.Select(p => p.GetDisabledAttribute()).Where(a => a != null).FirstOrDefault() ??
|
||||
method.GetDisabledAttribute() ??
|
||||
method.DeclaringType.GetTypeInfo().GetDisabledAttribute();
|
||||
if (attribute != null)
|
||||
method.DeclaringType.GetDisabledAttribute();
|
||||
|
||||
if (customAttribute != null)
|
||||
{
|
||||
var attribute = customAttribute.ToReflection();
|
||||
|
||||
// With a SettingName defined, just put that as string. The ScriptHost will evaluate it.
|
||||
var settingName = attribute.GetValue<string>("SettingName");
|
||||
if (!string.IsNullOrEmpty(settingName))
|
||||
|
@ -141,9 +144,9 @@ namespace MakeFunctionJson
|
|||
/// </summary>
|
||||
/// <param name="method"></param>
|
||||
/// <returns></returns>
|
||||
public static Attribute GetDisabledAttribute(this MethodInfo method)
|
||||
public static CustomAttribute GetDisabledAttribute(this MethodDefinition method)
|
||||
{
|
||||
return method.GetCustomAttributesData().FirstOrDefault(a => a.AttributeType.FullName == "Microsoft.Azure.WebJobs.DisableAttribute")?.ConvertToAttribute();
|
||||
return method.CustomAttributes.FirstOrDefault(a => a.AttributeType.FullName == "Microsoft.Azure.WebJobs.DisableAttribute");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -155,7 +158,7 @@ namespace MakeFunctionJson
|
|||
/// <param name="method"></param>
|
||||
/// <param name="error"></param>
|
||||
/// <returns></returns>
|
||||
public static bool HasUnsuportedAttributes(this MethodInfo method, out string error)
|
||||
public static bool HasUnsuportedAttributes(this MethodDefinition method, out string error)
|
||||
{
|
||||
error = string.Empty;
|
||||
var disabled = method.GetDisabled();
|
||||
|
|
|
@ -18,11 +18,13 @@
|
|||
<PackageReference Include="Newtonsoft.Json" Version="[11.0.2]" />
|
||||
<PackageReference Include="System.Runtime.Handles" Version="4.3.0" />
|
||||
<PackageReference Include="System.IO.FileSystem.Primitives" Version="4.3.0" />
|
||||
<PackageReference Include="Mono.Cecil" Version="0.11.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'net461'">
|
||||
<PackageReference Include="Newtonsoft.Json" Version="[9.0.1]" />
|
||||
<PackageReference Include="System.ValueTuple" Version="4.4.0" />
|
||||
<PackageReference Include="Mono.Cecil" Version="0.11.1" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Mono.Cecil;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
|
||||
namespace MakeFunctionJson
|
||||
{
|
||||
|
@ -13,10 +12,10 @@ namespace MakeFunctionJson
|
|||
/// </summary>
|
||||
/// <param name="parameterInfo"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsWebJobSdkTriggerParameter(this ParameterInfo parameterInfo)
|
||||
public static bool IsWebJobSdkTriggerParameter(this ParameterDefinition parameterInfo)
|
||||
{
|
||||
return parameterInfo
|
||||
.GetCustomAttributes()
|
||||
.CustomAttributes
|
||||
.Any(a => a.IsWebJobsAttribute() && a.ToAttributeFriendlyName().IndexOf("Trigger") > -1);
|
||||
}
|
||||
|
||||
|
@ -25,11 +24,10 @@ namespace MakeFunctionJson
|
|||
/// </summary>
|
||||
/// <param name="parameterInfo">Has to be a WebJobSdkParameter <see cref="IsWebJobsSdkParameter(ParameterInfo)"/></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<JObject> ToFunctionJsonBindings(this ParameterInfo parameterInfo)
|
||||
public static IEnumerable<JObject> ToFunctionJsonBindings(this ParameterDefinition parameterInfo)
|
||||
{
|
||||
|
||||
return parameterInfo
|
||||
.GetCustomAttributes()
|
||||
.CustomAttributes
|
||||
.Where(a => a.IsWebJobsAttribute()) // this has to return at least 1.
|
||||
.Select(a => TypeUtility.GetResolvedAttribute(parameterInfo, a)) // For IConnectionProvider logic.
|
||||
.Select(a => a.ToJObject()) // Convert the Attribute into a JObject.
|
||||
|
@ -47,9 +45,9 @@ namespace MakeFunctionJson
|
|||
/// </summary>
|
||||
/// <param name="parameterInfo"></param>
|
||||
/// <returns></returns>
|
||||
public static Attribute GetDisabledAttribute(this ParameterInfo parameterInfo)
|
||||
public static CustomAttribute GetDisabledAttribute(this ParameterDefinition parameterInfo)
|
||||
{
|
||||
return parameterInfo.GetCustomAttributes().FirstOrDefault(a => a.GetType().FullName == "Microsoft.Azure.WebJobs.DisableAttribute");
|
||||
return parameterInfo.CustomAttributes.FirstOrDefault(a => a.AttributeType.FullName == "Microsoft.Azure.WebJobs.DisableAttribute");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
#if NETCOREAPP2_1
|
||||
using System.Runtime.Loader;
|
||||
#endif
|
||||
using MakeFunctionJson;
|
||||
|
||||
namespace Microsoft.NET.Sdk.Functions.Console
|
||||
|
@ -19,7 +24,19 @@ namespace Microsoft.NET.Sdk.Functions.Console
|
|||
var assemblyPath = args[0].Trim();
|
||||
var outputPath = args[1].Trim();
|
||||
var functionsInDependencies = bool.Parse(args[2].Trim());
|
||||
var assemblyDir = Path.GetDirectoryName(assemblyPath);
|
||||
|
||||
#if NETCOREAPP2_1
|
||||
AssemblyLoadContext.Default.Resolving += (context, assemblyName) =>
|
||||
{
|
||||
return context.LoadFromAssemblyPath(Path.Combine(assemblyDir, assemblyName.Name + ".dll"));
|
||||
};
|
||||
#else
|
||||
AppDomain.CurrentDomain.AssemblyResolve += (sender, e) =>
|
||||
{
|
||||
return Assembly.LoadFrom(Path.Combine(assemblyDir, e.Name + ".dll"));
|
||||
};
|
||||
#endif
|
||||
IEnumerable<string> excludedFunctionNames = Enumerable.Empty<string>();
|
||||
|
||||
if (args.Length > 2)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Mono.Cecil;
|
||||
|
||||
namespace MakeFunctionJson
|
||||
{
|
||||
|
@ -11,9 +12,9 @@ namespace MakeFunctionJson
|
|||
return typeInfo.ImplementedInterfaces.Any(i => i.Name.Equals(interfaceName, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
public static Attribute GetDisabledAttribute(this TypeInfo type)
|
||||
public static CustomAttribute GetDisabledAttribute(this TypeDefinition type)
|
||||
{
|
||||
return type.GetCustomAttributes().FirstOrDefault(a => a.GetType().FullName == "Microsoft.Azure.WebJobs.DisableAttribute");
|
||||
return type.CustomAttributes.FirstOrDefault(a => a.AttributeType.FullName == "Microsoft.Azure.WebJobs.DisableAttribute");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
#if NETCOREAPP2_1
|
||||
using System.Runtime.Loader;
|
||||
#endif
|
||||
using Mono.Cecil;
|
||||
|
||||
namespace MakeFunctionJson
|
||||
{
|
||||
|
@ -15,7 +20,7 @@ namespace MakeFunctionJson
|
|||
/// </summary>
|
||||
/// <param name="parameter">The parameter to check.</param>
|
||||
/// <param name="attributeType">The attribute type to look for.</param>
|
||||
private static Attribute GetHierarchicalAttributeOrNull(ParameterInfo parameter, Type attributeType)
|
||||
private static CustomAttribute GetHierarchicalAttributeOrNull(ParameterDefinition parameter, Type attributeType)
|
||||
{
|
||||
if (parameter == null)
|
||||
{
|
||||
|
@ -28,7 +33,7 @@ namespace MakeFunctionJson
|
|||
return attribute;
|
||||
}
|
||||
|
||||
var method = parameter.Member as MethodInfo;
|
||||
var method = parameter.Method as MethodDefinition;
|
||||
if (method == null)
|
||||
{
|
||||
return null;
|
||||
|
@ -42,7 +47,7 @@ namespace MakeFunctionJson
|
|||
/// </summary>
|
||||
/// <param name="method">The method to check.</param>
|
||||
/// <param name="type">The attribute type to look for.</param>
|
||||
private static Attribute GetHierarchicalAttributeOrNull(MethodInfo method, Type type)
|
||||
private static CustomAttribute GetHierarchicalAttributeOrNull(Mono.Cecil.MethodDefinition method, Type type)
|
||||
{
|
||||
var attribute = method.GetCustomAttribute(type);
|
||||
if (attribute != null)
|
||||
|
@ -50,7 +55,7 @@ namespace MakeFunctionJson
|
|||
return attribute;
|
||||
}
|
||||
|
||||
attribute = method.DeclaringType.GetTypeInfo().GetCustomAttribute(type);
|
||||
attribute = method.DeclaringType.GetCustomAttribute(type);
|
||||
if (attribute != null)
|
||||
{
|
||||
return attribute;
|
||||
|
@ -59,8 +64,10 @@ namespace MakeFunctionJson
|
|||
return null;
|
||||
}
|
||||
|
||||
internal static Attribute GetResolvedAttribute(ParameterInfo parameter, Attribute attribute)
|
||||
internal static Attribute GetResolvedAttribute(ParameterDefinition parameter, CustomAttribute customAttribute)
|
||||
{
|
||||
Attribute attribute = customAttribute.ToReflection();
|
||||
|
||||
if (attribute != null &&
|
||||
attribute.GetType().GetTypeInfo().IsImplementing("IConnectionProvider") &&
|
||||
string.IsNullOrEmpty(attribute.GetValue<string>("Connection")))
|
||||
|
@ -75,14 +82,14 @@ namespace MakeFunctionJson
|
|||
|
||||
if (connectionProviderAttribute?.GetValue<Type>("ProviderType") != null)
|
||||
{
|
||||
var connectionOverrideProvider = GetHierarchicalAttributeOrNull(parameter, connectionProviderAttribute.GetValue<Type>("ProviderType"));
|
||||
var connectionOverrideProvider = GetHierarchicalAttributeOrNull(parameter, connectionProviderAttribute.GetValue<Type>("ProviderType"))?.ToReflection();
|
||||
if (connectionOverrideProvider != null &&
|
||||
connectionOverrideProvider.GetType().GetTypeInfo().IsImplementing("IConnectionProvider"))
|
||||
{
|
||||
var iConnectionProvider = connectionOverrideProvider.GetType().GetTypeInfo().GetInterface("IConnectionProvider");
|
||||
var propertyInfo = iConnectionProvider.GetProperty("Connection");
|
||||
var connectionValue = (string) propertyInfo.GetValue(attribute);
|
||||
connectionValue = connectionValue
|
||||
var connectionValue = (string)propertyInfo.GetValue(attribute);
|
||||
connectionValue = connectionValue
|
||||
?? connectionOverrideProvider.GetValue<string>("Connection")
|
||||
?? connectionOverrideProvider.GetValue<string>("Account");
|
||||
if (!string.IsNullOrEmpty(connectionValue))
|
||||
|
@ -95,5 +102,74 @@ namespace MakeFunctionJson
|
|||
|
||||
return attribute;
|
||||
}
|
||||
|
||||
public static Attribute ToReflection(this CustomAttribute customAttribute)
|
||||
{
|
||||
var attributeType = customAttribute.AttributeType.ToReflectionType();
|
||||
|
||||
Type[] constructorParams = customAttribute.Constructor.Parameters
|
||||
.Select(p => p.ParameterType.ToReflectionType())
|
||||
.ToArray();
|
||||
|
||||
Attribute attribute = attributeType.GetConstructor(constructorParams)
|
||||
.Invoke(customAttribute.ConstructorArguments.Select(p => NormalizeArg(p)).ToArray()) as Attribute;
|
||||
|
||||
foreach (var namedArgument in customAttribute.Properties)
|
||||
{
|
||||
attributeType.GetProperty(namedArgument.Name)?.SetValue(attribute, namedArgument.Argument.Value);
|
||||
attributeType.GetField(namedArgument.Name)?.SetValue(attribute, namedArgument.Argument.Value);
|
||||
}
|
||||
|
||||
return attribute;
|
||||
}
|
||||
|
||||
public static Type ToReflectionType(this TypeReference typeDef)
|
||||
{
|
||||
Type t = Type.GetType(typeDef.GetReflectionFullName());
|
||||
|
||||
if (t == null)
|
||||
{
|
||||
#if NETCOREAPP2_1
|
||||
Assembly a = AssemblyLoadContext.Default.LoadFromAssemblyPath(Path.GetFullPath(typeDef.Resolve().Module.FileName));
|
||||
#else
|
||||
Assembly a = Assembly.LoadFrom(Path.GetFullPath(typeDef.Resolve().Module.FileName));
|
||||
#endif
|
||||
t = a.GetType(typeDef.GetReflectionFullName());
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
private static object NormalizeArg(CustomAttributeArgument arg)
|
||||
{
|
||||
if (arg.Type.IsArray)
|
||||
{
|
||||
var arguments = arg.Value as CustomAttributeArgument[];
|
||||
Type arrayType = arg.Type.GetElementType().ToReflectionType();
|
||||
var array = Array.CreateInstance(arrayType, arguments.Length);
|
||||
for (int i = 0; i < array.Length; i++)
|
||||
{
|
||||
array.SetValue(arguments[i].Value, i);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
if (arg.Value is TypeDefinition typeDef)
|
||||
{
|
||||
return typeDef.ToReflectionType();
|
||||
}
|
||||
|
||||
return arg.Value;
|
||||
}
|
||||
|
||||
public static CustomAttribute GetCustomAttribute(this Mono.Cecil.ICustomAttributeProvider provider, Type parameterType)
|
||||
{
|
||||
return provider.CustomAttributes.SingleOrDefault(p => p.AttributeType.FullName == parameterType.FullName);
|
||||
}
|
||||
|
||||
public static string GetReflectionFullName(this TypeReference typeRef)
|
||||
{
|
||||
return typeRef.FullName.Replace("/", "+");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ namespace Microsoft.NET.Sdk.Functions.Tasks
|
|||
public bool GenerateHostJson { get; set; }
|
||||
|
||||
public ITaskItem[] UserProvidedFunctionJsonFiles { get; set; }
|
||||
|
||||
|
||||
public bool FunctionsInDependencies { get; set; }
|
||||
|
||||
public override bool Execute()
|
||||
|
@ -44,7 +44,7 @@ namespace Microsoft.NET.Sdk.Functions.Tasks
|
|||
}
|
||||
|
||||
string taskAssemblyDirectory = Path.GetDirectoryName(typeof(GenerateFunctions).GetTypeInfo().Assembly.Location);
|
||||
string baseDirectory = Path.GetDirectoryName(taskAssemblyDirectory);
|
||||
string baseDirectory = Path.GetDirectoryName(taskAssemblyDirectory);
|
||||
ProcessStartInfo processStartInfo = null;
|
||||
#if NET46
|
||||
processStartInfo = GetProcessStartInfo(baseDirectory, isCore: false);
|
||||
|
@ -68,9 +68,9 @@ namespace Microsoft.NET.Sdk.Functions.Tasks
|
|||
var output = process.StandardOutput.ReadToEnd();
|
||||
var error = process.StandardError.ReadToEnd();
|
||||
process.WaitForExit();
|
||||
|
||||
if (!string.IsNullOrEmpty(output))
|
||||
{
|
||||
|
||||
if (!string.IsNullOrEmpty(output))
|
||||
{
|
||||
Log.LogWarning(output);
|
||||
}
|
||||
|
||||
|
@ -111,7 +111,7 @@ namespace Microsoft.NET.Sdk.Functions.Tasks
|
|||
RedirectStandardOutput = true,
|
||||
WorkingDirectory = workingDirectory,
|
||||
FileName = exePath,
|
||||
Arguments = arguments
|
||||
Arguments = arguments
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
using FluentAssertions;
|
||||
using MakeFunctionJson;
|
||||
using Microsoft.Azure.WebJobs;
|
||||
using Mono.Cecil;
|
||||
using Xunit;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Microsoft.NET.Sdk.Functions.Test
|
||||
{
|
||||
|
@ -53,8 +54,8 @@ namespace Microsoft.NET.Sdk.Functions.Test
|
|||
[InlineData(typeof(FunctionsClass3), "Run", false)]
|
||||
public void MethodsWithDisabledParametersShouldBeDisabled(Type type, string methodName, object expectedIsDisabled)
|
||||
{
|
||||
var method = type.GetMethod(methodName);
|
||||
var funcJson = method.ToFunctionJson(string.Empty);
|
||||
MethodDefinition methodDef = TestUtility.GetMethodDefinition(type, methodName);
|
||||
FunctionJsonSchema funcJson = methodDef.ToFunctionJson(string.Empty);
|
||||
funcJson.Disabled.Should().Be(expectedIsDisabled);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
using FluentAssertions;
|
||||
using FluentAssertions.Json;
|
||||
using MakeFunctionJson;
|
||||
using Microsoft.Azure.WebJobs.ServiceBus;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.NET.Sdk.Functions.Test
|
||||
{
|
||||
public class EventHubAttributeTests
|
||||
{
|
||||
[Fact]
|
||||
public static void EventHubTriggerAttribute_ShouldHaveV1vsV2Differences()
|
||||
{
|
||||
var attribute = new EventHubTriggerAttribute("eventHub");
|
||||
|
||||
var jObject = attribute.ToJObject();
|
||||
|
||||
#if NET46
|
||||
jObject.Should().HaveElement("path");
|
||||
jObject["path"].Should().Be("eventHub");
|
||||
#else
|
||||
jObject.Should().HaveElement("type");
|
||||
jObject["type"].Should().Be("eventHubTrigger");
|
||||
|
||||
jObject.Should().HaveElement("path");
|
||||
jObject["path"].Should().Be("eventHub");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,9 +4,8 @@ using System.Linq;
|
|||
using System.Net.Http;
|
||||
using FluentAssertions;
|
||||
using MakeFunctionJson;
|
||||
using Microsoft.Azure.EventHubs;
|
||||
using Microsoft.Azure.WebJobs;
|
||||
using Microsoft.Azure.WebJobs.ServiceBus;
|
||||
using Microsoft.ServiceBus.Messaging;
|
||||
using Microsoft.WindowsAzure.Storage.Queue;
|
||||
using Xunit;
|
||||
|
||||
|
@ -18,25 +17,25 @@ namespace Microsoft.NET.Sdk.Functions.Test
|
|||
{
|
||||
[FunctionName("MyHttpTrigger")]
|
||||
public static void Run1([HttpTrigger] HttpRequestMessage request) { }
|
||||
|
||||
|
||||
[FunctionName("MyBlobTrigger")]
|
||||
public static void Run2([BlobTrigger("blob.txt")] string blobContent) { }
|
||||
|
||||
|
||||
[FunctionName("MyQueueTrigger")]
|
||||
public static void Run3([QueueTrigger("queue")] CloudQueue queue) { }
|
||||
|
||||
|
||||
[FunctionName("MyEventHubTrigger")]
|
||||
public static void Run4([EventHubTrigger("hub")] EventData message) { }
|
||||
|
||||
|
||||
[FunctionName("MyTimerTrigger")]
|
||||
public static void Run5([TimerTrigger("00:30:00")] TimerInfo timer) { }
|
||||
|
||||
|
||||
[FunctionName("MyServiceBusTrigger")]
|
||||
public static void Run6([ServiceBusTrigger("queue")] string message) { }
|
||||
|
||||
|
||||
[FunctionName("MyManualTrigger"), NoAutomaticTrigger]
|
||||
public static void Run7(string input) { }
|
||||
|
||||
|
||||
[FunctionName("MyManualTriggerWithoutParameters"), NoAutomaticTrigger]
|
||||
public static void Run8() { }
|
||||
}
|
||||
|
@ -53,16 +52,16 @@ namespace Microsoft.NET.Sdk.Functions.Test
|
|||
public void FunctionMethodsAreExported(string functionName, string type, string parameterName)
|
||||
{
|
||||
var logger = new RecorderLogger();
|
||||
var converter = new FunctionJsonConverter(logger, ".", ".", functionsInDependencies:false);
|
||||
var functions = converter.GenerateFunctions(new [] {typeof(FunctionsClass)});
|
||||
var converter = new FunctionJsonConverter(logger, ".", ".", functionsInDependencies: false);
|
||||
var functions = converter.GenerateFunctions(new[] { TestUtility.GetTypeDefinition(typeof(FunctionsClass)) });
|
||||
var schema = functions.Single(e => Path.GetFileName(e.Value.outputFile.DirectoryName) == functionName).Value.schema;
|
||||
var binding = schema.Bindings.Single();
|
||||
var binding = schema.Bindings.Single();
|
||||
binding.Value<string>("type").Should().Be(type);
|
||||
binding.Value<string>("name").Should().Be(parameterName);
|
||||
logger.Errors.Should().BeEmpty();
|
||||
logger.Warnings.Should().BeEmpty();
|
||||
}
|
||||
|
||||
|
||||
public class InvalidFunctionBecauseOfMissingTrigger
|
||||
{
|
||||
[FunctionName("MyServiceBusTrigger")]
|
||||
|
@ -79,7 +78,7 @@ namespace Microsoft.NET.Sdk.Functions.Test
|
|||
[FunctionName("MyServiceBusTrigger"), NoAutomaticTrigger]
|
||||
public static void Run([ServiceBusTrigger("queue")] string message) { }
|
||||
}
|
||||
|
||||
|
||||
[Theory]
|
||||
[InlineData(typeof(InvalidFunctionBecauseOfMissingTrigger), "Method Microsoft.NET.Sdk.Functions.Test.FunctionJsonConverterTests+InvalidFunctionBecauseOfMissingTrigger.Run is missing a trigger attribute. Both a trigger attribute and FunctionName attribute are required for an Azure function definition.")]
|
||||
// [InlineData(typeof(InvalidFunctionBecauseOfMissingFunctionName), "Method Microsoft.NET.Sdk.Functions.Test.FunctionJsonConverterTests+InvalidFunctionBecauseOfMissingFunctionName.Run is missing the 'FunctionName' attribute. Both a trigger attribute and 'FunctionName' are required for an Azure function definition.")]
|
||||
|
@ -87,8 +86,8 @@ namespace Microsoft.NET.Sdk.Functions.Test
|
|||
public void InvalidFunctionMethodProducesWarning(Type type, string warningMessage)
|
||||
{
|
||||
var logger = new RecorderLogger();
|
||||
var converter = new FunctionJsonConverter(logger, ".", ".", functionsInDependencies:false);
|
||||
var functions = converter.GenerateFunctions(new [] {type});
|
||||
var converter = new FunctionJsonConverter(logger, ".", ".", functionsInDependencies: false);
|
||||
var functions = converter.GenerateFunctions(new[] { TestUtility.GetTypeDefinition(type) });
|
||||
functions.Should().BeEmpty();
|
||||
logger.Errors.Should().BeEmpty();
|
||||
logger.Warnings.Should().ContainSingle();
|
||||
|
|
|
@ -1,26 +1,32 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FluentAssertions;
|
||||
using MakeFunctionJson;
|
||||
using Microsoft.Azure.WebJobs;
|
||||
using Microsoft.Azure.WebJobs.ServiceBus;
|
||||
using Mono.Cecil;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.NET.Sdk.Functions.Test
|
||||
{
|
||||
public class GeneralWebJobsAttributesTests
|
||||
{
|
||||
private static void FakeFunction(
|
||||
[QueueTrigger("a")]
|
||||
[BlobTrigger("b")]
|
||||
[EventHubTrigger("c")]
|
||||
[ServiceBusTrigger("d")] string abcd)
|
||||
{
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> GetAttributes()
|
||||
{
|
||||
yield return new object[] { new QueueTriggerAttribute(string.Empty) };
|
||||
yield return new object[] { new BlobTriggerAttribute(string.Empty) };
|
||||
yield return new object[] { new EventHubTriggerAttribute(string.Empty) };
|
||||
yield return new object[] { new ServiceBusTriggerAttribute(string.Empty) };
|
||||
return TestUtility.GetCustomAttributes(typeof(GeneralWebJobsAttributesTests), "FakeFunction", "abcd")
|
||||
.Select(p => new object[] { p });
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(GetAttributes))]
|
||||
public void IsWebJobsAttribute(Attribute attribute)
|
||||
public void IsWebJobsAttribute(CustomAttribute attribute)
|
||||
{
|
||||
attribute.IsWebJobsAttribute().Should().BeTrue(because: $"{attribute.GetType().FullName} is a WebJob's attribute");
|
||||
}
|
||||
|
|
|
@ -1,12 +1,8 @@
|
|||
using FluentAssertions;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using FluentAssertions;
|
||||
using MakeFunctionJson;
|
||||
using Microsoft.Azure.WebJobs;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.NET.Sdk.Functions.Test
|
||||
|
@ -43,7 +39,7 @@ namespace Microsoft.NET.Sdk.Functions.Test
|
|||
[InlineData(typeof(FunctionsClass1), "Run6", true)]
|
||||
public void HasUnsupportedAttributesWorksCorrectly(Type type, string methodName, bool expected)
|
||||
{
|
||||
var method = type.GetMethod(methodName);
|
||||
var method = TestUtility.GetMethodDefinition(type, methodName);
|
||||
var hasUnsuportedAttribute = method.HasUnsuportedAttributes(out string _);
|
||||
hasUnsuportedAttribute.Should().Be(expected);
|
||||
}
|
||||
|
|
|
@ -3,8 +3,6 @@ using FluentAssertions.Json;
|
|||
using MakeFunctionJson;
|
||||
using Microsoft.Azure.WebJobs;
|
||||
using Microsoft.Azure.WebJobs.Extensions.Http;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.NET.Sdk.Functions.Test
|
||||
|
@ -22,18 +20,5 @@ namespace Microsoft.NET.Sdk.Functions.Test
|
|||
jObject.Should().HaveElement("authLevel");
|
||||
jObject["authLevel"].Should().Be("function");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HttpTriggerAttributeWithWebHookTypeShouldntHaveAnAuthLevel()
|
||||
{
|
||||
var attribute = new HttpTriggerAttribute()
|
||||
{
|
||||
WebHookType = "something"
|
||||
};
|
||||
|
||||
var jObject = attribute.ToJObject();
|
||||
|
||||
jObject["authLevel"].Should().BeNull();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,8 +55,10 @@ namespace Microsoft.NET.Sdk.Functions.Test
|
|||
[InlineData(typeof(FunctionsClass4), "foobarfoobar")]
|
||||
public void TestIConnectionProviderHierarchicalLogic(Type type, string expected)
|
||||
{
|
||||
var parameterInfo = type.GetMethod("Run").GetParameters().First();
|
||||
var attribute = (Attribute) parameterInfo.GetCustomAttributes(typeof(QueueTriggerAttribute), false).First();
|
||||
var method = TestUtility.GetMethodDefinition(type, "Run");
|
||||
|
||||
var parameterInfo = method.Parameters.First();
|
||||
var attribute = parameterInfo.GetCustomAttribute(typeof(QueueTriggerAttribute));
|
||||
|
||||
var resolvedAttribute = TypeUtility.GetResolvedAttribute(parameterInfo, attribute);
|
||||
|
||||
|
|
|
@ -125,12 +125,21 @@
|
|||
<Reference Include="Microsoft.WindowsAzure.Storage, Version=8.6.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\WindowsAzure.Storage.8.6.0\lib\net45\Microsoft.WindowsAzure.Storage.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Mono.Cecil, Version=0.11.1.0, Culture=neutral, PublicKeyToken=50cebf1cceb9d05e, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Mono.Cecil.0.11.1\lib\net40\Mono.Cecil.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Mono.Cecil.Mdb, Version=0.11.1.0, Culture=neutral, PublicKeyToken=50cebf1cceb9d05e, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Mono.Cecil.0.11.1\lib\net40\Mono.Cecil.Mdb.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Mono.Cecil.Pdb, Version=0.11.1.0, Culture=neutral, PublicKeyToken=50cebf1cceb9d05e, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Mono.Cecil.0.11.1\lib\net40\Mono.Cecil.Pdb.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Mono.Cecil.Rocks, Version=0.11.1.0, Culture=neutral, PublicKeyToken=50cebf1cceb9d05e, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Mono.Cecil.0.11.1\lib\net40\Mono.Cecil.Rocks.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NCrontab, Version=3.2.20120.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\ncrontab.3.3.0\lib\net35\NCrontab.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=11.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NSubstitute, Version=2.0.3.0, Culture=neutral, PublicKeyToken=92dd2e9066daa5ca, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\NSubstitute.2.0.3\lib\net45\NSubstitute.dll</HintPath>
|
||||
</Reference>
|
||||
|
@ -225,18 +234,22 @@
|
|||
<Reference Include="xunit.execution.desktop, Version=2.3.1.3858, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\xunit.extensibility.execution.2.3.1\lib\net452\xunit.execution.desktop.dll</HintPath>
|
||||
</Reference>
|
||||
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.EventHubs" Version="3.0.6" />
|
||||
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.ServiceBus" Version="3.2.0" />
|
||||
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.Storage" Version="3.0.10" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
|
||||
<PackageReference Include="Microsoft.Azure.WebJobs" Version="3.0.14" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="DisableAttributeTests.cs" />
|
||||
<Compile Include="EventHubAttributeTests.cs" />
|
||||
<Compile Include="GeneralWebJobsAttributesTests.cs" />
|
||||
<Compile Include="HasUnsupportedAttributesTests.cs" />
|
||||
<Compile Include="HttpTriggerTests.cs" />
|
||||
<Compile Include="IConnectionProviderTests.cs" />
|
||||
<Compile Include="FunctionJsonConverterTests.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="ServiceBusTriggerTests.cs" />
|
||||
<Compile Include="RecorderLogger.cs" />
|
||||
<Compile Include="TestUtility.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="app.config" />
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
using FluentAssertions;
|
||||
using FluentAssertions.Json;
|
||||
using MakeFunctionJson;
|
||||
using Microsoft.Azure.WebJobs;
|
||||
using Microsoft.ServiceBus.Messaging;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.NET.Sdk.Functions.Test
|
||||
{
|
||||
public class ServiceBusTriggerTests
|
||||
{
|
||||
[Fact]
|
||||
// https://github.com/Azure/azure-functions-vs-build-sdk/issues/1
|
||||
public void ServiceBusTriggerShouldHaveStringEnumForAccessRights()
|
||||
{
|
||||
var attribute = new ServiceBusTriggerAttribute("queue1", AccessRights.Manage);
|
||||
|
||||
var jObject = attribute.ToJObject();
|
||||
|
||||
jObject.Should().HaveElement("accessRights");
|
||||
jObject["accessRights"].Should().Be("manage");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Mono.Cecil;
|
||||
|
||||
namespace Microsoft.NET.Sdk.Functions.Test
|
||||
{
|
||||
public static class TestUtility
|
||||
{
|
||||
public static MethodDefinition GetMethodDefinition(Type type, string methodName)
|
||||
{
|
||||
return GetTypeDefinition(type).Methods.SingleOrDefault(p => p.Name == methodName);
|
||||
}
|
||||
|
||||
public static TypeDefinition GetTypeDefinition(Type type)
|
||||
{
|
||||
var module = ModuleDefinition.ReadModule(type.Assembly.Location);
|
||||
return module.GetType(type.FullName.Replace("+", "/"));
|
||||
}
|
||||
|
||||
public static IEnumerable<CustomAttribute> GetCustomAttributes(Type type, string methodName, string parameterName)
|
||||
{
|
||||
var methodDef = GetMethodDefinition(type, methodName);
|
||||
var paramDef = methodDef.Parameters.SingleOrDefault(p => p.Name == parameterName);
|
||||
return paramDef.CustomAttributes;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -28,6 +28,7 @@
|
|||
<package id="Microsoft.NETCore.Platforms" version="1.1.0" targetFramework="net46" />
|
||||
<package id="Microsoft.Tpl.Dataflow" version="4.5.24" targetFramework="net46" />
|
||||
<package id="Microsoft.Win32.Primitives" version="4.3.0" targetFramework="net46" />
|
||||
<package id="Mono.Cecil" version="0.11.1" targetFramework="net461" />
|
||||
<package id="ncrontab" version="3.3.0" targetFramework="net46" />
|
||||
<package id="NETStandard.Library" version="1.6.1" targetFramework="net46" />
|
||||
<package id="Newtonsoft.Json" version="11.0.2" targetFramework="net461" />
|
||||
|
|
Загрузка…
Ссылка в новой задаче