Build extensions separately
This commit adds a new set of extensions that will be referenced by function code. These new worker extensions have the WebJobs corresponding extension in an attribute. During build, the Sdk package collects the WebJobs extension information and performs a separate build. This helps keep the extension package from Host separate from user code for worker.
This commit is contained in:
Родитель
f3727843a2
Коммит
e4096d8748
|
@ -33,7 +33,19 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
|||
.editorconfig = .editorconfig
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestUtility", "test\TestUtility\TestUtility.csproj", "{C30B77A7-4085-422E-AADE-A4322989F5F8}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestUtility", "test\TestUtility\TestUtility.csproj", "{C30B77A7-4085-422E-AADE-A4322989F5F8}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "extensions", "extensions", "{A7B4FF1E-3DF7-4F28-9333-D0961CDDF702}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Worker.Extensions.Http", "extensions\Worker.Extensions.Http\Worker.Extensions.Http.csproj", "{1E601406-6923-4CB9-AAD7-928E08906B10}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Worker.Extensions.Abstractions", "extensions\Worker.Extensions.Abstractions\Worker.Extensions.Abstractions.csproj", "{B5F66802-0968-4B99-8E97-E42C744CE5CE}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Worker.Extensions.Storage", "extensions\Worker.Extensions.Storage\Worker.Extensions.Storage.csproj", "{D6EAB0C1-491C-4723-B1F3-B6F5461CD359}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Worker.Extensions.Timer", "extensions\Worker.Extensions.Timer\Worker.Extensions.Timer.csproj", "{4DBEC557-E5CC-41E9-9319-BC02615CF228}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Worker.Extensions.CosmosDB", "extensions\Worker.Extensions.CosmosDB\Worker.Extensions.CosmosDB.csproj", "{37F71B56-1FC7-4BEC-9AB4-FF57C6BF2882}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
|
@ -73,6 +85,26 @@ Global
|
|||
{C30B77A7-4085-422E-AADE-A4322989F5F8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C30B77A7-4085-422E-AADE-A4322989F5F8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C30B77A7-4085-422E-AADE-A4322989F5F8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{1E601406-6923-4CB9-AAD7-928E08906B10}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1E601406-6923-4CB9-AAD7-928E08906B10}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1E601406-6923-4CB9-AAD7-928E08906B10}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1E601406-6923-4CB9-AAD7-928E08906B10}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B5F66802-0968-4B99-8E97-E42C744CE5CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B5F66802-0968-4B99-8E97-E42C744CE5CE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B5F66802-0968-4B99-8E97-E42C744CE5CE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B5F66802-0968-4B99-8E97-E42C744CE5CE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D6EAB0C1-491C-4723-B1F3-B6F5461CD359}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D6EAB0C1-491C-4723-B1F3-B6F5461CD359}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D6EAB0C1-491C-4723-B1F3-B6F5461CD359}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D6EAB0C1-491C-4723-B1F3-B6F5461CD359}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4DBEC557-E5CC-41E9-9319-BC02615CF228}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4DBEC557-E5CC-41E9-9319-BC02615CF228}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4DBEC557-E5CC-41E9-9319-BC02615CF228}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4DBEC557-E5CC-41E9-9319-BC02615CF228}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{37F71B56-1FC7-4BEC-9AB4-FF57C6BF2882}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{37F71B56-1FC7-4BEC-9AB4-FF57C6BF2882}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{37F71B56-1FC7-4BEC-9AB4-FF57C6BF2882}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{37F71B56-1FC7-4BEC-9AB4-FF57C6BF2882}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -85,6 +117,11 @@ Global
|
|||
{6B64FE04-BA90-49FC-893A-DF12EF15280C} = {4B0D77E7-FA83-4FDD-9E67-CC95EAD21348}
|
||||
{BFB2832E-C3AB-4F09-B285-B24E535EC858} = {FD7243E4-BF18-43F8-8744-BA1D17ACF378}
|
||||
{C30B77A7-4085-422E-AADE-A4322989F5F8} = {FD7243E4-BF18-43F8-8744-BA1D17ACF378}
|
||||
{1E601406-6923-4CB9-AAD7-928E08906B10} = {A7B4FF1E-3DF7-4F28-9333-D0961CDDF702}
|
||||
{B5F66802-0968-4B99-8E97-E42C744CE5CE} = {A7B4FF1E-3DF7-4F28-9333-D0961CDDF702}
|
||||
{D6EAB0C1-491C-4723-B1F3-B6F5461CD359} = {A7B4FF1E-3DF7-4F28-9333-D0961CDDF702}
|
||||
{4DBEC557-E5CC-41E9-9319-BC02615CF228} = {A7B4FF1E-3DF7-4F28-9333-D0961CDDF702}
|
||||
{37F71B56-1FC7-4BEC-9AB4-FF57C6BF2882} = {A7B4FF1E-3DF7-4F28-9333-D0961CDDF702}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {497D2ED4-A13E-4BCA-8D29-F30CA7D0EA4A}
|
||||
|
|
|
@ -37,6 +37,8 @@ steps:
|
|||
|
||||
- pwsh: ./setup-e2e-tests.ps1
|
||||
displayName: "Setup E2E tests"
|
||||
env:
|
||||
CORE_TOOLS_URL: $(CORE_TOOLS_URL)
|
||||
|
||||
- task: DotNetCoreCLI@2
|
||||
displayName: 'Run tests'
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.Azure.Functions.Worker.Extensions.Abstractions
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public abstract class BindingAttribute : Attribute
|
||||
{
|
||||
public BindingAttribute()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.Azure.Functions.Worker.Extensions.Abstractions
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Assembly)]
|
||||
public class ExtensionInformationAttribute : Attribute
|
||||
{
|
||||
public string ExtensionPackage { get; }
|
||||
|
||||
public string ExtensionVersion { get; }
|
||||
|
||||
public ExtensionInformationAttribute(string extensionPackage, string extensionVersion)
|
||||
{
|
||||
ExtensionPackage = extensionPackage;
|
||||
ExtensionVersion = extensionVersion;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.Azure.Functions.Worker.Extensions.Abstractions
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class FunctionNameAttribute : Attribute
|
||||
{
|
||||
public FunctionNameAttribute(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
|
||||
public string Name { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.Azure.Functions.Worker.Extensions.Abstractions
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Parameter)]
|
||||
public abstract class InputBindingAttribute : BindingAttribute
|
||||
{
|
||||
public InputBindingAttribute()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.Azure.Functions.Worker.Extensions.Abstractions
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public abstract class OutputBindingAttribute : BindingAttribute
|
||||
{
|
||||
public OutputBindingAttribute(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
|
||||
public string Name { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.Azure.Functions.Worker.Extensions.Abstractions
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Parameter)]
|
||||
public abstract class TriggerBindingAttribute : BindingAttribute
|
||||
{
|
||||
public TriggerBindingAttribute()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<Import Project="..\..\build\Common.props" />
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<AssemblyName>Microsoft.Azure.Functions.Worker.Extensions.Abstractions</AssemblyName>
|
||||
<RootNamespace>Microsoft.Azure.Functions.Worker.Extensions.Abstractions</RootNamespace>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>..\..\key.snk</AssemblyOriginatorKeyFile>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,103 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.Azure.Functions.Worker.Extensions.Abstractions;
|
||||
|
||||
namespace Microsoft.Azure.Functions.Worker.Extensions.CosmosDB
|
||||
{
|
||||
public class CosmosDBOutputAttribute : OutputBindingAttribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructs a new instance.
|
||||
/// </summary>
|
||||
/// <param name="databaseName">The CosmosDB database name.</param>
|
||||
/// <param name="collectionName">The CosmosDB collection name.</param>
|
||||
public CosmosDBOutputAttribute(string name, string databaseName, string collectionName) : base(name)
|
||||
{
|
||||
DatabaseName = databaseName;
|
||||
CollectionName = collectionName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The name of the database to which the parameter applies.
|
||||
/// May include binding parameters.
|
||||
/// </summary>
|
||||
public string DatabaseName { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the collection to which the parameter applies.
|
||||
/// May include binding parameters.
|
||||
/// </summary>
|
||||
public string CollectionName { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional.
|
||||
/// Only applies to output bindings.
|
||||
/// If true, the database and collection will be automatically created if they do not exist.
|
||||
/// </summary>
|
||||
public bool CreateIfNotExists { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional. A string value indicating the app setting to use as the CosmosDB connection string, if different
|
||||
/// than the one specified in the <see cref="CosmosDBOptions"/>.
|
||||
/// </summary>
|
||||
public string? ConnectionStringSetting { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional. The Id of the document to retrieve from the collection.
|
||||
/// May include binding parameters.
|
||||
/// </summary>
|
||||
public string? Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional.
|
||||
/// When specified on an output binding and <see cref="CreateIfNotExists"/> is true, defines the partition key
|
||||
/// path for the created collection.
|
||||
/// When specified on an input binding, specifies the partition key value for the lookup.
|
||||
/// May include binding parameters.
|
||||
/// </summary>
|
||||
public string? PartitionKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional.
|
||||
/// When specified on an output binding and <see cref="CreateIfNotExists"/> is true, defines the throughput of the created
|
||||
/// collection.
|
||||
/// </summary>
|
||||
public int CollectionThroughput { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional.
|
||||
/// When specified on an input binding using an <see cref="System.Collections.Generic.IEnumerable{T}"/>, defines the query to run against the collection.
|
||||
/// May include binding parameters.
|
||||
/// </summary>
|
||||
public string? SqlQuery { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional.
|
||||
/// Enable to use with Multi Master accounts.
|
||||
/// </summary>
|
||||
public bool UseMultipleWriteLocations { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional.
|
||||
/// Enables the use of JsonConvert.DefaultSettings in the monitored Azure Cosmos DB collection.
|
||||
/// <remarks>
|
||||
/// This setting only applies to the monitored collection and the consumer to setup the serialization used in the monitored collection.
|
||||
/// The JsonConvert.DefaultSettings must be set during the initialization process.
|
||||
/// This is achieved by deriving a class from <see cref="CosmosDBWebJobsStartup"/> and adding a <see cref="WebJobsStartupAttribute"/>
|
||||
/// to the assembly that specifies the derived class
|
||||
/// </remarks>
|
||||
/// </summary>
|
||||
public bool UseDefaultJsonSerialization { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional.
|
||||
/// Defines preferred locations (regions) for geo-replicated database accounts in the Azure Cosmos DB service.
|
||||
/// Values should be comma-separated.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// PreferredLocations = "East US,South Central US,North Europe"
|
||||
/// </example>
|
||||
public string? PreferredLocations { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,159 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.Azure.Functions.Worker.Extensions.Abstractions;
|
||||
|
||||
namespace Microsoft.Azure.Functions.Worker.Extensions.CosmosDB
|
||||
{
|
||||
public sealed class CosmosDBTriggerAttribute : TriggerBindingAttribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Triggers an event when changes occur on a monitored collection
|
||||
/// </summary>
|
||||
/// <param name="databaseName">Name of the database of the collection to monitor for changes</param>
|
||||
/// <param name="collectionName">Name of the collection to monitor for changes</param>
|
||||
public CosmosDBTriggerAttribute(string databaseName, string collectionName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(collectionName))
|
||||
{
|
||||
throw new ArgumentException("Missing information for the collection to monitor", "collectionName");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(databaseName))
|
||||
{
|
||||
throw new ArgumentException("Missing information for the collection to monitor", "databaseName");
|
||||
}
|
||||
|
||||
CollectionName = collectionName;
|
||||
DatabaseName = databaseName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Connection string for the service containing the collection to monitor
|
||||
/// </summary>
|
||||
public string? ConnectionStringSetting { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Name of the collection to monitor for changes
|
||||
/// </summary>
|
||||
public string CollectionName { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Name of the database containing the collection to monitor for changes
|
||||
/// </summary>
|
||||
public string DatabaseName { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Connection string for the service containing the lease collection
|
||||
/// </summary>
|
||||
public string? LeaseConnectionStringSetting { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Name of the lease collection. Default value is "leases"
|
||||
/// </summary>
|
||||
public string? LeaseCollectionName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Name of the database containing the lease collection
|
||||
/// </summary>
|
||||
public string? LeaseDatabaseName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional.
|
||||
/// Only applies to lease collection.
|
||||
/// If true, the database and collection for leases will be automatically created if it does not exist.
|
||||
/// </summary>
|
||||
public bool CreateLeaseCollectionIfNotExists { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Optional.
|
||||
/// When specified on an output binding and <see cref="CreateLeaseCollectionIfNotExists"/> is true, defines the throughput of the created
|
||||
/// collection.
|
||||
/// </summary>
|
||||
public int LeasesCollectionThroughput { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional.
|
||||
/// Defines a prefix to be used within a Leases collection for this Trigger. Useful when sharing the same Lease collection among multiple Triggers
|
||||
/// </summary>
|
||||
public string? LeaseCollectionPrefix { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional.
|
||||
/// Customizes the amount of milliseconds between lease checkpoints. Default is always after a Function call.
|
||||
/// </summary>
|
||||
public int CheckpointInterval { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional.
|
||||
/// Customizes the amount of documents between lease checkpoints. Default is always after a Function call.
|
||||
/// </summary>
|
||||
public int CheckpointDocumentCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional.
|
||||
/// Customizes the delay in milliseconds in between polling a partition for new changes on the feed, after all current changes are drained. Default is 5000 (5 seconds).
|
||||
/// </summary>
|
||||
public int FeedPollDelay { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional.
|
||||
/// Customizes the renew interval in milliseconds for all leases for partitions currently held by the Trigger. Default is 17000 (17 seconds).
|
||||
/// </summary>
|
||||
public int LeaseRenewInterval { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional.
|
||||
/// Customizes the interval in milliseconds to kick off a task to compute if partitions are distributed evenly among known host instances. Default is 13000 (13 seconds).
|
||||
/// </summary>
|
||||
public int LeaseAcquireInterval { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional.
|
||||
/// Customizes the interval in milliseconds for which the lease is taken on a lease representing a partition. If the lease is not renewed within this interval, it will cause it to expire and ownership of the partition will move to another Trigger instance. Default is 60000 (60 seconds).
|
||||
/// </summary>
|
||||
public int LeaseExpirationInterval { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional.
|
||||
/// Customizes the maximum amount of items received in an invocation
|
||||
/// </summary>
|
||||
public int MaxItemsPerInvocation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional.
|
||||
/// Gets or sets whether change feed in the Azure Cosmos DB service should start from beginning (true) or from current (false). By default it's start from current (false).
|
||||
/// </summary>
|
||||
public bool StartFromBeginning { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Optional.
|
||||
/// Defines preferred locations (regions) for geo-replicated database accounts in the Azure Cosmos DB service.
|
||||
/// Values should be comma-separated.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// PreferredLocations = "East US,South Central US,North Europe".
|
||||
/// </example>
|
||||
public string? PreferredLocations { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional.
|
||||
/// Enable to use with Multi Master accounts.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This setting only applies to the Leases collection, as there are no write operations done to the monitored collection.
|
||||
/// </remarks>
|
||||
public bool UseMultipleWriteLocations { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional.
|
||||
/// Enables the use of JsonConvert.DefaultSettings in the monitored Azure Cosmos DB collection.
|
||||
/// <remarks>
|
||||
/// This setting only applies to the monitored collection and the consumer to setup the serialization used in the monitored collection.
|
||||
/// The JsonConvert.DefaultSettings must be set in a class derived from CosmosDBWebJobsStartup.
|
||||
/// </remarks>
|
||||
/// </summary>
|
||||
public bool UseDefaultJsonSerialization { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.Azure.Functions.Worker.Extensions.Abstractions;
|
||||
|
||||
[assembly: ExtensionInformation("Microsoft.Azure.WebJobs.Extensions.CosmosDB", "3.0.9")]
|
|
@ -0,0 +1,17 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<Import Project="..\..\build\Common.props" />
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<AssemblyName>Microsoft.Azure.Functions.Worker.Extensions.CosmosDB</AssemblyName>
|
||||
<RootNamespace>Microsoft.Azure.Functions.Worker.Extensions.CosmosDB</RootNamespace>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>..\..\key.snk</AssemblyOriginatorKeyFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Worker.Extensions.Abstractions\Worker.Extensions.Abstractions.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,33 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
namespace Microsoft.Azure.Functions.Worker.Extensions.Http
|
||||
{
|
||||
public enum AuthorizationLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// Allow access to anonymous requests.
|
||||
/// </summary>
|
||||
Anonymous = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Allow access to requests that include a valid user authentication token
|
||||
/// </summary>
|
||||
User = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Allow access to requests that include a function key
|
||||
/// </summary>
|
||||
Function = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Allows access to requests that include a system key
|
||||
/// </summary>
|
||||
System = 3,
|
||||
|
||||
/// <summary>
|
||||
/// Allow access to requests that include the master key
|
||||
/// </summary>
|
||||
Admin = 4,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.Azure.Functions.Worker.Extensions.Abstractions;
|
||||
|
||||
namespace Microsoft.Azure.Functions.Worker.Extensions.Http
|
||||
{
|
||||
public sealed class HttpTriggerAttribute : TriggerBindingAttribute
|
||||
{
|
||||
public HttpTriggerAttribute()
|
||||
{
|
||||
AuthLevel = AuthorizationLevel.Function;
|
||||
}
|
||||
|
||||
public HttpTriggerAttribute(params string[] methods)
|
||||
{
|
||||
Methods = methods;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new instance.
|
||||
/// </summary>
|
||||
/// <param name="authLevel">The <see cref="AuthorizationLevel"/> to apply.</param>
|
||||
public HttpTriggerAttribute(AuthorizationLevel authLevel)
|
||||
{
|
||||
AuthLevel = authLevel;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new instance.
|
||||
/// </summary>
|
||||
/// <param name="authLevel">The <see cref="AuthorizationLevel"/> to apply.</param>
|
||||
/// <param name="methods">The http methods to allow.</param>
|
||||
public HttpTriggerAttribute(AuthorizationLevel authLevel, params string[] methods)
|
||||
{
|
||||
AuthLevel = authLevel;
|
||||
Methods = methods;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the route template for the function. Can include
|
||||
/// route parameters using WebApi supported syntax. If not specified,
|
||||
/// will default to the function name.
|
||||
/// </summary>
|
||||
public string? Route { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the http methods that are supported for the function.
|
||||
/// </summary>
|
||||
public string[]? Methods { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the authorization level for the function.
|
||||
/// </summary>
|
||||
public AuthorizationLevel AuthLevel { get; private set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<Import Project="..\..\build\Common.props" />
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<AssemblyName>Microsoft.Azure.Functions.Worker.Extensions.Http</AssemblyName>
|
||||
<RootNamespace>Microsoft.Azure.Functions.Worker.Extensions.Http</RootNamespace>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>..\..\key.snk</AssemblyOriginatorKeyFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Worker.Extensions.Abstractions\Worker.Extensions.Abstractions.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,33 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.Azure.Functions.Worker.Extensions.Abstractions;
|
||||
|
||||
namespace Microsoft.Azure.Functions.Worker.Extensions.Storage
|
||||
{
|
||||
public sealed class BlobInputAttribute : InputBindingAttribute
|
||||
{
|
||||
private readonly string _blobPath;
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="BlobInputAttribute"/> class.</summary>
|
||||
/// <param name="blobPath">The path of the blob to which to bind.</param>
|
||||
public BlobInputAttribute(string blobPath)
|
||||
{
|
||||
_blobPath = blobPath;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path of the blob to which to bind.
|
||||
/// </summary>
|
||||
public string BlobPath
|
||||
{
|
||||
get { return _blobPath; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the app setting name that contains the Azure Storage connection string.
|
||||
/// </summary>
|
||||
public string? Connection { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.Azure.Functions.Worker.Extensions.Abstractions;
|
||||
|
||||
namespace Microsoft.Azure.Functions.Worker.Extensions.Storage
|
||||
{
|
||||
public sealed class BlobOutputAttribute : OutputBindingAttribute
|
||||
{
|
||||
private readonly string _blobPath;
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="BlobOutputAttribute"/> class.</summary>
|
||||
/// <param name="name">The name of the property to which to bind</param>
|
||||
/// <param name="blobPath">The path of the blob to which to bind.</param>
|
||||
public BlobOutputAttribute(string name, string blobPath) : base(name)
|
||||
{
|
||||
_blobPath = blobPath;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path of the blob to which to bind.
|
||||
/// </summary>
|
||||
public string BlobPath
|
||||
{
|
||||
get { return _blobPath; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the app setting name that contains the Azure Storage connection string.
|
||||
/// </summary>
|
||||
public string? Connection { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.Azure.Functions.Worker.Extensions.Abstractions;
|
||||
|
||||
namespace Microsoft.Azure.Functions.Worker.Extensions.Storage
|
||||
{
|
||||
public sealed class BlobTriggerAttribute : TriggerBindingAttribute
|
||||
{
|
||||
private readonly string _blobPath;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BlobTriggerAttribute"/> class.
|
||||
/// </summary>
|
||||
/// <param name="blobPath">The path of the blob to which to bind.</param>
|
||||
/// <remarks>
|
||||
/// The blob portion of the path can contain tokens in curly braces to indicate a pattern to match. The matched
|
||||
/// name can be used in other binding attributes to define the output name of a Job function.
|
||||
/// </remarks>
|
||||
public BlobTriggerAttribute(string blobPath)
|
||||
{
|
||||
_blobPath = blobPath;
|
||||
}
|
||||
|
||||
/// <summary>Gets the path of the blob to which to bind.</summary>
|
||||
/// <remarks>
|
||||
/// The blob portion of the path can contain tokens in curly braces to indicate a pattern to match. The matched
|
||||
/// name can be used in other binding attributes to define the output name of a Job function.
|
||||
/// </remarks>
|
||||
public string BlobPath
|
||||
{
|
||||
get { return _blobPath; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the app setting name that contains the Azure Storage connection string.
|
||||
/// </summary>
|
||||
public string? Connection { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.Azure.Functions.Worker.Extensions.Abstractions;
|
||||
|
||||
[assembly: ExtensionInformation("Microsoft.Azure.WebJobs.Extensions.Storage", "4.0.3")]
|
|
@ -0,0 +1,31 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.Azure.Functions.Worker.Extensions.Abstractions;
|
||||
|
||||
namespace Microsoft.Azure.Functions.Worker.Extensions.Storage
|
||||
{
|
||||
public sealed class QueueOutputAttribute : OutputBindingAttribute
|
||||
{
|
||||
private readonly string _queueName;
|
||||
|
||||
public QueueOutputAttribute(string name, string queueName) : base(name)
|
||||
{
|
||||
_queueName = queueName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the queue to which to bind.
|
||||
/// </summary>
|
||||
public string QueueName
|
||||
{
|
||||
get { return _queueName; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the app setting name that contains the Azure Storage connection string.
|
||||
/// </summary>
|
||||
public string? Connection { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.Azure.Functions.Worker.Extensions.Abstractions;
|
||||
|
||||
namespace Microsoft.Azure.Functions.Worker.Extensions.Storage
|
||||
{
|
||||
public sealed class QueueTriggerAttribute : TriggerBindingAttribute
|
||||
{
|
||||
private readonly string _queueName;
|
||||
|
||||
public QueueTriggerAttribute(string queueName)
|
||||
{
|
||||
_queueName = queueName;
|
||||
}
|
||||
|
||||
/// <summary>Gets the name of the queue to which to bind.</summary>
|
||||
public string QueueName
|
||||
{
|
||||
get { return _queueName; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the app setting name that contains the Azure Storage connection string.
|
||||
/// </summary>
|
||||
public string? Connection { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<Import Project="..\..\build\Common.props" />
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<AssemblyName>Microsoft.Azure.Functions.Worker.Extensions.Storage</AssemblyName>
|
||||
<RootNamespace>Microsoft.Azure.Functions.Worker.Extensions.Storage</RootNamespace>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>..\..\key.snk</AssemblyOriginatorKeyFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Worker.Extensions.Abstractions\Worker.Extensions.Abstractions.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,6 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.Azure.Functions.Worker.Extensions.Abstractions;
|
||||
|
||||
[assembly: ExtensionInformation("Microsoft.Azure.WebJobs.Extensions", "4.0.1")]
|
|
@ -0,0 +1,46 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.Azure.Functions.Worker.Extensions.Abstractions;
|
||||
|
||||
namespace Microsoft.Azure.Functions.Worker.Extensions.Timer
|
||||
{
|
||||
public sealed class TimerTriggerAttribute : TriggerBindingAttribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructs a new instance based on the schedule expression passed in.
|
||||
/// </summary>
|
||||
/// <param name="schedule">A schedule expression. This can either be a 6 field crontab expression
|
||||
/// <a href="https://docs.microsoft.com/en-us/azure/azure-functions/functions-bindings-timer#cron-expressions"/> or a <see cref="TimeSpan"/>
|
||||
/// string (e.g. "00:30:00"). On Azure Functions, a TimeSpan string is only supported
|
||||
/// when running on an App Service Plan.</param>
|
||||
public TimerTriggerAttribute(string schedule)
|
||||
{
|
||||
Schedule = schedule;
|
||||
UseMonitor = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the schedule expression.
|
||||
/// </summary>
|
||||
public string Schedule { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the schedule should be monitored.
|
||||
/// Schedule monitoring persists schedule occurrences to aid in ensuring the
|
||||
/// schedule is maintained correctly even when roles restart.
|
||||
/// If not set explicitly, this will default to true for schedules that have a recurrence
|
||||
/// interval greater than 1 minute (i.e., for schedules that occur more than once
|
||||
/// per minute, persistence will be disabled).
|
||||
/// </summary>
|
||||
public bool UseMonitor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the function should be invoked
|
||||
/// immediately on startup. After the initial startup run, the function will
|
||||
/// be run on schedule thereafter.
|
||||
/// </summary>
|
||||
public bool RunOnStartup { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<Import Project="..\..\build\Common.props" />
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<AssemblyName>Microsoft.Azure.Functions.Worker.Extensions.Timer</AssemblyName>
|
||||
<RootNamespace>Microsoft.Azure.Functions.Worker.Extensions.Timer</RootNamespace>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>..\..\key.snk</AssemblyOriginatorKeyFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Worker.Extensions.Abstractions\Worker.Extensions.Abstractions.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
|
||||
|
@ -6,8 +6,10 @@ using System.Collections.Generic;
|
|||
using System.Net;
|
||||
using System.Text.Json;
|
||||
using Microsoft.Azure.Functions.Worker;
|
||||
using Microsoft.Azure.WebJobs;
|
||||
using Microsoft.Azure.WebJobs.Extensions.Http;
|
||||
using Microsoft.Azure.Functions.Worker.Extensions.Abstractions;
|
||||
using Microsoft.Azure.Functions.Worker.Extensions.Http;
|
||||
using Microsoft.Azure.Functions.Worker.Extensions.Storage;
|
||||
using Microsoft.Azure.Functions.Worker.Pipeline;
|
||||
|
||||
namespace FunctionApp
|
||||
{
|
||||
|
@ -15,12 +17,14 @@ namespace FunctionApp
|
|||
{
|
||||
|
||||
[FunctionName("Function1")]
|
||||
[QueueOutput("book", "functionstesting2", Connection = "AzureWebJobsStorage")]
|
||||
public static HttpResponseData Run(
|
||||
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequestData req, [Blob("test-samples/sample1.txt", Connection = "AzureWebJobsStorage")] string myBlob,
|
||||
[Queue("functionstesting2", Connection = "AzureWebJobsStorage")] OutputBinding<Book> book)
|
||||
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequestData req,
|
||||
[BlobInput("test-samples/sample1.txt", Connection = "AzureWebJobsStorage")] string myBlob, FunctionExecutionContext context)
|
||||
{
|
||||
var bookVal = (Book)JsonSerializer.Deserialize(myBlob, typeof(Book));
|
||||
book.SetValue(bookVal);
|
||||
context.OutputBindings["book"] = bookVal;
|
||||
|
||||
var response = new HttpResponseData(HttpStatusCode.OK);
|
||||
var headers = new Dictionary<string, string>();
|
||||
headers.Add("Date", "Mon, 18 Jul 2016 16:06:00 GMT");
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.Azure.WebJobs;
|
||||
using Microsoft.Azure.Functions.Worker.Extensions.Abstractions;
|
||||
using Microsoft.Azure.Functions.Worker.Extensions.Storage;
|
||||
|
||||
namespace FunctionApp
|
||||
{
|
||||
|
@ -10,7 +11,7 @@ namespace FunctionApp
|
|||
{
|
||||
[FunctionName("Function2")]
|
||||
public static Book Run([QueueTrigger("functionstesting2", Connection = "AzureWebJobsStorage")] Book myQueueItem,
|
||||
[Blob("test-samples/sample1.txt", Connection = "AzureWebJobsStorage")] string myBlob)
|
||||
[BlobInput("test-samples/sample1.txt", Connection = "AzureWebJobsStorage")] string myBlob)
|
||||
{
|
||||
Console.WriteLine(myBlob);
|
||||
return myQueueItem;
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
using System.Net;
|
||||
using Microsoft.Azure.Functions.Worker;
|
||||
using Microsoft.Azure.WebJobs;
|
||||
using Microsoft.Azure.WebJobs.Extensions.Http;
|
||||
using Microsoft.Azure.Functions.Worker.Extensions.Abstractions;
|
||||
using Microsoft.Azure.Functions.Worker.Extensions.Http;
|
||||
using Microsoft.Azure.Functions.Worker.Extensions.Storage;
|
||||
using Microsoft.Azure.Functions.Worker.Pipeline;
|
||||
|
||||
namespace FunctionApp
|
||||
{
|
||||
|
@ -12,13 +14,14 @@ namespace FunctionApp
|
|||
{
|
||||
|
||||
[FunctionName("Function3")]
|
||||
[QueueOutput("name", "functionstesting2", Connection = "AzureWebJobsStorage")]
|
||||
public static HttpResponseData Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequestData req,
|
||||
[Queue("functionstesting2", Connection = "AzureWebJobsStorage")] OutputBinding<string> name)
|
||||
FunctionExecutionContext context)
|
||||
{
|
||||
var response = new HttpResponseData(HttpStatusCode.OK);
|
||||
response.Body = "Success!!";
|
||||
|
||||
name.SetValue("some name");
|
||||
context.OutputBindings["name"] = "some name";
|
||||
|
||||
return response;
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using Microsoft.Azure.Functions.Worker;
|
||||
using Microsoft.Azure.Functions.Worker.Extensions.Abstractions;
|
||||
using Microsoft.Azure.Functions.Worker.Extensions.Http;
|
||||
using Microsoft.Azure.Functions.Worker.Pipeline;
|
||||
using Microsoft.Azure.WebJobs;
|
||||
using Microsoft.Azure.WebJobs.Extensions.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace FunctionApp
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using Microsoft.Azure.Functions.Worker;
|
||||
using Microsoft.Azure.Functions.Worker.Extensions.Abstractions;
|
||||
using Microsoft.Azure.Functions.Worker.Extensions.Http;
|
||||
using Microsoft.Azure.Functions.Worker.Pipeline;
|
||||
using Microsoft.Azure.WebJobs;
|
||||
using Microsoft.Azure.WebJobs.Extensions.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace FunctionApp
|
||||
|
|
|
@ -13,15 +13,14 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="1.0.0-preview2" />
|
||||
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.Http" Version="3.0.8" />
|
||||
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.Storage" Version="4.0.3" />
|
||||
<PackageReference Include="Microsoft.Azure.WebJobs.Script.Abstractions" Version="1.0.0-preview" />
|
||||
<PackageReference Include="Microsoft.Azure.WebJobs.Script.ExtensionsMetadataGenerator" Version="1.2.0" />
|
||||
<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="1.0.0-preview4-test1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="System.Net.NameResolution" Version="4.3.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\extensions\Worker.Extensions.Abstractions\Worker.Extensions.Abstractions.csproj" />
|
||||
<ProjectReference Include="..\..\extensions\Worker.Extensions.Http\Worker.Extensions.Http.csproj" />
|
||||
<ProjectReference Include="..\..\extensions\Worker.Extensions.Storage\Worker.Extensions.Storage.csproj" />
|
||||
<ProjectReference Include="..\..\src\DotNetWorker\DotNetWorker.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<add key="nuget.org" value="https://www.nuget.org/api/v2/" />
|
||||
<add key="azure_app_service" value="https://www.myget.org/F/azure-appservice/api/v2" />
|
||||
</packageSources>
|
||||
</configuration>
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
using System.Diagnostics;
|
||||
|
@ -14,9 +14,9 @@ namespace FunctionApp
|
|||
{
|
||||
static async Task Main(string[] args)
|
||||
{
|
||||
#if DEBUG
|
||||
Debugger.Launch();
|
||||
#endif
|
||||
// #if DEBUG
|
||||
// Debugger.Launch();
|
||||
// #endif
|
||||
var host = new HostBuilder()
|
||||
.ConfigureAppConfiguration(c =>
|
||||
{
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"IsEncrypted": false,
|
||||
"Values": {
|
||||
"FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated",
|
||||
"AzureWebJobsStorage": "UseDevelopmentStorage=true"
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Azure.WebJobs.Extensions.FunctionMetadataLoader;
|
||||
using Microsoft.Azure.WebJobs.Hosting;
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Mono.Cecil;
|
||||
|
||||
namespace Microsoft.Azure.Functions.Worker.Sdk
|
||||
{
|
||||
public static class CustomAttributeExtensions
|
||||
{
|
||||
public static IDictionary<string, object> GetAllDefinedProperties(this CustomAttribute attribute)
|
||||
{
|
||||
var properties = new Dictionary<string, object>();
|
||||
|
||||
// To avoid needing to instantiate any types, assume that the constructor
|
||||
// argument names are equal to property names.
|
||||
LoadConstructorArguments(properties, attribute);
|
||||
LoadDefinedProperties(properties, attribute);
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
private static void LoadConstructorArguments(IDictionary<string, object> properties, CustomAttribute attribute)
|
||||
{
|
||||
var constructorParams = attribute.Constructor.Resolve().Parameters;
|
||||
for (int i = 0; i < attribute.ConstructorArguments.Count; i++)
|
||||
{
|
||||
var arg = attribute.ConstructorArguments[i];
|
||||
var param = constructorParams[i];
|
||||
|
||||
string? paramName = param?.Name;
|
||||
object? paramValue = arg.Value;
|
||||
|
||||
if (paramName == null || paramValue == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
paramValue = GetEnrichedValue(param!.ParameterType, paramValue);
|
||||
|
||||
properties[paramName] = paramValue!;
|
||||
}
|
||||
}
|
||||
|
||||
private static void LoadDefinedProperties(IDictionary<string, object> properties, CustomAttribute attribute)
|
||||
{
|
||||
foreach (CustomAttributeNamedArgument property in attribute.Properties)
|
||||
{
|
||||
object? propVal = property.Argument.Value;
|
||||
|
||||
if (propVal == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
propVal = GetEnrichedValue(property.Argument.Type, propVal);
|
||||
|
||||
properties[property.Name] = propVal!;
|
||||
}
|
||||
}
|
||||
|
||||
private static object? GetEnrichedValue(TypeReference type, object value)
|
||||
{
|
||||
if (TryGetEnumName(type.Resolve(), value, out string? enumName))
|
||||
{
|
||||
return enumName;
|
||||
}
|
||||
else if (type.IsArray)
|
||||
{
|
||||
var arrayValue = value as IEnumerable<CustomAttributeArgument>;
|
||||
return arrayValue.Select(p => p.Value).ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryGetEnumName(TypeDefinition typeDef, object enumValue, out string? enumName)
|
||||
{
|
||||
if (typeDef.IsEnum)
|
||||
{
|
||||
enumName = typeDef.Fields.Single(f => Equals(f.Constant, enumValue)).Name;
|
||||
return true;
|
||||
}
|
||||
|
||||
enumName = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Microsoft.Azure.Functions.Worker.Sdk
|
||||
{
|
||||
public class ExtensionReference
|
||||
{
|
||||
[JsonPropertyName("name")]
|
||||
public string? Name { get; set; }
|
||||
|
||||
[JsonPropertyName("typeName")]
|
||||
public string? TypeName { get; set; }
|
||||
|
||||
[JsonPropertyName("hintPath")]
|
||||
public string? HintPath { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.Azure.Functions.Worker.Sdk
|
||||
{
|
||||
internal class ExtensionsCsprojGenerator
|
||||
{
|
||||
private const string ExtensionsProjectName = "WorkerExtensions.csproj";
|
||||
|
||||
private readonly IDictionary<string, string> _extensions;
|
||||
private readonly string _outputPath;
|
||||
|
||||
public ExtensionsCsprojGenerator(IDictionary<string, string> extensions, string outputPath)
|
||||
{
|
||||
_extensions = extensions ?? throw new ArgumentNullException(nameof(extensions));
|
||||
_outputPath = outputPath ?? throw new ArgumentNullException(nameof(outputPath));
|
||||
}
|
||||
|
||||
public void Generate()
|
||||
{
|
||||
var extensionsCsprojFilePath = Path.Combine(_outputPath, ExtensionsProjectName);
|
||||
|
||||
RecreateDirectory(_outputPath);
|
||||
|
||||
WriteExtensionsCsProj(extensionsCsprojFilePath);
|
||||
}
|
||||
|
||||
private void RecreateDirectory(string directoryPath)
|
||||
{
|
||||
if (Directory.Exists(directoryPath))
|
||||
{
|
||||
Directory.Delete(directoryPath, recursive: true);
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(directoryPath);
|
||||
}
|
||||
|
||||
private void WriteExtensionsCsProj(string filePath)
|
||||
{
|
||||
string csprojContent = GetCsProjContent();
|
||||
|
||||
File.WriteAllText(filePath, csprojContent);
|
||||
}
|
||||
|
||||
internal string GetCsProjContent()
|
||||
{
|
||||
string extensionReferences = GetExtensionReferences();
|
||||
|
||||
return $@"
|
||||
<Project Sdk=""Microsoft.NET.Sdk"">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<Configuration>Release</Configuration>
|
||||
<AssemblyName>Microsoft.Azure.Functions.Worker.Extensions</AssemblyName>
|
||||
<RootNamespace>Microsoft.Azure.Functions.Worker.Extensions</RootNamespace>
|
||||
<MajorMinorProductVersion>1.0</MajorMinorProductVersion>
|
||||
<Version>$(MajorMinorProductVersion).0</Version>
|
||||
<AssemblyVersion>$(MajorMinorProductVersion).0.0</AssemblyVersion>
|
||||
<FileVersion>$(Version)</FileVersion>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include=""Microsoft.Azure.WebJobs.Script.ExtensionsMetadataGenerator"" Version=""1.2.0"" />
|
||||
{extensionReferences}
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
";
|
||||
}
|
||||
|
||||
private string GetExtensionReferences()
|
||||
{
|
||||
var packages = new StringBuilder();
|
||||
|
||||
foreach (KeyValuePair<string, string> extensionPair in _extensions)
|
||||
{
|
||||
packages.AppendLine(GetPackageReferenceFromExtension(name: extensionPair.Key, version: extensionPair.Value));
|
||||
}
|
||||
|
||||
return packages.ToString();
|
||||
}
|
||||
|
||||
private static string GetPackageReferenceFromExtension(string name, string version)
|
||||
{
|
||||
return $@"<PackageReference Include=""{name}"" Version=""{version}"" />";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Microsoft.Azure.Functions.Worker.Sdk
|
||||
{
|
||||
public class ExtensionsMetadata
|
||||
{
|
||||
[JsonPropertyName("extensions")]
|
||||
public IEnumerable<ExtensionReference>? Extensions { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Microsoft.Azure.Functions.Worker.Sdk
|
||||
{
|
||||
internal class ExtensionsMetadataEnhancer
|
||||
{
|
||||
// TODO: confirm paths will work both in Windows and Linux Azure, when published from Windows or Linux
|
||||
private const string ExtensionsBinaryDirectoryPath = @"./.azurefunctions";
|
||||
private const string AssemblyNameFromQualifiedNameRegex = @"^.+,+\s(.+),\sVersion=.+$";
|
||||
|
||||
public static void AddHintPath(IEnumerable<ExtensionReference> extensions)
|
||||
{
|
||||
foreach (ExtensionReference extension in extensions)
|
||||
{
|
||||
string? assemblyName = GetAssemblyNameOrNull(extension.TypeName);
|
||||
|
||||
// TODO: Worth checking if assembly if also present in there?
|
||||
if (string.IsNullOrEmpty(extension.HintPath) && !string.IsNullOrEmpty(assemblyName))
|
||||
{
|
||||
extension.HintPath = $@"{ExtensionsBinaryDirectoryPath}/{assemblyName}.dll";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string? GetAssemblyNameOrNull(string? typeName)
|
||||
{
|
||||
if (typeName == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var match = Regex.Match(typeName, AssemblyNameFromQualifiedNameRegex);
|
||||
|
||||
if (match.Success && match.Groups.Count == 2)
|
||||
{
|
||||
return match.Groups[1].Value;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
|
@ -13,16 +13,37 @@ namespace Microsoft.Azure.Functions.Worker.Sdk
|
|||
{
|
||||
internal class FunctionMetadataGenerator
|
||||
{
|
||||
private const string BindingType = "Microsoft.Azure.Functions.Worker.Extensions.Abstractions.BindingAttribute";
|
||||
private const string OutputBindingType = "Microsoft.Azure.Functions.Worker.Extensions.Abstractions.OutputBindingAttribute";
|
||||
private const string FunctionNameType = "Microsoft.Azure.Functions.Worker.Extensions.Abstractions.FunctionNameAttribute";
|
||||
private const string ExtensionsInformationType = "Microsoft.Azure.Functions.Worker.Extensions.Abstractions.ExtensionInformationAttribute";
|
||||
|
||||
private readonly IndentableLogger _logger;
|
||||
|
||||
// TODO: Verify that we don't need to allow
|
||||
// same extensions of different versions. Picking the last version for now.
|
||||
// We can also just add all the versions of extensions and then let the
|
||||
// build pick the one it likes.
|
||||
private readonly IDictionary<string, string> _extensions;
|
||||
|
||||
public FunctionMetadataGenerator()
|
||||
: this((l, m) => { })
|
||||
{
|
||||
_extensions = new Dictionary<string, string>();
|
||||
}
|
||||
|
||||
public FunctionMetadataGenerator(Action<TraceLevel, string> log)
|
||||
{
|
||||
_logger = new IndentableLogger(log);
|
||||
_extensions = new Dictionary<string, string>();
|
||||
}
|
||||
|
||||
public IDictionary<string, string> Extensions
|
||||
{
|
||||
get
|
||||
{
|
||||
return _extensions;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<SdkFunctionMetadata> GenerateFunctionMetadata(string assemblyPath, IEnumerable<string> referencePaths)
|
||||
|
@ -81,7 +102,7 @@ namespace Microsoft.Azure.Functions.Worker.Sdk
|
|||
foreach (TypeDefinition type in module.Types)
|
||||
{
|
||||
var functionsResult = GenerateFunctionMetadata(type).ToArray();
|
||||
if (functionsResult.Length > 0)
|
||||
if (functionsResult.Any())
|
||||
{
|
||||
_logger.LogMessage($"Found {functionsResult.Length} functions in '{type.GetReflectionFullName()}'.");
|
||||
}
|
||||
|
@ -98,166 +119,26 @@ namespace Microsoft.Azure.Functions.Worker.Sdk
|
|||
|
||||
foreach (MethodDefinition method in type.Methods)
|
||||
{
|
||||
if (!TryCreateFunctionMetadata(method, out SdkFunctionMetadata? metadata)
|
||||
|| metadata == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
AddFunctionMetadataIfFunction(functions, method);
|
||||
}
|
||||
|
||||
foreach (var binding in CreateBindingMetadata(method))
|
||||
return functions;
|
||||
}
|
||||
|
||||
private void AddFunctionMetadataIfFunction(IList<SdkFunctionMetadata> functions, MethodDefinition method)
|
||||
{
|
||||
if (TryCreateFunctionMetadata(method, out SdkFunctionMetadata? metadata)
|
||||
&& metadata != null)
|
||||
{
|
||||
var allBindings = CreateBindingMetadataAndAddExtensions(method);
|
||||
|
||||
foreach(var binding in allBindings)
|
||||
{
|
||||
metadata.Bindings.Add(binding);
|
||||
}
|
||||
|
||||
functions.Add(metadata);
|
||||
}
|
||||
|
||||
return functions;
|
||||
}
|
||||
|
||||
|
||||
private IEnumerable<ExpandoObject> CreateBindingMetadata(MethodDefinition method)
|
||||
{
|
||||
var bindingMetadata = new List<ExpandoObject>();
|
||||
|
||||
foreach (ParameterDefinition parameter in method.Parameters)
|
||||
{
|
||||
foreach (CustomAttribute attribute in parameter.CustomAttributes)
|
||||
{
|
||||
if (IsWebJobsBinding(attribute))
|
||||
{
|
||||
string bindingType = attribute.AttributeType.Name.Replace("Attribute", string.Empty);
|
||||
|
||||
ExpandoObject binding = new ExpandoObject();
|
||||
var bindingDict = (IDictionary<string, object>)binding;
|
||||
bindingDict["Name"] = parameter.Name;
|
||||
bindingDict["Type"] = bindingType;
|
||||
bindingDict["Direction"] = GetBindingDirection(parameter);
|
||||
|
||||
foreach (var property in GetAttributeProperties(attribute))
|
||||
{
|
||||
bindingDict.Add(property.Key, property.Value);
|
||||
}
|
||||
|
||||
bindingMetadata.Add(binding);
|
||||
|
||||
// TODO: Fix $return detection
|
||||
// auto-add a return type for http for now
|
||||
if (string.Equals(bindingType, "httptrigger", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
IDictionary<string, object> returnBinding = new ExpandoObject();
|
||||
returnBinding["Name"] = "$return";
|
||||
returnBinding["Type"] = "http";
|
||||
returnBinding["Direction"] = "Out";
|
||||
|
||||
bindingMetadata.Add((ExpandoObject)returnBinding);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bindingMetadata;
|
||||
}
|
||||
|
||||
private IDictionary<string, object> GetAttributeProperties(CustomAttribute attribute)
|
||||
{
|
||||
var properties = new Dictionary<string, object>();
|
||||
|
||||
// To avoid needing to instantiate any types, assume that the constructor
|
||||
// argument names are equal to property names.
|
||||
var constructorParams = attribute.Constructor.Resolve().Parameters;
|
||||
for (int i = 0; i < attribute.ConstructorArguments.Count; i++)
|
||||
{
|
||||
var arg = attribute.ConstructorArguments[i];
|
||||
var param = constructorParams[i];
|
||||
|
||||
if (param == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string? paramName = param.Name;
|
||||
object? paramValue = arg.Value;
|
||||
|
||||
if (paramName == null || paramValue == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Temporary fix for timer trigger attribute property being different
|
||||
// from what is expected in FunctionMetadata
|
||||
// https://github.com/Azure/azure-functions-host/issues/6989
|
||||
if (string.Equals(paramName, "scheduleExpression", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
paramName = "schedule";
|
||||
}
|
||||
|
||||
paramValue = GetParamValue(param.ParameterType, paramValue);
|
||||
|
||||
properties[paramName] = paramValue!;
|
||||
}
|
||||
|
||||
foreach (var namedArgument in attribute.Properties)
|
||||
{
|
||||
object? argValue = namedArgument.Argument.Value;
|
||||
|
||||
if (argValue == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
argValue = GetParamValue(namedArgument.Argument.Type, argValue);
|
||||
|
||||
properties[namedArgument.Name] = argValue!;
|
||||
}
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
internal static object? GetParamValue(TypeReference paramType, object arg)
|
||||
{
|
||||
if (TryGetEnumName(paramType.Resolve(), arg, out string? enumName))
|
||||
{
|
||||
return enumName;
|
||||
}
|
||||
else if (paramType.IsArray)
|
||||
{
|
||||
var arrayValue = arg as IEnumerable<CustomAttributeArgument>;
|
||||
return arrayValue.Select(p => p.Value).ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
return arg;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryGetEnumName(TypeDefinition typeDef, object enumValue, out string? enumName)
|
||||
{
|
||||
if (typeDef.IsEnum)
|
||||
{
|
||||
enumName = typeDef.Fields.Single(f => Equals(f.Constant, enumValue)).Name;
|
||||
return true;
|
||||
}
|
||||
|
||||
enumName = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private string GetBindingDirection(ParameterDefinition parameter)
|
||||
{
|
||||
if (parameter.ParameterType.IsGenericInstance &&
|
||||
parameter.ParameterType.Resolve().FullName == "Microsoft.Azure.Functions.Worker.OutputBinding`1")
|
||||
{
|
||||
return "Out";
|
||||
}
|
||||
|
||||
return "In";
|
||||
}
|
||||
|
||||
private static bool IsWebJobsBinding(CustomAttribute attribute)
|
||||
{
|
||||
return attribute.AttributeType.Resolve().CustomAttributes
|
||||
.Any(p => p.AttributeType.FullName == "Microsoft.Azure.WebJobs.Description.BindingAttribute");
|
||||
}
|
||||
|
||||
private bool TryCreateFunctionMetadata(MethodDefinition method, out SdkFunctionMetadata? function)
|
||||
|
@ -266,28 +147,22 @@ namespace Microsoft.Azure.Functions.Worker.Sdk
|
|||
|
||||
foreach (CustomAttribute attribute in method.CustomAttributes)
|
||||
{
|
||||
if (attribute.AttributeType.FullName == "Microsoft.Azure.WebJobs.FunctionNameAttribute")
|
||||
if (attribute.AttributeType.FullName == FunctionNameType)
|
||||
{
|
||||
TypeDefinition declaringType = method.DeclaringType;
|
||||
|
||||
string assemblyName = declaringType.Module.Assembly.Name.Name;
|
||||
|
||||
var functionName = attribute.ConstructorArguments.SingleOrDefault().Value.ToString();
|
||||
string functionName = attribute.ConstructorArguments.SingleOrDefault().Value.ToString();
|
||||
|
||||
if (string.IsNullOrEmpty(functionName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
function = new SdkFunctionMetadata
|
||||
{
|
||||
Name = functionName,
|
||||
ScriptFile = $"bin/{assemblyName}.dll",
|
||||
EntryPoint = $"{declaringType.GetReflectionFullName()}.{method.Name}",
|
||||
Language = "dotnet-isolated"
|
||||
};
|
||||
TypeDefinition declaringType = method.DeclaringType;
|
||||
|
||||
function.Properties["IsCodeless"] = false;
|
||||
string actualMethodName = method.Name;
|
||||
string declaryingTypeName = declaringType.GetReflectionFullName();
|
||||
string assemblyName = declaringType.Module.Assembly.Name.Name;
|
||||
|
||||
function = CreateSdkFunctionMetadata(functionName, actualMethodName, declaryingTypeName, assemblyName);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -295,5 +170,177 @@ namespace Microsoft.Azure.Functions.Worker.Sdk
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
private SdkFunctionMetadata CreateSdkFunctionMetadata(string functionName, string actualMethodName, string declaringTypeName, string assemblyName)
|
||||
{
|
||||
var function = new SdkFunctionMetadata
|
||||
{
|
||||
Name = functionName,
|
||||
ScriptFile = $"{assemblyName}.dll",
|
||||
EntryPoint = $"{declaringTypeName}.{actualMethodName}",
|
||||
Language = "dotnet-isolated"
|
||||
};
|
||||
|
||||
function.Properties["IsCodeless"] = false;
|
||||
|
||||
return function;
|
||||
}
|
||||
|
||||
private IEnumerable<ExpandoObject> CreateBindingMetadataAndAddExtensions(MethodDefinition method)
|
||||
{
|
||||
var bindingMetadata = new List<ExpandoObject>();
|
||||
|
||||
AddInputTriggerBindingsAndExtensions(bindingMetadata, method);
|
||||
AddOutputBindingsAndExtensions(bindingMetadata, method);
|
||||
|
||||
return bindingMetadata;
|
||||
}
|
||||
|
||||
private void AddOutputBindingsAndExtensions(IList<ExpandoObject> bindingMetadata, MethodDefinition method)
|
||||
{
|
||||
foreach (CustomAttribute methodAttribute in method.CustomAttributes)
|
||||
{
|
||||
if (IsOutputBindingType(methodAttribute))
|
||||
{
|
||||
AddOutputBindingMetadata(bindingMetadata, methodAttribute);
|
||||
AddExtensionInfo(_extensions, methodAttribute);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddInputTriggerBindingsAndExtensions(IList<ExpandoObject> bindingMetadata, MethodDefinition method)
|
||||
{
|
||||
foreach (ParameterDefinition parameter in method.Parameters)
|
||||
{
|
||||
foreach (CustomAttribute parameterAttribute in parameter.CustomAttributes)
|
||||
{
|
||||
if (IsFunctionBindingType(parameterAttribute))
|
||||
{
|
||||
AddBindingMetadata(bindingMetadata, parameterAttribute, parameter.Name);
|
||||
AddExtensionInfo(_extensions, parameterAttribute);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddOutputBindingMetadata(IList<ExpandoObject> bindingMetadata, CustomAttribute attribute)
|
||||
{
|
||||
AddBindingMetadata(bindingMetadata, attribute, parameterName: null);
|
||||
}
|
||||
|
||||
private static void AddBindingMetadata(IList<ExpandoObject> bindingMetadata, CustomAttribute attribute, string? parameterName)
|
||||
{
|
||||
string bindingType = GetBindingType(attribute);
|
||||
|
||||
ExpandoObject binding = BuildBindingMetadataFromAttribute(attribute, bindingType, parameterName);
|
||||
bindingMetadata.Add(binding);
|
||||
|
||||
// TODO: Fix $return detection
|
||||
// auto-add a return type for http for now
|
||||
AddHttpOutputBindingIfHttp(bindingMetadata, bindingType);
|
||||
}
|
||||
|
||||
private static ExpandoObject BuildBindingMetadataFromAttribute(CustomAttribute attribute, string bindingType, string? parameterName)
|
||||
{
|
||||
ExpandoObject binding = new ExpandoObject();
|
||||
|
||||
var bindingDict = (IDictionary<string, object>)binding;
|
||||
|
||||
if (!string.IsNullOrEmpty(parameterName))
|
||||
{
|
||||
bindingDict["Name"] = parameterName!;
|
||||
}
|
||||
|
||||
bindingDict["Type"] = bindingType;
|
||||
bindingDict["Direction"] = GetBindingDirection(attribute);
|
||||
|
||||
foreach (var property in attribute.GetAllDefinedProperties())
|
||||
{
|
||||
bindingDict.Add(property.Key, property.Value);
|
||||
}
|
||||
|
||||
return binding;
|
||||
}
|
||||
|
||||
private static string GetBindingType(CustomAttribute attribute)
|
||||
{
|
||||
var attributeType = attribute.AttributeType.Name;
|
||||
|
||||
// TODO: fix this if we continue to use "<>EventAttribute" (questionable)
|
||||
// TODO: Should "webjob type" be a property of the "worker types" and come from there?
|
||||
return attributeType
|
||||
.Replace("TriggerAttribute", "Trigger")
|
||||
.Replace("InputAttribute", string.Empty)
|
||||
.Replace("OutputAttribute", string.Empty);
|
||||
}
|
||||
|
||||
private static void AddHttpOutputBindingIfHttp(IList<ExpandoObject> bindingMetadata, string bindingType)
|
||||
{
|
||||
if (string.Equals(bindingType, "httptrigger", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
IDictionary<string, object> returnBinding = new ExpandoObject();
|
||||
returnBinding["Name"] = "$return";
|
||||
returnBinding["Type"] = "http";
|
||||
returnBinding["Direction"] = "Out";
|
||||
|
||||
bindingMetadata.Add((ExpandoObject)returnBinding);
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddExtensionInfo(IDictionary<string, string> extensions, CustomAttribute attribute)
|
||||
{
|
||||
AssemblyDefinition extensionAssemblyDefintion = attribute.AttributeType.Resolve().Module.Assembly;
|
||||
|
||||
foreach (var assemblyAttribute in extensionAssemblyDefintion.CustomAttributes)
|
||||
{
|
||||
if (assemblyAttribute.AttributeType.FullName == ExtensionsInformationType)
|
||||
{
|
||||
string extensionName = assemblyAttribute.ConstructorArguments[0].Value.ToString();
|
||||
string extensionVersion = assemblyAttribute.ConstructorArguments[1].Value.ToString();
|
||||
|
||||
extensions[extensionName] = extensionVersion;
|
||||
|
||||
// Only 1 extension per library
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetBindingDirection(CustomAttribute attribute)
|
||||
{
|
||||
if (IsOutputBindingType(attribute))
|
||||
{
|
||||
return "Out";
|
||||
}
|
||||
|
||||
return "In";
|
||||
}
|
||||
|
||||
private static bool IsOutputBindingType(CustomAttribute attribute)
|
||||
{
|
||||
return TryGetBaseAttributeType(attribute, OutputBindingType, out _);
|
||||
}
|
||||
|
||||
private static bool IsFunctionBindingType(CustomAttribute attribute)
|
||||
{
|
||||
return TryGetBaseAttributeType(attribute, BindingType, out _);
|
||||
}
|
||||
|
||||
private static bool TryGetBaseAttributeType(CustomAttribute attribute, string baseType, out TypeReference? baseTypeRef)
|
||||
{
|
||||
baseTypeRef = attribute.AttributeType?.Resolve()?.BaseType;
|
||||
|
||||
while (baseTypeRef != null)
|
||||
{
|
||||
if (baseTypeRef.FullName == baseType)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
baseTypeRef = baseTypeRef.Resolve().BaseType;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Build.Utilities.Core" Version="16.8.0" />
|
||||
<PackageReference Include="Microsoft.Build.Utilities.Core" Version="16.8.0" />
|
||||
<PackageReference Include="Mono.Cecil" Version="0.11.3" />
|
||||
<PackageReference Include="System.Text.Json" Version="5.0.0" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -16,81 +16,61 @@ WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and
|
|||
<_FunctionsWorkerConfigInputFile>$(MSBuildThisFileDirectory)\..\tools\worker.config.json</_FunctionsWorkerConfigInputFile>
|
||||
<_FunctionsMetadataLoaderExtensionFile>$(MSBuildThisFileDirectory)\..\tools\netstandard2.0\Microsoft.Azure.WebJobs.Extensions.FunctionMetadataLoader.dll</_FunctionsMetadataLoaderExtensionFile>
|
||||
<_FunctionsTaskAssemblyFullPath Condition=" '$(_FunctionsTaskAssemblyFullPath)'=='' ">$(MSBuildThisFileDirectory)..\tools\netstandard2.0\Microsoft.Azure.Functions.Worker.Sdk.dll</_FunctionsTaskAssemblyFullPath>
|
||||
<_FunctionsExtensionsDirectory>.azurefunctions</_FunctionsExtensionsDirectory>
|
||||
<_FunctionsExtensionsJsonName>extensions.json</_FunctionsExtensionsJsonName>
|
||||
</PropertyGroup>
|
||||
|
||||
<UsingTask TaskName="GenerateFunctionMetadata"
|
||||
AssemblyFile="$(_FunctionsTaskAssemblyFullPath)"/>
|
||||
|
||||
<Target Name="_FunctionsPostBuild" AfterTargets="Build">
|
||||
<Target Name="_FunctionsPostBuild" AfterTargets="Build">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputFile>$(TargetDir)\worker.config.json</OutputFile>
|
||||
<ExtensionsCsProjFilePath>$([System.IO.Path]::Combine($([System.IO.Path]::GetTempPath()), $([System.IO.Path]::GetRandomFileName())))</ExtensionsCsProjFilePath>
|
||||
</PropertyGroup>
|
||||
|
||||
<GenerateFunctionMetadata
|
||||
AssemblyPath="$(TargetDir)$(AssemblyName).dll"
|
||||
ReferencePaths="@(ReferencePath)"
|
||||
ExtensionsCsProjFilePath="$(ExtensionsCsProjFilePath)"
|
||||
OutputPath="$(TargetDir)"/>
|
||||
|
||||
<Copy
|
||||
SourceFiles="$(_FunctionsMetadataLoaderExtensionFile)"
|
||||
DestinationFolder="$(TargetDir)"
|
||||
DestinationFolder="$(TargetDir)\$(_FunctionsExtensionsDirectory)"
|
||||
OverwriteReadOnlyFiles="true" />
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputFile>$(TargetDir)\worker.config.json</OutputFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<WriteLinesToFile
|
||||
File="$(OutputFile)"
|
||||
Lines="$([System.IO.File]::ReadAllText($(_FunctionsWorkerConfigInputFile)).Replace('$functionAssembly$', $(TargetFileName)))"
|
||||
Overwrite="true" />
|
||||
</Target>
|
||||
</Target>
|
||||
|
||||
<!--
|
||||
The targets below are temporary and can be removed once the change to allow running with an extensions.json
|
||||
file in the script root directory has been deployed: https://github.com/Azure/azure-functions-host/pull/6889
|
||||
Build the webjobs extensions in ".azurefunctions"
|
||||
-->
|
||||
<Target Name ="_WorkerExtensionsBuild" AfterTargets="_WorkerExtensionsRestore">
|
||||
<MSBuild Projects="$(ExtensionsCsProjFilePath)\WorkerExtensions.csproj" Targets="Build" Properties="Configuration=Release;OutputPath=$(TargetDir)\.azurefunctions;CopyLocalLockFileAssemblies=true"/>
|
||||
</Target>
|
||||
|
||||
<Target Name="_WorkerExtensionsRestore" AfterTargets="_FunctionsPostBuild">
|
||||
<MSBuild Projects="$(ExtensionsCsProjFilePath)\WorkerExtensions.csproj" Targets="Restore" Properties="IsRestoring=true"/>
|
||||
</Target>
|
||||
|
||||
<!--
|
||||
Add HintPath to references in "extensions.json"
|
||||
-->
|
||||
<UsingTask TaskName="EnhanceExtensionsMetadata"
|
||||
AssemblyFile="$(_FunctionsTaskAssemblyFullPath)"/>
|
||||
|
||||
<!--
|
||||
Build targets from Functions SDK
|
||||
-->
|
||||
<Target Name="_GenerateFunctionsPostBuild"
|
||||
AfterTargets="Build"
|
||||
DependsOnTargets="_FunctionsPostBuildCollectFiles;_FunctionsPostBuildCopyFiles">
|
||||
</Target>
|
||||
|
||||
<Target Name="_FunctionsPostBuildCollectFiles">
|
||||
|
||||
<ItemGroup>
|
||||
<FunctionsBuildAssemblies Include="$(TargetDir)*.deps.json;
|
||||
$(TargetDir)*.runtimeconfig*.json;
|
||||
$(TargetDir)**\*.dll;
|
||||
$(TargetDir)**\*.pdb"
|
||||
Exclude="$(TargetDir)bin\**;
|
||||
$(TargetDir)worker.config.json"/>
|
||||
|
||||
<FunctionsBuildRuntimes Include="$(TargetDir)runtimes\**" />
|
||||
|
||||
</ItemGroup>
|
||||
|
||||
</Target>
|
||||
|
||||
<Target Name="_FunctionsPostBuildCopyFiles"
|
||||
Inputs="@(FunctionsBuildAssemblies)"
|
||||
Outputs="$(TargetDir)bin\%(RecursiveDir)%(Filename)%(Extension)">
|
||||
|
||||
<!-- Copy the assemblies to the bin folder-->
|
||||
<Copy SourceFiles="@(FunctionsBuildAssemblies)"
|
||||
DestinationFiles="$(TargetDir)bin\%(RecursiveDir)%(Filename)%(Extension)"
|
||||
OverwriteReadOnlyFiles="true" />
|
||||
|
||||
</Target>
|
||||
|
||||
<Target Name="_FunctionsPostBuildCopyRuntimes" BeforeTargets="_FunctionsPostBuildCopyFiles"
|
||||
Inputs="@(FunctionsBuildRuntimes)"
|
||||
Outputs="$(TargetDir)bin\runtimes\%(RecursiveDir)%(Filename)%(Extension)">
|
||||
|
||||
<!-- Copy the runtimes folder to the bin folder-->
|
||||
<Copy SourceFiles="@(FunctionsBuildRuntimes)"
|
||||
DestinationFiles="$(TargetDir)bin\runtimes\%(RecursiveDir)%(Filename)%(Extension)"
|
||||
OverwriteReadOnlyFiles="true" />
|
||||
<Target Name="_EnhanceFunctionsExtensionsMetadataPostBuild"
|
||||
AfterTargets="_WorkerExtensionsBuild">
|
||||
|
||||
<EnhanceExtensionsMetadata
|
||||
ExtensionsJsonPath="$(TargetDir)\$(_FunctionsExtensionsDirectory)\$(_FunctionsExtensionsJsonName)"
|
||||
OutputPath="$(TargetDir)\$(_FunctionsExtensionsJsonName)"/>
|
||||
|
||||
</Target>
|
||||
|
||||
<Target
|
||||
|
@ -163,7 +143,7 @@ WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and
|
|||
|
||||
<Target Name="_FunctionsPostPublish"
|
||||
AfterTargets="Publish"
|
||||
DependsOnTargets="_GenerateFunctionsAndCopyContentFiles"
|
||||
DependsOnTargets="_GenerateFunctionsAndCopyContentFiles;_WorkerExtensionsPublish"
|
||||
>
|
||||
</Target>
|
||||
|
||||
|
@ -177,32 +157,22 @@ WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and
|
|||
|
||||
<Target Name="_GenerateFunctionsAndCopyContentFiles">
|
||||
|
||||
<ItemGroup>
|
||||
<FunctionsPublishAssemblies Include="$(PublishDir)*.deps.json;
|
||||
$(PublishDir)*.runtimeconfig*.json;
|
||||
$(PublishDir)*.dll;
|
||||
$(PublishDir)*.pdb" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Move the additional assemblies to the bin folder.
|
||||
we move the assemblies rather than copy because we want to reduce the publish payload -->
|
||||
<Move SourceFiles="@(FunctionsPublishAssemblies)"
|
||||
DestinationFiles="$(PublishDir)bin\%(RecursiveDir)%(Filename)%(Extension)"
|
||||
OverwriteReadOnlyFiles="true" />
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputFile>$(PublishDir)\worker.config.json</OutputFile>
|
||||
<ExtensionsCsProjFilePath>$([System.IO.Path]::Combine($([System.IO.Path]::GetTempPath()), $([System.IO.Path]::GetRandomFileName())))</ExtensionsCsProjFilePath>
|
||||
</PropertyGroup>
|
||||
|
||||
<GenerateFunctionMetadata
|
||||
AssemblyPath="$(PublishDir)\bin\$(AssemblyName).dll"
|
||||
AssemblyPath="$(PublishDir)\$(AssemblyName).dll"
|
||||
ReferencePaths="@(ReferencePath)"
|
||||
ExtensionsCsProjFilePath="$(ExtensionsCsProjFilePath)"
|
||||
OutputPath="$(PublishDir)"/>
|
||||
|
||||
<Copy
|
||||
SourceFiles="$(_FunctionsMetadataLoaderExtensionFile)"
|
||||
DestinationFolder="$(PublishDir)\bin"
|
||||
DestinationFolder="$(PublishDir)\$(_FunctionsExtensionsDirectory)"
|
||||
OverwriteReadOnlyFiles="true" />
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputFile>$(PublishDir)\worker.config.json</OutputFile>
|
||||
</PropertyGroup>
|
||||
<WriteLinesToFile
|
||||
File="$(OutputFile)"
|
||||
Lines="$([System.IO.File]::ReadAllText($(_FunctionsWorkerConfigInputFile)).Replace('$functionAssembly$', $(TargetFileName)))"
|
||||
|
@ -210,22 +180,26 @@ WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and
|
|||
|
||||
</Target>
|
||||
|
||||
<!--
|
||||
============================================================
|
||||
_ResolveCopyLocalAssetsForPublishFunctions
|
||||
<!--
|
||||
Publish the webjobs extensions in ".azurefunctions"
|
||||
-->
|
||||
<Target Name ="_WorkerExtensionsPublish" AfterTargets="_WorkerExtensionsRestorePublish">
|
||||
<MSBuild Projects="$(ExtensionsCsProjFilePath)\WorkerExtensions.csproj" Targets="Build" Properties="Configuration=Release;OutputPath=$(PublishDir)\.azurefunctions;CopyLocalLockFileAssemblies=true"/>
|
||||
</Target>
|
||||
|
||||
Moves all CopyLocal assemblies to bin folder.
|
||||
============================================================
|
||||
-->
|
||||
<Target
|
||||
Name="_ResolveCopyLocalAssetsForPublishFunctions"
|
||||
AfterTargets="_ResolveCopyLocalAssetsForPublish">
|
||||
<Target Name="_WorkerExtensionsRestorePublish" AfterTargets="_GenerateFunctionsAndCopyContentFiles">
|
||||
<MSBuild Projects="$(ExtensionsCsProjFilePath)\WorkerExtensions.csproj" Targets="Restore" Properties="IsRestoring=true"/>
|
||||
</Target>
|
||||
|
||||
<ItemGroup>
|
||||
<_ResolvedCopyLocalPublishAssets>
|
||||
<DestinationSubDirectory>bin\%(_ResolvedCopyLocalPublishAssets.DestinationSubDirectory)</DestinationSubDirectory>
|
||||
</_ResolvedCopyLocalPublishAssets>
|
||||
</ItemGroup>
|
||||
<!--
|
||||
Add HintPath to references in "extensions.json"
|
||||
-->
|
||||
<Target Name="_EnhanceFunctionsExtensionsMetadataPostPublish"
|
||||
AfterTargets="_WorkerExtensionsPublish">
|
||||
|
||||
<EnhanceExtensionsMetadata
|
||||
ExtensionsJsonPath="$(PublishDir)\$(_FunctionsExtensionsDirectory)\$(_FunctionsExtensionsJsonName)"
|
||||
OutputPath="$(PublishDir)\$(_FunctionsExtensionsJsonName)"/>
|
||||
|
||||
</Target>
|
||||
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using Microsoft.Build.Framework;
|
||||
using Microsoft.Build.Utilities;
|
||||
|
||||
namespace Microsoft.Azure.Functions.Worker.Sdk.Tasks
|
||||
{
|
||||
public class EnhanceExtensionsMetadata : Task
|
||||
{
|
||||
[Required]
|
||||
public string? ExtensionsJsonPath { get; set; }
|
||||
|
||||
[Required]
|
||||
public string? OutputPath { get; set; }
|
||||
|
||||
public override bool Execute()
|
||||
{
|
||||
string json = File.ReadAllText(ExtensionsJsonPath);
|
||||
|
||||
var extensionsMetadata = JsonSerializer.Deserialize<ExtensionsMetadata>(json);
|
||||
ExtensionsMetadataEnhancer.AddHintPath(extensionsMetadata?.Extensions ?? Enumerable.Empty<ExtensionReference>());
|
||||
|
||||
string newJson = JsonSerializer.Serialize(extensionsMetadata);
|
||||
File.WriteAllText(OutputPath, newJson);
|
||||
|
||||
File.Delete(ExtensionsJsonPath);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
using System.Diagnostics;
|
||||
|
@ -16,13 +16,21 @@ namespace Microsoft.Azure.Functions.Worker.Sdk
|
|||
[Required]
|
||||
public string? OutputPath { get; set; }
|
||||
|
||||
[Required]
|
||||
public string? ExtensionsCsProjFilePath { get; set; }
|
||||
|
||||
[Required]
|
||||
public ITaskItem[]? ReferencePaths { get; set; }
|
||||
|
||||
public override bool Execute()
|
||||
{
|
||||
var generator = new FunctionMetadataGenerator(MSBuildLogger);
|
||||
var functions = generator.GenerateFunctionMetadata(AssemblyPath!, ReferencePaths.Select(p => p.ItemSpec));
|
||||
var functionGenerator = new FunctionMetadataGenerator(MSBuildLogger);
|
||||
var functions = functionGenerator.GenerateFunctionMetadata(AssemblyPath!, ReferencePaths.Select(p => p.ItemSpec));
|
||||
|
||||
var extensions = functionGenerator.Extensions;
|
||||
var extensionsCsProjGenerator = new ExtensionsCsprojGenerator(extensions, ExtensionsCsProjFilePath!);
|
||||
|
||||
extensionsCsProjGenerator.Generate();
|
||||
|
||||
FunctionMetadataJsonWriter.WriteMetadata(functions, OutputPath!);
|
||||
|
||||
|
|
|
@ -3,6 +3,6 @@
|
|||
"language": "dotnet-isolated",
|
||||
"extensions": [ ".dll" ],
|
||||
"defaultExecutablePath": "dotnet",
|
||||
"defaultWorkerPath": "bin/$functionAssembly$"
|
||||
"defaultWorkerPath": "$functionAssembly$"
|
||||
}
|
||||
}
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
namespace Microsoft.Azure.Functions.Worker.Tests
|
||||
{
|
||||
internal class TestFunctionMetadata : FunctionMetadata
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Azure.Functions.Worker.E2EApp.Cosmos;
|
||||
using Microsoft.Azure.Functions.Worker.Extensions.Abstractions;
|
||||
using Microsoft.Azure.Functions.Worker.Extensions.CosmosDB;
|
||||
using Microsoft.Azure.Functions.Worker.Pipeline;
|
||||
using Microsoft.Azure.WebJobs;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.Azure.Functions.Worker.E2EApp
|
||||
|
@ -10,18 +14,18 @@ namespace Microsoft.Azure.Functions.Worker.E2EApp
|
|||
public static class CosmosFunction
|
||||
{
|
||||
[FunctionName("CosmosTrigger")]
|
||||
[CosmosDBOutput(
|
||||
name: "output",
|
||||
databaseName: "%CosmosDb%",
|
||||
collectionName: "%CosmosCollOut%",
|
||||
ConnectionStringSetting = "CosmosConnection",
|
||||
CreateIfNotExists = true)]
|
||||
public static void Run([CosmosDBTrigger(
|
||||
databaseName: "%CosmosDb%",
|
||||
collectionName: "%CosmosCollIn%",
|
||||
ConnectionStringSetting = "CosmosConnection",
|
||||
LeaseCollectionName = "leases",
|
||||
CreateLeaseCollectionIfNotExists = true)] IReadOnlyList<MyDocument> input,
|
||||
[CosmosDB(
|
||||
databaseName: "%CosmosDb%",
|
||||
collectionName: "%CosmosCollOut%",
|
||||
ConnectionStringSetting = "CosmosConnection",
|
||||
CreateIfNotExists = true)] OutputBinding<IEnumerable<object>> output,
|
||||
FunctionExecutionContext context)
|
||||
CreateLeaseCollectionIfNotExists = true)] IReadOnlyList<MyDocument> input, FunctionExecutionContext context)
|
||||
{
|
||||
if (input != null && input.Count > 0)
|
||||
{
|
||||
|
@ -30,7 +34,7 @@ namespace Microsoft.Azure.Functions.Worker.E2EApp
|
|||
context.Logger.LogInformation($"id: {doc.Id}");
|
||||
}
|
||||
|
||||
output.SetValue(input.Select(p => new { id = p.Id }));
|
||||
context.OutputBindings["output"] = input.Select(p => new { id = p.Id });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
namespace Microsoft.Azure.Functions.Worker.E2EApp.Cosmos
|
||||
{
|
||||
public class MyDocument
|
||||
|
|
|
@ -9,15 +9,11 @@
|
|||
<Nullable>disable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="1.0.0-preview3" />
|
||||
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.CosmosDB" Version="3.0.8" />
|
||||
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.Http" Version="3.0.2" />
|
||||
<PackageReference Include="Microsoft.Azure.WebJobs.Script.ExtensionsMetadataGenerator" Version="1.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\src\DotNetWorker\DotNetWorker.csproj" />
|
||||
<ProjectReference Include="..\..\..\..\extensions\Worker.Extensions.Abstractions\Worker.Extensions.Abstractions.csproj" />
|
||||
<ProjectReference Include="..\..\..\..\extensions\Worker.Extensions.CosmosDB\Worker.Extensions.CosmosDB.csproj" />
|
||||
<ProjectReference Include="..\..\..\..\extensions\Worker.Extensions.Http\Worker.Extensions.Http.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -29,4 +25,8 @@
|
|||
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="1.0.0-preview5-test2" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
|
@ -4,10 +4,9 @@ using System.Net;
|
|||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Web;
|
||||
using Grpc.Core;
|
||||
using Microsoft.Azure.Functions.Worker.Extensions.Abstractions;
|
||||
using Microsoft.Azure.Functions.Worker.Extensions.Http;
|
||||
using Microsoft.Azure.Functions.Worker.Pipeline;
|
||||
using Microsoft.Azure.WebJobs;
|
||||
using Microsoft.Azure.WebJobs.Extensions.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.Azure.Functions.Worker.E2EApp
|
||||
|
|
|
@ -1,13 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Web;
|
||||
using Grpc.Core;
|
||||
using Microsoft.Azure.Functions.Worker.Extensions.Abstractions;
|
||||
using Microsoft.Azure.Functions.Worker.Extensions.Http;
|
||||
using Microsoft.Azure.Functions.Worker.Pipeline;
|
||||
using Microsoft.Azure.WebJobs;
|
||||
using Microsoft.Azure.WebJobs.Extensions.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.Azure.Functions.Worker.E2EApp
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
|
||||
<add key="azure_app_service" value="https://www.myget.org/F/azure-appservice/api/v2" />
|
||||
</packageSources>
|
||||
</configuration>
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Azure.Functions.Worker.Configuration;
|
||||
|
|
|
@ -0,0 +1,197 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using Microsoft.Azure.Functions.Worker.Sdk;
|
||||
using Mono.Cecil;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.Azure.Functions.SdkTests
|
||||
{
|
||||
public class CustomAttributeExtensionsTests
|
||||
{
|
||||
[Fact]
|
||||
public void GetAllDefinedProperties_GetsJustConstructorParams()
|
||||
{
|
||||
ModuleDefinition module = ModuleDefinition.ReadModule(Assembly.GetExecutingAssembly().Location);
|
||||
|
||||
MethodDefinition method = GetMethod(module, "Use_JustConstructor");
|
||||
CustomAttribute attribute = GetAttribute(method, "JustConstructorAttribute");
|
||||
|
||||
IDictionary<string, object> props = attribute.GetAllDefinedProperties();
|
||||
IDictionary<string, object> expected = new Dictionary<string, object>
|
||||
{
|
||||
{ "name", "Someone1" },
|
||||
{ "number", 25 },
|
||||
{ "ch", 'y' },
|
||||
};
|
||||
|
||||
AssertDictionary(expected, props);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetAllDefinedProperties_GetsConstructorParamsAndProperties_Overridden()
|
||||
{
|
||||
ModuleDefinition module = ModuleDefinition.ReadModule(Assembly.GetExecutingAssembly().Location);
|
||||
|
||||
MethodDefinition method = GetMethod(module, "Use_ConstructorAndProperties");
|
||||
CustomAttribute attribute = GetAttribute(method, "ConstructorAndPropertiesAttribute");
|
||||
|
||||
IDictionary<string, object> props = attribute.GetAllDefinedProperties();
|
||||
IDictionary<string, object> expected = new Dictionary<string, object>
|
||||
{
|
||||
{ "name", "Someone2" },
|
||||
{ "number", 26 },
|
||||
{ "ch", 'n' },
|
||||
{ "Value", "Overridden1" }
|
||||
};
|
||||
|
||||
AssertDictionary(expected, props);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetAllDefinedProperties_GetsConstructorParamsAndProperties_Default_Ignored()
|
||||
{
|
||||
ModuleDefinition module = ModuleDefinition.ReadModule(Assembly.GetExecutingAssembly().Location);
|
||||
|
||||
MethodDefinition method = GetMethod(module, "Use_ConstructorAndProperties_DefaultProperty");
|
||||
CustomAttribute attribute = GetAttribute(method, "ConstructorAndPropertiesAttribute");
|
||||
|
||||
IDictionary<string, object> props = attribute.GetAllDefinedProperties();
|
||||
IDictionary<string, object> expected = new Dictionary<string, object>
|
||||
{
|
||||
{ "name", "Someone2" },
|
||||
{ "number", 26 },
|
||||
{ "ch", 'n' }
|
||||
};
|
||||
|
||||
AssertDictionary(expected, props);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetAllDefinedProperties_GetsProperties()
|
||||
{
|
||||
ModuleDefinition module = ModuleDefinition.ReadModule(Assembly.GetExecutingAssembly().Location);
|
||||
|
||||
MethodDefinition method = GetMethod(module, "Use_JustProperties");
|
||||
CustomAttribute attribute = GetAttribute(method, "JustPropertiesAttribute");
|
||||
|
||||
IDictionary<string, object> props = attribute.GetAllDefinedProperties();
|
||||
IDictionary<string, object> expected = new Dictionary<string, object>
|
||||
{
|
||||
{ "Value", "Overridden2" }
|
||||
};
|
||||
|
||||
AssertDictionary(expected, props);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetAllDefinedProperties_GetsProperties_Null_Ignored()
|
||||
{
|
||||
ModuleDefinition module = ModuleDefinition.ReadModule(Assembly.GetExecutingAssembly().Location);
|
||||
|
||||
MethodDefinition method = GetMethod(module, "Use_JustProperties_Null");
|
||||
CustomAttribute attribute = GetAttribute(method, "JustPropertiesAttribute");
|
||||
|
||||
IDictionary<string, object> props = attribute.GetAllDefinedProperties();
|
||||
IDictionary<string, object> expected = new Dictionary<string, object> ();
|
||||
|
||||
AssertDictionary(expected, props);
|
||||
}
|
||||
|
||||
private static void AssertDictionary<K, V>(IDictionary<K, V> dict, IDictionary<K, V> expected)
|
||||
{
|
||||
Assert.Equal(expected.Count, dict.Count);
|
||||
|
||||
foreach (var kvp in expected)
|
||||
{
|
||||
Assert.Equal(kvp.Value, dict[kvp.Key]);
|
||||
}
|
||||
}
|
||||
|
||||
private MethodDefinition GetMethod(ModuleDefinition module, string methodName)
|
||||
{
|
||||
foreach (var type in module.Types)
|
||||
{
|
||||
foreach (MethodDefinition m in type.Methods)
|
||||
{
|
||||
if (m.Name == methodName)
|
||||
{
|
||||
return m;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new Exception($"Unable to load '{methodName}' method definition for testing.");
|
||||
}
|
||||
|
||||
private CustomAttribute GetAttribute(MethodDefinition method, string attributeName)
|
||||
{
|
||||
foreach (var a in method.CustomAttributes)
|
||||
{
|
||||
if (a.AttributeType.Name == attributeName)
|
||||
{
|
||||
return a;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Exception($"Unable to load '{attributeName}' attribute for testing.");
|
||||
}
|
||||
}
|
||||
|
||||
public class Methods
|
||||
{
|
||||
[JustConstructor("Someone1", 25, 'y')]
|
||||
public void Use_JustConstructor()
|
||||
{
|
||||
}
|
||||
|
||||
[ConstructorAndProperties("Someone2", 26, 'n', Value = "Overridden1")]
|
||||
public void Use_ConstructorAndProperties()
|
||||
{
|
||||
}
|
||||
|
||||
[ConstructorAndProperties("Someone2", 26, 'n')]
|
||||
public void Use_ConstructorAndProperties_DefaultProperty()
|
||||
{
|
||||
}
|
||||
|
||||
[JustProperties(Value = "Overridden2")]
|
||||
public void Use_JustProperties()
|
||||
{
|
||||
}
|
||||
|
||||
[JustProperties(Value = null)]
|
||||
public void Use_JustProperties_Null()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class JustConstructorAttribute : Attribute
|
||||
{
|
||||
public JustConstructorAttribute(string name, int number, char ch)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class ConstructorAndPropertiesAttribute : Attribute
|
||||
{
|
||||
public ConstructorAndPropertiesAttribute(string name, int number, char ch)
|
||||
{
|
||||
}
|
||||
|
||||
public string Value { get; set; } = "DefaultValue";
|
||||
}
|
||||
|
||||
public class JustPropertiesAttribute : Attribute
|
||||
{
|
||||
public JustPropertiesAttribute()
|
||||
{
|
||||
}
|
||||
|
||||
public string Value { get; set; } = "DefaultValue";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Azure.Functions.Worker.Sdk;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.Azure.Functions.SdkTests
|
||||
{
|
||||
public class ExtensionsCsProjGeneratorTests
|
||||
{
|
||||
[Fact]
|
||||
public void GetCsProjContent_Succeeds()
|
||||
{
|
||||
IDictionary<string, string> extensions = new Dictionary<string, string>
|
||||
{
|
||||
{ "Microsoft.Azure.WebJobs.Extensions.Storage", "4.0.3" },
|
||||
{ "Microsoft.Azure.WebJobs.Extensions.Http", "3.0.0" },
|
||||
{ "Microsoft.Azure.WebJobs.Extensions", "2.0.0" },
|
||||
};
|
||||
|
||||
var generator = new ExtensionsCsprojGenerator(extensions, "");
|
||||
|
||||
string actualCsproj = generator.GetCsProjContent();
|
||||
|
||||
Assert.Equal(ExpectedCsProj(), actualCsproj);
|
||||
}
|
||||
|
||||
private static string ExpectedCsProj()
|
||||
{
|
||||
return @"
|
||||
<Project Sdk=""Microsoft.NET.Sdk"">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<Configuration>Release</Configuration>
|
||||
<AssemblyName>Microsoft.Azure.Functions.Worker.Extensions</AssemblyName>
|
||||
<RootNamespace>Microsoft.Azure.Functions.Worker.Extensions</RootNamespace>
|
||||
<MajorMinorProductVersion>1.0</MajorMinorProductVersion>
|
||||
<Version>$(MajorMinorProductVersion).0</Version>
|
||||
<AssemblyVersion>$(MajorMinorProductVersion).0.0</AssemblyVersion>
|
||||
<FileVersion>$(Version)</FileVersion>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include=""Microsoft.Azure.WebJobs.Script.ExtensionsMetadataGenerator"" Version=""1.2.0"" />
|
||||
<PackageReference Include=""Microsoft.Azure.WebJobs.Extensions.Storage"" Version=""4.0.3"" />
|
||||
<PackageReference Include=""Microsoft.Azure.WebJobs.Extensions.Http"" Version=""3.0.0"" />
|
||||
<PackageReference Include=""Microsoft.Azure.WebJobs.Extensions"" Version=""2.0.0"" />
|
||||
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Azure.Functions.Worker.Sdk;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.Azure.Functions.SdkTests
|
||||
{
|
||||
public class ExtensionsMetadataEnhancerTests
|
||||
{
|
||||
[Fact]
|
||||
public void AddHintPath_AddsExtensionsPath()
|
||||
{
|
||||
var extensions = GetBasicReferences_WithoutHintPath();
|
||||
var extensionsWithHints = GetBasicReferences_WithExtensionsHintPath();
|
||||
|
||||
ValidateHintPathUnequal(extensions, extensionsWithHints);
|
||||
|
||||
ExtensionsMetadataEnhancer.AddHintPath(extensions);
|
||||
|
||||
ValidateAllEqual(extensionsWithHints, extensions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddHintPath_DoesNotAdd_WhenAlreadyPresent()
|
||||
{
|
||||
var extensionsPreset = GetBasicReferences_WithPresetHintPath();
|
||||
var extensionsCorrectedHints = GetBasicReferences_WithExtensionsHintPath();
|
||||
|
||||
ValidateHintPathUnequal(extensionsCorrectedHints, extensionsPreset);
|
||||
|
||||
ExtensionsMetadataEnhancer.AddHintPath(extensionsPreset);
|
||||
|
||||
ValidateHintPathUnequal(extensionsCorrectedHints, extensionsPreset);
|
||||
ValidateAllEqual(GetBasicReferences_WithPresetHintPath(), extensionsPreset);
|
||||
}
|
||||
|
||||
private static void ValidateAllEqual(IEnumerable<ExtensionReference> expected, IEnumerable<ExtensionReference> actual)
|
||||
{
|
||||
Assert.Equal(expected.Count(), actual.Count());
|
||||
|
||||
using IEnumerator<ExtensionReference> expectedEnum = expected.GetEnumerator();
|
||||
using IEnumerator<ExtensionReference> actualEnum = actual.GetEnumerator();
|
||||
while (expectedEnum.MoveNext() && actualEnum.MoveNext())
|
||||
{
|
||||
Assert.Equal(expectedEnum.Current.Name, actualEnum.Current.Name);
|
||||
Assert.Equal(expectedEnum.Current.TypeName, actualEnum.Current.TypeName);
|
||||
Assert.Equal(expectedEnum.Current.HintPath, actualEnum.Current.HintPath);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ValidateHintPathUnequal(IEnumerable<ExtensionReference> expected, IEnumerable<ExtensionReference> actual)
|
||||
{
|
||||
Assert.Equal(expected.Count(), actual.Count());
|
||||
|
||||
using IEnumerator<ExtensionReference> expectedEnum = expected.GetEnumerator();
|
||||
using IEnumerator<ExtensionReference> actualEnum = actual.GetEnumerator();
|
||||
while (expectedEnum.MoveNext() && actualEnum.MoveNext())
|
||||
{
|
||||
Assert.Equal(expectedEnum.Current.Name, actualEnum.Current.Name);
|
||||
Assert.Equal(expectedEnum.Current.TypeName, actualEnum.Current.TypeName);
|
||||
Assert.NotEqual(expectedEnum.Current.HintPath, actualEnum.Current.HintPath);
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<ExtensionReference> GetBasicReferences_WithoutHintPath()
|
||||
{
|
||||
return new List<ExtensionReference>
|
||||
{
|
||||
new ExtensionReference() {
|
||||
Name = "MySecretExtension",
|
||||
TypeName = "Microsoft.Azure.WebJobs.Extensions.FunctionMetadataLoader.Startup, Microsoft.Azure.WebJobs.Extensions.FunctionMetadataLoader, Version=1.0.0.0, Culture=neutral, PublicKeyToken=551316b6919f366c"
|
||||
},
|
||||
new ExtensionReference() {
|
||||
Name = "AnotherExtension",
|
||||
TypeName = "Microsoft.Azure.WebJobs.Extensions.Storage.AzureStorageWebJobsStartup, Microsoft.Azure.WebJobs.Extensions.Storage, Version=4.0.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
|
||||
},
|
||||
new ExtensionReference() {
|
||||
Name = "SomeRandom",
|
||||
TypeName = "Microsoft.Azure.WebJobs.Extensions.Storage.AzureStorageWebJobsStartup, Yada.Foo.Yada.Yada.Bar, Version=4.0.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static IEnumerable<ExtensionReference> GetBasicReferences_WithExtensionsHintPath()
|
||||
{
|
||||
return GetBasicReferences_WithHintPath(".azurefunctions");
|
||||
}
|
||||
|
||||
private static IEnumerable<ExtensionReference> GetBasicReferences_WithPresetHintPath()
|
||||
{
|
||||
return GetBasicReferences_WithHintPath("somePresetDirectory");
|
||||
}
|
||||
|
||||
private static IEnumerable<ExtensionReference> GetBasicReferences_WithHintPath(string baseDir)
|
||||
{
|
||||
return new List<ExtensionReference>
|
||||
{
|
||||
new ExtensionReference() {
|
||||
Name = "MySecretExtension",
|
||||
TypeName = "Microsoft.Azure.WebJobs.Extensions.FunctionMetadataLoader.Startup, Microsoft.Azure.WebJobs.Extensions.FunctionMetadataLoader, Version=1.0.0.0, Culture=neutral, PublicKeyToken=551316b6919f366c",
|
||||
HintPath = $"./{baseDir}/Microsoft.Azure.WebJobs.Extensions.FunctionMetadataLoader.dll"
|
||||
},
|
||||
new ExtensionReference() {
|
||||
Name = "AnotherExtension",
|
||||
TypeName = "Microsoft.Azure.WebJobs.Extensions.Storage.AzureStorageWebJobsStartup, Microsoft.Azure.WebJobs.Extensions.Storage, Version=4.0.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35",
|
||||
HintPath = $"./{baseDir}/Microsoft.Azure.WebJobs.Extensions.Storage.dll"
|
||||
},
|
||||
new ExtensionReference() {
|
||||
Name = "SomeRandom",
|
||||
TypeName = "Microsoft.Azure.WebJobs.Extensions.Storage.AzureStorageWebJobsStartup, Yada.Foo.Yada.Yada.Bar, Version=4.0.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35",
|
||||
HintPath = $"./{baseDir}/Yada.Foo.Yada.Yada.Bar.dll"
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,17 +1,18 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Dynamic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Azure.Functions.Worker;
|
||||
using Microsoft.Azure.Functions.Worker.Extensions.Abstractions;
|
||||
using Microsoft.Azure.Functions.Worker.Extensions.Http;
|
||||
using Microsoft.Azure.Functions.Worker.Extensions.Storage;
|
||||
using Microsoft.Azure.Functions.Worker.Extensions.Timer;
|
||||
using Microsoft.Azure.Functions.Worker.Sdk;
|
||||
using Microsoft.Azure.WebJobs;
|
||||
using Microsoft.Azure.WebJobs.Extensions.Http;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.Azure.Functions.SdkTests
|
||||
|
@ -26,6 +27,11 @@ namespace Microsoft.Azure.Functions.SdkTests
|
|||
var generator = new FunctionMetadataGenerator();
|
||||
var typeDef = TestUtility.GetTypeDefinition(typeof(BasicHttp));
|
||||
var functions = generator.GenerateFunctionMetadata(typeDef);
|
||||
var extensions = generator.Extensions;
|
||||
|
||||
AssertDictionary(extensions, new Dictionary<string, string>
|
||||
{
|
||||
});
|
||||
|
||||
ValidateFunction(functions.Single(), BasicHttp.FunctionName, GetEntryPoint(nameof(BasicHttp), nameof(BasicHttp.Http)),
|
||||
b => ValidateTrigger(b),
|
||||
|
@ -63,6 +69,7 @@ namespace Microsoft.Azure.Functions.SdkTests
|
|||
var generator = new FunctionMetadataGenerator();
|
||||
var typeDef = TestUtility.GetTypeDefinition(typeof(Storage));
|
||||
var functions = generator.GenerateFunctionMetadata(typeDef);
|
||||
var extensions = generator.Extensions;
|
||||
|
||||
Assert.Equal(2, functions.Count());
|
||||
|
||||
|
@ -73,6 +80,11 @@ namespace Microsoft.Azure.Functions.SdkTests
|
|||
b => ValidateQueueTrigger(b),
|
||||
b => ValidateBlobOutput(b));
|
||||
|
||||
AssertDictionary(extensions, new Dictionary<string, string>
|
||||
{
|
||||
{ "Microsoft.Azure.WebJobs.Extensions.Storage", "4.0.3" }
|
||||
});
|
||||
|
||||
void ValidateQueueTrigger(ExpandoObject b)
|
||||
{
|
||||
AssertExpandoObject(b, new Dictionary<string, object>
|
||||
|
@ -89,12 +101,11 @@ namespace Microsoft.Azure.Functions.SdkTests
|
|||
{
|
||||
AssertExpandoObject(b, new Dictionary<string, object>
|
||||
{
|
||||
{ "Name", "blobOutput" },
|
||||
{ "name", "blobOutput" },
|
||||
{ "Type", "Blob" },
|
||||
{ "Direction", "Out" },
|
||||
{ "blobPath", "container1/hello.txt" },
|
||||
{ "Connection", "MyOtherConnection" },
|
||||
{ "access", "ReadWrite" }
|
||||
{ "Connection", "MyOtherConnection" }
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -113,11 +124,12 @@ namespace Microsoft.Azure.Functions.SdkTests
|
|||
});
|
||||
}
|
||||
|
||||
// TODO: Callout - output binding will have different case for "name"
|
||||
void ValidateQueueOutput(ExpandoObject b)
|
||||
{
|
||||
AssertExpandoObject(b, new Dictionary<string, object>
|
||||
{
|
||||
{ "Name", "queueOutput" },
|
||||
{ "name", "queueOutput" },
|
||||
{ "Type", "Queue" },
|
||||
{ "Direction", "Out" },
|
||||
{ "queueName", "queue2" },
|
||||
|
@ -131,10 +143,16 @@ namespace Microsoft.Azure.Functions.SdkTests
|
|||
var generator = new FunctionMetadataGenerator();
|
||||
var typeDef = TestUtility.GetTypeDefinition(typeof(Timer));
|
||||
var functions = generator.GenerateFunctionMetadata(typeDef);
|
||||
var extensions = generator.Extensions;
|
||||
|
||||
ValidateFunction(functions.Single(), "TimerFunction", GetEntryPoint(nameof(Timer), nameof(Timer.RunTimer)),
|
||||
b => ValidateTrigger(b));
|
||||
|
||||
AssertDictionary(extensions, new Dictionary<string, string>
|
||||
{
|
||||
{ "Microsoft.Azure.WebJobs.Extensions", "4.0.1" }
|
||||
});
|
||||
|
||||
void ValidateTrigger(ExpandoObject b)
|
||||
{
|
||||
AssertExpandoObject(b, new Dictionary<string, object>
|
||||
|
@ -153,7 +171,7 @@ namespace Microsoft.Azure.Functions.SdkTests
|
|||
private void ValidateFunction(SdkFunctionMetadata sdkFunctionMetadata, string name, string entryPoint, params Action<ExpandoObject>[] bindingValidations)
|
||||
{
|
||||
Assert.Equal(name, sdkFunctionMetadata.Name);
|
||||
Assert.Equal($"bin/{_thisAssembly.GetName().Name}.dll", sdkFunctionMetadata.ScriptFile);
|
||||
Assert.Equal($"{_thisAssembly.GetName().Name}.dll", sdkFunctionMetadata.ScriptFile);
|
||||
Assert.Equal("dotnet-isolated", sdkFunctionMetadata.Language);
|
||||
Assert.Equal(sdkFunctionMetadata.EntryPoint, entryPoint);
|
||||
Assert.Null(sdkFunctionMetadata.FunctionDirectory);
|
||||
|
@ -167,6 +185,11 @@ namespace Microsoft.Azure.Functions.SdkTests
|
|||
{
|
||||
var dict = (IDictionary<string, object>)expando;
|
||||
|
||||
AssertDictionary(dict, expected);
|
||||
}
|
||||
|
||||
private static void AssertDictionary<K,V>(IDictionary<K, V> dict, IDictionary<K, V> expected)
|
||||
{
|
||||
Assert.Equal(expected.Count, dict.Count);
|
||||
|
||||
foreach (var kvp in expected)
|
||||
|
@ -193,17 +216,17 @@ namespace Microsoft.Azure.Functions.SdkTests
|
|||
}
|
||||
|
||||
[FunctionName("QueueToBlobFunction")]
|
||||
[BlobOutput("blobOutput", "container1/hello.txt", Connection = "MyOtherConnection")]
|
||||
public void QueueToBlob(
|
||||
[QueueTrigger("queueName", Connection = "MyConnection")] string queuePayload,
|
||||
[Blob("container1/hello.txt", FileAccess.ReadWrite, Connection = "MyOtherConnection")] OutputBinding<string> blobOutput)
|
||||
[QueueTrigger("queueName", Connection = "MyConnection")] string queuePayload)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
[FunctionName("BlobToQueueFunction")]
|
||||
[QueueOutput("queueOutput", "queue2")]
|
||||
public void BlobToQueue(
|
||||
[BlobTrigger("container2/%file%")] string blob,
|
||||
[Queue("queue2")] OutputBinding<string> queueOutput)
|
||||
[BlobTrigger("container2/%file%")] string blob)
|
||||
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
|
|
|
@ -8,10 +8,7 @@
|
|||
<AssemblyOriginatorKeyFile>..\..\key.snk</AssemblyOriginatorKeyFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions" Version="4.0.1" />
|
||||
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.Http" Version="3.0.8" />
|
||||
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.Storage" Version="4.0.3" />
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
|
||||
<PackageReference Include="Moq" Version="4.15.2" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
|
@ -27,6 +24,9 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\extensions\Worker.Extensions.Http\Worker.Extensions.Http.csproj" />
|
||||
<ProjectReference Include="..\..\extensions\Worker.Extensions.Storage\Worker.Extensions.Storage.csproj" />
|
||||
<ProjectReference Include="..\..\extensions\Worker.Extensions.Timer\Worker.Extensions.Timer.csproj" />
|
||||
<ProjectReference Include="..\..\sdk\FunctionMetadataLoaderExtension\FunctionMetadataLoaderExtension.csproj" />
|
||||
<ProjectReference Include="..\..\sdk\Sdk\Sdk.csproj" />
|
||||
<ProjectReference Include="..\..\src\DotNetWorker\DotNetWorker.csproj" />
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
[
|
||||
{
|
||||
"name": "Function1",
|
||||
"scriptFile": "bin/FunctionApp.dll",
|
||||
"scriptFile": "FunctionApp.dll",
|
||||
"entryPoint": "FunctionApp.Function1.Run",
|
||||
"language": "dotnet-isolated",
|
||||
"properties": {
|
||||
|
@ -41,7 +41,7 @@
|
|||
},
|
||||
{
|
||||
"name": "Function2",
|
||||
"scriptFile": "bin/FunctionApp.dll",
|
||||
"scriptFile": "FunctionApp.dll",
|
||||
"entryPoint": "FunctionApp.Function2.Run",
|
||||
"language": "dotnet-isolated",
|
||||
"properties": {
|
||||
|
@ -66,7 +66,7 @@
|
|||
},
|
||||
{
|
||||
"name": "Function3",
|
||||
"scriptFile": "bin/FunctionApp.dll",
|
||||
"scriptFile": "FunctionApp.dll",
|
||||
"entryPoint": "FunctionApp.Function3.Run",
|
||||
"language": "dotnet-isolated",
|
||||
"properties": {
|
||||
|
@ -99,7 +99,7 @@
|
|||
},
|
||||
{
|
||||
"name": "Function4",
|
||||
"scriptFile": "bin/FunctionApp.dll",
|
||||
"scriptFile": "FunctionApp.dll",
|
||||
"entryPoint": "FunctionApp.Function4.Run",
|
||||
"language": "dotnet-isolated",
|
||||
"properties": {
|
||||
|
@ -125,7 +125,7 @@
|
|||
},
|
||||
{
|
||||
"name": "Function5",
|
||||
"scriptFile": "bin/FunctionApp.dll",
|
||||
"scriptFile": "FunctionApp.dll",
|
||||
"entryPoint": "FunctionApp.Function5.Run",
|
||||
"language": "dotnet-isolated",
|
||||
"properties": {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
|
@ -39,14 +39,14 @@ namespace Microsoft.Azure.Functions.SdkE2ETests
|
|||
exitCode = await new ProcessWrapper().RunProcess(TestUtility.DotNetExecutable, dotnetArgs, projectFileDirectory, testOutputHelper: _testOutputHelper);
|
||||
Assert.True(exitCode.HasValue && exitCode.Value == 0);
|
||||
|
||||
// Make sure files are in /bin
|
||||
string additionalBinDir = Path.Combine(outputDir, "bin");
|
||||
Assert.True(Directory.Exists(additionalBinDir));
|
||||
var files = Directory.EnumerateFiles(additionalBinDir);
|
||||
// Make sure files are in /.azurefunctions
|
||||
string azureFunctionsDir = Path.Combine(outputDir, ".azurefunctions");
|
||||
Assert.True(Directory.Exists(azureFunctionsDir));
|
||||
var files = Directory.EnumerateFiles(azureFunctionsDir);
|
||||
|
||||
// Verify files are present
|
||||
string metadataLoaderPath = Path.Combine(additionalBinDir, "Microsoft.Azure.WebJobs.Extensions.FunctionMetadataLoader.dll");
|
||||
string extensionsJsonPath = Path.Combine(additionalBinDir, "extensions.json");
|
||||
string metadataLoaderPath = Path.Combine(azureFunctionsDir, "Microsoft.Azure.WebJobs.Extensions.FunctionMetadataLoader.dll");
|
||||
string extensionsJsonPath = Path.Combine(outputDir, "extensions.json");
|
||||
string functionsMetadataPath = Path.Combine(outputDir, "functions.metadata");
|
||||
Assert.True(File.Exists(metadataLoaderPath));
|
||||
Assert.True(File.Exists(extensionsJsonPath));
|
||||
|
@ -59,8 +59,12 @@ namespace Microsoft.Azure.Functions.SdkE2ETests
|
|||
{
|
||||
extensions = new[]
|
||||
{
|
||||
new Extension("Startup", "Microsoft.Azure.WebJobs.Extensions.FunctionMetadataLoader.Startup, Microsoft.Azure.WebJobs.Extensions.FunctionMetadataLoader, Version=1.0.0.0, Culture=neutral, PublicKeyToken=551316b6919f366c"),
|
||||
new Extension("AzureStorage", "Microsoft.Azure.WebJobs.Extensions.Storage.AzureStorageWebJobsStartup, Microsoft.Azure.WebJobs.Extensions.Storage, Version=4.0.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35")
|
||||
new Extension("Startup",
|
||||
"Microsoft.Azure.WebJobs.Extensions.FunctionMetadataLoader.Startup, Microsoft.Azure.WebJobs.Extensions.FunctionMetadataLoader, Version=1.0.0.0, Culture=neutral, PublicKeyToken=551316b6919f366c",
|
||||
@"./.azurefunctions/Microsoft.Azure.WebJobs.Extensions.FunctionMetadataLoader.dll"),
|
||||
new Extension("AzureStorage",
|
||||
"Microsoft.Azure.WebJobs.Extensions.Storage.AzureStorageWebJobsStartup, Microsoft.Azure.WebJobs.Extensions.Storage, Version=4.0.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35",
|
||||
@"./.azurefunctions/Microsoft.Azure.WebJobs.Extensions.Storage.dll")
|
||||
}
|
||||
});
|
||||
Assert.True(JToken.DeepEquals(extensionsJsonContents, expected), $"Actual: {extensionsJsonContents}{Environment.NewLine}Expected: {expected}");
|
||||
|
@ -71,10 +75,11 @@ namespace Microsoft.Azure.Functions.SdkE2ETests
|
|||
|
||||
private class Extension
|
||||
{
|
||||
public Extension(string name, string typeName)
|
||||
public Extension(string name, string typeName, string hintPath)
|
||||
{
|
||||
Name = name;
|
||||
TypeName = typeName;
|
||||
HintPath = hintPath;
|
||||
}
|
||||
|
||||
[JsonProperty("name")]
|
||||
|
@ -82,6 +87,9 @@ namespace Microsoft.Azure.Functions.SdkE2ETests
|
|||
|
||||
[JsonProperty("typeName")]
|
||||
public string TypeName { get; set; }
|
||||
|
||||
[JsonProperty("hintPath")]
|
||||
public string HintPath { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче