Skip to content

Commit 6317846

Browse files
authored
DI registrations fix and Functions v4 sample (#53)
1 parent 65953b0 commit 6317846

12 files changed

Lines changed: 235 additions & 3 deletions

FunctionsValidationFilter.sln

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
2424
README.md = README.md
2525
EndProjectSection
2626
EndProject
27+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sample", "sample", "{2E293A51-1C16-42DB-B82D-C51B1CCE6A93}"
28+
EndProject
29+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleFunctionApp", "sample\SampleFunctionApp\SampleFunctionApp.csproj", "{0143B82E-2680-498B-ABC5-D4D7C0D628AF}"
30+
EndProject
2731
Global
2832
GlobalSection(SolutionConfigurationPlatforms) = preSolution
2933
Debug|Any CPU = Debug|Any CPU
@@ -42,6 +46,10 @@ Global
4246
{B1B902B8-EB41-413A-B96B-DAE971DAF11C}.Debug|Any CPU.Build.0 = Debug|Any CPU
4347
{B1B902B8-EB41-413A-B96B-DAE971DAF11C}.Release|Any CPU.ActiveCfg = Release|Any CPU
4448
{B1B902B8-EB41-413A-B96B-DAE971DAF11C}.Release|Any CPU.Build.0 = Release|Any CPU
49+
{0143B82E-2680-498B-ABC5-D4D7C0D628AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
50+
{0143B82E-2680-498B-ABC5-D4D7C0D628AF}.Debug|Any CPU.Build.0 = Debug|Any CPU
51+
{0143B82E-2680-498B-ABC5-D4D7C0D628AF}.Release|Any CPU.ActiveCfg = Release|Any CPU
52+
{0143B82E-2680-498B-ABC5-D4D7C0D628AF}.Release|Any CPU.Build.0 = Release|Any CPU
4553
EndGlobalSection
4654
GlobalSection(SolutionProperties) = preSolution
4755
HideSolutionNode = FALSE
@@ -50,6 +58,7 @@ Global
5058
{4314B709-63A3-466F-B849-47141DADD1CC} = {4548709D-6DFA-4CE9-A6EF-F20F2D210057}
5159
{5AB4FFC0-D80D-41CC-A5A2-A1099B0285DB} = {8E7F3B16-8C19-4028-8D40-201A767EACF3}
5260
{B1B902B8-EB41-413A-B96B-DAE971DAF11C} = {8E7F3B16-8C19-4028-8D40-201A767EACF3}
61+
{0143B82E-2680-498B-ABC5-D4D7C0D628AF} = {2E293A51-1C16-42DB-B82D-C51B1CCE6A93}
5362
EndGlobalSection
5463
GlobalSection(ExtensibilityGlobals) = postSolution
5564
SolutionGuid = {CDCB1E10-4043-4808-A8D9-DFAF5833A118}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
using FluentValidation;
2+
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions;
3+
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Configurations;
4+
using Microsoft.Extensions.DependencyInjection;
5+
using Microsoft.Extensions.Hosting;
6+
using Microsoft.OpenApi.Models;
7+
8+
var host = new HostBuilder()
9+
.ConfigureFunctionsWorkerDefaults()
10+
.ConfigureServices(services =>
11+
{
12+
services.AddSingleton<IOpenApiConfigurationOptions>(_ =>
13+
{
14+
var options = new OpenApiConfigurationOptions()
15+
{
16+
Info = new OpenApiInfo()
17+
{
18+
Version = DefaultOpenApiConfigurationOptions.GetOpenApiDocVersion(),
19+
Title = $"{DefaultOpenApiConfigurationOptions.GetOpenApiDocTitle()}",
20+
Description = DefaultOpenApiConfigurationOptions.GetOpenApiDocDescription(),
21+
Contact = new OpenApiContact()
22+
{
23+
Name = "sample",
24+
Email = "test@example.com",
25+
Url = new Uri("https://github.com/NewDayTechnology/NewDay.Extensions.FunctionsValidationFilter"),
26+
},
27+
License = new OpenApiLicense()
28+
{
29+
Name = "Apache-2.0",
30+
Url = new Uri("https://opensource.org/license/apache-2-0/"),
31+
}
32+
},
33+
Servers = DefaultOpenApiConfigurationOptions.GetHostNames(),
34+
OpenApiVersion = DefaultOpenApiConfigurationOptions.GetOpenApiVersion(),
35+
IncludeRequestingHostName = DefaultOpenApiConfigurationOptions.IsFunctionsRuntimeEnvironmentDevelopment(),
36+
ForceHttps = DefaultOpenApiConfigurationOptions.IsHttpsForced(),
37+
ForceHttp = DefaultOpenApiConfigurationOptions.IsHttpForced(),
38+
};
39+
40+
options.DocumentFilters.AddFunctionsValidationFilter<Program>();
41+
42+
return options;
43+
});
44+
services.AddValidatorsFromAssembly(typeof(Program).Assembly);
45+
})
46+
.Build();
47+
48+
host.Run();
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"profiles": {
3+
"SampleFunctionApp": {
4+
"commandName": "Project",
5+
"commandLineArgs": "--port 7194",
6+
"launchBrowser": false
7+
}
8+
}
9+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"dependencies": {
3+
"appInsights1": {
4+
"type": "appInsights"
5+
},
6+
"storage1": {
7+
"type": "storage",
8+
"connectionId": "AzureWebJobsStorage"
9+
}
10+
}
11+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"dependencies": {
3+
"appInsights1": {
4+
"type": "appInsights.sdk"
5+
},
6+
"storage1": {
7+
"type": "storage.emulator",
8+
"connectionId": "AzureWebJobsStorage"
9+
}
10+
}
11+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
using System.ComponentModel.DataAnnotations;
2+
using System.Net;
3+
using FluentValidation;
4+
using Microsoft.AspNetCore.Mvc;
5+
using Microsoft.Azure.Functions.Worker;
6+
using Microsoft.Azure.Functions.Worker.Http;
7+
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Attributes;
8+
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Enums;
9+
using Microsoft.Extensions.Logging;
10+
using Microsoft.OpenApi.Models;
11+
12+
namespace SampleFunctionApp
13+
{
14+
public class SampleFunction
15+
{
16+
private readonly ILogger _logger;
17+
private readonly IValidator<SampleInput> _validator;
18+
19+
public SampleFunction(ILoggerFactory loggerFactory, IValidator<SampleInput> validator)
20+
{
21+
_logger = loggerFactory.CreateLogger<SampleFunction>();
22+
_validator = validator;
23+
}
24+
25+
[Function("SampleFunction")]
26+
[OpenApiOperation(operationId: "sampleFunction", tags: new[] { "sample" }, Summary = "Sample input validation", Visibility = OpenApiVisibilityType.Important)]
27+
[OpenApiRequestBody(contentType: "application/json", bodyType: typeof(SampleInput), Required = true)]
28+
[OpenApiResponseWithoutBody(statusCode: HttpStatusCode.OK)]
29+
[OpenApiResponseWithBody(statusCode: HttpStatusCode.BadRequest, contentType: "application/json", bodyType: typeof(ProblemDetails), Summary = "Invalid input supplied")]
30+
public async Task<HttpResponseData> Run([HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequestData req)
31+
{
32+
_logger.LogInformation("C# HTTP trigger function processed a request.");
33+
34+
var input = await req.ReadFromJsonAsync<SampleInput>();
35+
if (input is null)
36+
{
37+
var error = req.CreateResponse(HttpStatusCode.BadRequest);
38+
await error.WriteAsJsonAsync(
39+
new ProblemDetails
40+
{
41+
Status = (int)HttpStatusCode.BadRequest,
42+
Title = "Missing body"
43+
});
44+
45+
return error;
46+
}
47+
48+
var validationResult = await _validator.ValidateAsync(input);
49+
if (!validationResult.IsValid)
50+
{
51+
var error = req.CreateResponse(HttpStatusCode.BadRequest);
52+
await error.WriteAsJsonAsync(
53+
new ProblemDetails
54+
{
55+
Status = (int)HttpStatusCode.BadRequest,
56+
Title = "One or more validation errors occurred.",
57+
Extensions = {
58+
["errors"] = validationResult.Errors.Select(e => new
59+
{
60+
e.ErrorCode,
61+
e.PropertyName,
62+
e.ErrorMessage
63+
})
64+
}
65+
});
66+
67+
return error;
68+
}
69+
70+
return req.CreateResponse(HttpStatusCode.OK);
71+
}
72+
}
73+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<TargetFramework>net8.0</TargetFramework>
4+
<AzureFunctionsVersion>v4</AzureFunctionsVersion>
5+
<OutputType>Exe</OutputType>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
</PropertyGroup>
9+
<ItemGroup>
10+
<PackageReference Include="Microsoft.Azure.Functions.Worker" Version="1.19.0" />
11+
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http" Version="3.0.13" />
12+
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.OpenApi" Version="1.5.1" />
13+
<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="1.14.0" />
14+
<PackageReference Include="FluentValidation" Version="11.4.0" />
15+
</ItemGroup>
16+
<ItemGroup>
17+
<ProjectReference Include="..\..\src\FunctionsValidationFilter.DependencyInjection\FunctionsValidationFilter.DependencyInjection.csproj" />
18+
<ProjectReference Include="..\..\src\FunctionsValidationFilter\FunctionsValidationFilter.csproj" />
19+
</ItemGroup>
20+
<ItemGroup>
21+
<None Update="host.json">
22+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
23+
</None>
24+
<None Update="local.settings.json">
25+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
26+
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
27+
</None>
28+
</ItemGroup>
29+
<ItemGroup>
30+
<Using Include="System.Threading.ExecutionContext" Alias="ExecutionContext" />
31+
</ItemGroup>
32+
</Project>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace SampleFunctionApp
2+
{
3+
public class SampleInput
4+
{
5+
public string Name { get; set; } = null!;
6+
public int Value { get; set; }
7+
}
8+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using FluentValidation;
2+
using SampleFunctionApp;
3+
4+
namespace NewDay.Extensions.SampleFunctionApp;
5+
6+
public class SampleInputValidator : AbstractValidator<SampleInput>
7+
{
8+
public SampleInputValidator()
9+
{
10+
RuleFor(model => model.Name).NotNull().NotEmpty();
11+
RuleFor(model => model.Value).GreaterThan(0);
12+
}
13+
}

sample/SampleFunctionApp/host.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"version": "2.0",
3+
"logging": {
4+
"applicationInsights": {
5+
"samplingSettings": {
6+
"isEnabled": true,
7+
"excludedTypes": "Request"
8+
},
9+
"enableLiveMetricsFilters": true
10+
}
11+
}
12+
}

0 commit comments

Comments
 (0)