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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 6 additions & 9 deletions release_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,11 @@
- My change description (#PR/#issue)
-->

### Microsoft.Azure.Functions.Worker (metapackage) 2.1.0
### Microsoft.Azure.Functions.Worker (metapackage) <version>
-

- `AZURE_FUNCTIONS_` environment variables are now loaded correctly when using `FunctionsApplicationBuilder`. (#2878)
### Microsoft.Azure.Functions.Worker.Core <version>
- Support setting `IFunctionExecutor` in invocation features: `FunctionContext.Features` (#3200)

### Microsoft.Azure.Functions.Worker.Core 2.1.0

- Support for function metadata transforms (#3145)

### Microsoft.Azure.Functions.Worker.Grpc 2.1.0

- Updated to use the new metadata manage and leverage metadata transforms (#3145)
### Microsoft.Azure.Functions.Worker.Grpc <version>
-
16 changes: 9 additions & 7 deletions src/DotNetWorker.Core/Pipeline/FunctionExecutionMiddleware.cs
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
// 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.Threading.Tasks;
using Microsoft.Azure.Functions.Worker.Invocation;

namespace Microsoft.Azure.Functions.Worker.Pipeline
{
internal class FunctionExecutionMiddleware
internal class FunctionExecutionMiddleware(IFunctionExecutor functionExecutor)
{
private readonly IFunctionExecutor _functionExecutor;

public FunctionExecutionMiddleware(IFunctionExecutor functionExecutor)
{
_functionExecutor = functionExecutor;
}
private readonly IFunctionExecutor _functionExecutor = functionExecutor
?? throw new ArgumentNullException(nameof(functionExecutor));

public Task Invoke(FunctionContext context)
{
if (context.Features.Get<IFunctionExecutor>() is { } executor)
{
return executor.ExecuteAsync(context).AsTask();
}

return _functionExecutor.ExecuteAsync(context).AsTask();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
using Moq;
using Xunit;

#nullable enable

namespace Microsoft.Azure.Functions.Worker.Tests.FunctionMetadata
{
public class DefaultFunctionMetadataManagerTests
Expand Down Expand Up @@ -70,17 +72,17 @@ public async Task GetFunctionMetadataAsync_TransformerThrows_LogsAndThrows()

var manager = new DefaultFunctionMetadataManager(
mockProvider.Object,
new[] { mockTransformer.Object },
[mockTransformer.Object],
mockLogger.Object);

var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => manager.GetFunctionMetadataAsync("test"));
Assert.Equal("fail", ex.Message);
mockLogger.Verify(l => l.Log(
LogLevel.Error,
It.IsAny<EventId>(),
It.Is<It.IsAnyType>((v, t) => v.ToString().Contains("ThrowingTransformer")),
It.Is<It.IsAnyType>((v, t) => v.ToString()!.Contains("ThrowingTransformer")),
It.IsAny<Exception>(),
It.IsAny<Func<It.IsAnyType, Exception, string>>()), Times.Once);
It.IsAny<Func<It.IsAnyType, Exception?, string>>()), Times.Once);
}

private class TestFunctionMetadata : IFunctionMetadata
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// 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.Threading.Tasks;
using Microsoft.Azure.Functions.Worker.Invocation;
using Moq;
using Xunit;

namespace Microsoft.Azure.Functions.Worker.Pipeline.Tests
{
public class FunctionsExecutionMiddlewareTests
{
[Fact]
public void Ctor_ThrowsArgumentNullException_WhenFunctionExecutorIsNull()
{
// Arrange & Act & Assert
var exception = Assert.Throws<ArgumentNullException>(() => new FunctionExecutionMiddleware(null!));
Assert.Equal("functionExecutor", exception.ParamName);
}

[Fact]
public async Task Invoke_NoFeature_CallsInjectedExecutor()
{
// Arrange
Mock<IFunctionExecutor> functionExecutorMock = new(MockBehavior.Strict);
FunctionExecutionMiddleware middleware = new(functionExecutorMock.Object);
Mock<FunctionContext> functionContextMock = new(MockBehavior.Strict);
functionContextMock.Setup(m => m.Features).Returns(new InvocationFeatures([]));

functionExecutorMock
.Setup(m => m.ExecuteAsync(functionContextMock.Object))
.Returns(ValueTask.CompletedTask)
.Verifiable();

// Act
await middleware.Invoke(functionContextMock.Object);

// Assert
functionExecutorMock.Verify(f => f.ExecuteAsync(functionContextMock.Object), Times.Once);
}

[Fact]
public async Task Invoke_Feature_CallsFeatureExecutor()
{
// Arrange
Mock<IFunctionExecutor> functionExecutorMock1 = new(MockBehavior.Strict);
Mock<IFunctionExecutor> functionExecutorMock2 = new(MockBehavior.Strict);
FunctionExecutionMiddleware middleware = new(functionExecutorMock1.Object);
Mock<FunctionContext> functionContextMock = new(MockBehavior.Strict);

InvocationFeatures features = new([]);
features.Set(functionExecutorMock2.Object);
functionContextMock.Setup(m => m.Features).Returns(features);

functionExecutorMock2
.Setup(m => m.ExecuteAsync(functionContextMock.Object))
.Returns(ValueTask.CompletedTask)
.Verifiable();

// Act
await middleware.Invoke(functionContextMock.Object);

// Assert
functionExecutorMock1.Verify(f => f.ExecuteAsync(It.IsAny<FunctionContext>()), Times.Never);
functionExecutorMock2.Verify(f => f.ExecuteAsync(functionContextMock.Object), Times.Once);
}
}
}