Skip to content

Commit

Permalink
Version endpoint (#330)
Browse files Browse the repository at this point in the history
  • Loading branch information
lkurzyniec authored Jun 11, 2024
1 parent 847a50f commit 2a111ee
Show file tree
Hide file tree
Showing 20 changed files with 209 additions and 33 deletions.
Binary file modified .assets/api.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified .assets/core.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions .github/workflows/docker-push.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ jobs:
uses: docker/build-push-action@v5
with:
push: true
build-args: |
VERSION=${{ env.VERSION }}
SHA=${{ github.sha }}
labels: ${{ steps.meta.outputs.labels }}
tags: |
${{ env.BASE_TAG }}:${{ env.VERSION }}
Expand Down
53 changes: 27 additions & 26 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -1,26 +1,27 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Launch API",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/src/HappyCode.NetCoreBoilerplate.Api/bin/Debug/netcoreapp3.1/HappyCode.NetCoreBoilerplate.Api.dll",
"args": [],
"cwd": "${workspaceFolder}/src/HappyCode.NetCoreBoilerplate.Api",
"stopAtEntry": false,
"serverReadyAction": {
"action": "openExternally",
"pattern": "^\\s*Now listening on:\\s+(https?://\\S+)",
},
"env": {
"ASPNETCORE_ENVIRONMENT": "Development",
"ASPNETCORE_URLS": "http://*:5000",
}
}
]
}
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Debug API",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/src/HappyCode.NetCoreBoilerplate.Api/bin/Debug/net8.0/HappyCode.NetCoreBoilerplate.Api.dll",
"args": [],
"cwd": "${workspaceFolder}/src/HappyCode.NetCoreBoilerplate.Api",
"stopAtEntry": false,
"serverReadyAction": {
"action": "openExternally",
"pattern": "\\bNow listening on:\\s+(https?://\\S+)",
"uriFormat": "%s/swagger"
},
"env": {
"ASPNETCORE_ENVIRONMENT": "Development",
"ASPNETCORE_URLS": "http://localhost:5000",
}
}
]
}
4 changes: 3 additions & 1 deletion .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
"type": "process",
"args": [
"build",
"${workspaceFolder}/src/HappyCode.NetCoreBoilerplate.Api/HappyCode.NetCoreBoilerplate.Api.csproj"
"${workspaceFolder}/src/HappyCode.NetCoreBoilerplate.Api/HappyCode.NetCoreBoilerplate.Api.csproj",
"-c",
"Debug",
],
"problemMatcher": "$msCompile"
}
Expand Down
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<PropertyGroup>
<Authors>Łukasz Kurzyniec</Authors>
<Copyright>Copyright © happy+code Łukasz Kurzyniec 2022</Copyright>
<Copyright>Copyright © happy+code Łukasz Kurzyniec 2024</Copyright>
<Version>2.0.0</Version>
</PropertyGroup>

Expand Down
19 changes: 16 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ After successful start of the solution in any of above option, check useful endp

* swagger - <http://localhost:5000/swagger/>
* health check - <http://localhost:5000/healthz/ready>
* version - <http://localhost:5000/version>

### Standalone

Expand Down Expand Up @@ -183,6 +184,10 @@ Generally it is totally up to you! But in case you do not have any plan, You can
* Configurations
* `Serilog` configuration place - [SerilogConfigurator.cs](src/HappyCode.NetCoreBoilerplate.Api/Infrastructure/Configurations/SerilogConfigurator.cs)
* `Swagger` registration place - [SwaggerRegistration.cs](src/HappyCode.NetCoreBoilerplate.Api/Infrastructure/Registrations/SwaggerRegistration.cs)
* Feature flag documentation filter - [FeatureFlagSwaggerDocumentFilter.cs](src/HappyCode.NetCoreBoilerplate.Api/Infrastructure/Filters/FeatureFlagSwaggerDocumentFilter.cs)
* Security requirement operation filter - [SecurityRequirementSwaggerOperationFilter.cs](src/HappyCode.NetCoreBoilerplate.Api/Infrastructure/Filters/SecurityRequirementSwaggerOperationFilter.cs)
* Logging
* Custom enricher to have version properties in logs - [VersionEnricher.cs](src/HappyCode.NetCoreBoilerplate.Api/Infrastructure/Logging/VersionEnricher.cs)
* Simple exemplary API controllers - [EmployeesController.cs](src/HappyCode.NetCoreBoilerplate.Api/Controllers/EmployeesController.cs), [CarsController.cs](src/HappyCode.NetCoreBoilerplate.Api/Controllers/CarsController.cs), [PingsController.cs](src/HappyCode.NetCoreBoilerplate.Api/Controllers/PingsController.cs)
* Example of BackgroundService - [PingWebsiteBackgroundService.cs](src/HappyCode.NetCoreBoilerplate.Api/BackgroundServices/PingWebsiteBackgroundService.cs)

Expand All @@ -199,6 +204,8 @@ Generally it is totally up to you! But in case you do not have any plan, You can
* DbContexts
* MySQL DbContext - [EmployeesContext.cs](src/HappyCode.NetCoreBoilerplate.Core/EmployeesContext.cs)
* MsSQL DbContext - [CarsContext.cs](src/HappyCode.NetCoreBoilerplate.Core/CarsContext.cs)
* Providers
* Version provider - [VersionProvider.cs](src/HappyCode.NetCoreBoilerplate.Core/Providers/VersionProvider.cs)
* Core registrations - [CoreRegistrations.cs](src/HappyCode.NetCoreBoilerplate.Core/Registrations/CoreRegistrations.cs)
* Exemplary MySQL repository - [EmployeeRepository.cs](src/HappyCode.NetCoreBoilerplate.Core/Repositories/EmployeeRepository.cs)
* Exemplary MsSQL service - [CarService.cs](src/HappyCode.NetCoreBoilerplate.Core/Services/CarService.cs)
Expand All @@ -224,21 +231,27 @@ Generally it is totally up to you! But in case you do not have any plan, You can
* Fixture with TestServer - [TestServerClientFixture.cs](test/HappyCode.NetCoreBoilerplate.Api.IntegrationTests/Infrastructure/TestServerClientFixture.cs)
* TestStartup with InMemory databases - [TestStartup.cs](test/HappyCode.NetCoreBoilerplate.Api.IntegrationTests/Infrastructure/TestStartup.cs)
* Simple data feeders - [EmployeeContextDataFeeder.cs](test/HappyCode.NetCoreBoilerplate.Api.IntegrationTests/Infrastructure/DataFeeders/EmployeeContextDataFeeder.cs), [CarsContextDataFeeder.cs](test/HappyCode.NetCoreBoilerplate.Api.IntegrationTests/Infrastructure/DataFeeders/CarsContextDataFeeder.cs)
* Exemplary tests - [EmployeesTests.cs](test/HappyCode.NetCoreBoilerplate.Api.IntegrationTests/EmployeesTests.cs), [CarsTests.cs](test/HappyCode.NetCoreBoilerplate.Api.IntegrationTests/CarsTests.cs)
* Fakes - [FakePingService.cs](test/HappyCode.NetCoreBoilerplate.Api.IntegrationTests/Infrastructure/Fakes/FakePingService.cs)
* Exemplary tests - [EmployeesTests.cs](test/HappyCode.NetCoreBoilerplate.Api.IntegrationTests/EmployeesTests.cs), [CarsTests.cs](test/HappyCode.NetCoreBoilerplate.Api.IntegrationTests/CarsTests.cs), [PingsTests.cs](test/HappyCode.NetCoreBoilerplate.Api.IntegrationTests/PingsTests.cs)

![HappyCode.NetCoreBoilerplate.Api.IntegrationTests](.assets/itests.png "HappyCode.NetCoreBoilerplate.Api.IntegrationTests")

### Unit tests

[HappyCode.NetCoreBoilerplate.Api.UnitTests](test/HappyCode.NetCoreBoilerplate.Api.UnitTests)

* Exemplary tests - [EmployeesControllerTests.cs](test/HappyCode.NetCoreBoilerplate.Api.UnitTests/Controllers/EmployeesControllerTests.cs)
* Unit tests of `ApiKeyAuthorizationFilter.cs` - [ApiKeyAuthorizationFilterTests.cs](test/HappyCode.NetCoreBoilerplate.Api.UnitTests/Infrastructure/Filters/ApiKeyAuthorizationFilterTests.cs)
* Exemplary tests - [EmployeesControllerTests.cs](test/HappyCode.NetCoreBoilerplate.Api.UnitTests/Controllers/EmployeesControllerTests.cs), [CarsControllerTests.cs](test/HappyCode.NetCoreBoilerplate.Api.UnitTests/Controllers/CarsControllerTests.cs), [PingsControllerTests.cs](test/HappyCode.NetCoreBoilerplate.Api.UnitTests/Controllers/PingsControllerTests.cs)
* API Infrastructure Unit tests
* [ApiKeyAuthorizationFilterTests.cs](test/HappyCode.NetCoreBoilerplate.Api.UnitTests/Infrastructure/Filters/ApiKeyAuthorizationFilterTests.cs)
* [ValidateModelStateFilterTests.cs](test/HappyCode.NetCoreBoilerplate.Api.UnitTests/Infrastructure/Filters/ValidateModelStateFilterTests.cs)
* [VersionEnricherTests.cs](test/HappyCode.NetCoreBoilerplate.Api.UnitTests/Infrastructure/Logging/VersionEnricherTests.cs)

[HappyCode.NetCoreBoilerplate.Core.UnitTests](test/HappyCode.NetCoreBoilerplate.Core.UnitTests)

* Extension methods to mock `DbSet` faster - [EnumerableExtensions.cs](test/HappyCode.NetCoreBoilerplate.Core.UnitTests/Extensions/EnumerableExtensions.cs)
* Exemplary tests - [EmployeeRepositoryTests.cs](test/HappyCode.NetCoreBoilerplate.Core.UnitTests/Repositories/EmployeeRepositoryTests.cs), [CarServiceTests.cs](test/HappyCode.NetCoreBoilerplate.Core.UnitTests/Services/CarServiceTests.cs)
* Providers tests
* [VersionProviderTests.cs](test/HappyCode.NetCoreBoilerplate.Core.UnitTests/Providers/VersionProviderTests.cs) with [HappyCode.NetCoreBoilerplate.Core.UnitTests.runsettings](test/HappyCode.NetCoreBoilerplate.Core.UnitTests/HappyCode.NetCoreBoilerplate.Core.UnitTests.runsettings)

![HappyCode.NetCoreBoilerplate.Core.UnitTests](.assets/utests.png "HappyCode.NetCoreBoilerplate.Core.UnitTests")

Expand Down
6 changes: 6 additions & 0 deletions dockerfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
ARG VERSION=2.0.0
ARG SHA=none

FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
USER $APP_UID
WORKDIR /app
Expand Down Expand Up @@ -45,6 +48,9 @@ COPY --from=publish /app .
ENV DOTNET_NOLOGO=true
ENV DOTNET_CLI_TELEMETRY_OPTOUT=true

ENV HC_SHA=${SHA}
ENV HC_VERSION=${VERSION}

HEALTHCHECK --interval=5m --timeout=3s --start-period=10s --retries=1 \
CMD curl --fail http://localhost:8080/healthz/live || exit 1

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using HappyCode.NetCoreBoilerplate.Api.Infrastructure.Logging;
using Microsoft.Extensions.Configuration;
using Serilog;
using Serilog.Core;
Expand All @@ -11,6 +12,7 @@ public static Logger CreateLogger()
var configuration = LoadAppConfiguration();
return new LoggerConfiguration()
.ReadFrom.Configuration(configuration)
.Enrich.With(new VersionEnricher(new ()))
.CreateLogger();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using HappyCode.NetCoreBoilerplate.Core.Providers;
using Serilog.Core;
using Serilog.Events;

namespace HappyCode.NetCoreBoilerplate.Api.Infrastructure.Logging;

public class VersionEnricher : ILogEventEnricher
{
private readonly VersionProvider _versionProvider;

public VersionEnricher(VersionProvider versionProvider)
{
_versionProvider = versionProvider;
}

public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
{
foreach (var item in _versionProvider.VersionEntries)
{
logEvent.AddPropertyIfAbsent(new LogEventProperty(item.Key, new ScalarValue(item.Value)));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_URLS": "http://localhost:5000",
"ASPNETCORE_ENVIRONMENT": "Development"
"ASPNETCORE_ENVIRONMENT": "Development",
"HC_SHA": "2a6ba3cac8416214cfa84eb1f9092f130427479f",
"HC_VERSION": "2.0.0"
}
}
}
}
}
5 changes: 5 additions & 0 deletions src/HappyCode.NetCoreBoilerplate.Api/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@
using HappyCode.NetCoreBoilerplate.Api.Infrastructure.Registrations;
using HappyCode.NetCoreBoilerplate.BooksModule;
using HappyCode.NetCoreBoilerplate.Core;
using HappyCode.NetCoreBoilerplate.Core.Providers;
using HappyCode.NetCoreBoilerplate.Core.Registrations;
using HappyCode.NetCoreBoilerplate.Core.Settings;
using HealthChecks.UI.Client;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
Expand Down Expand Up @@ -91,6 +93,9 @@ public virtual void Configure(IApplicationBuilder app, IWebHostEnvironment env)
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse,
}).ShortCircuit();

endpoints.MapGet("/version", (VersionProvider provider) => provider.VersionEntries)
.ExcludeFromDescription();

endpoints.MapControllers();
endpoints.MapBooksModule();
});
Expand Down
24 changes: 24 additions & 0 deletions src/HappyCode.NetCoreBoilerplate.Core/Providers/VersionProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System.Collections;
using System.Linq;

namespace HappyCode.NetCoreBoilerplate.Core.Providers;

public class VersionProvider
{
private const string PREFIX = "HC_";

private readonly Lazy<Dictionary<string, string>> _versionEntries = new(GetVersionEntries);

private static Dictionary<string, string> GetVersionEntries()
{
var variables = Environment.GetEnvironmentVariables()
.Cast<DictionaryEntry>()
.Where(x => x.Key.ToString().StartsWith(PREFIX))
.ToDictionary(
x => x.Key.ToString().Remove(0, PREFIX.Length),
y => y.Value.ToString());
return variables;
}

public Dictionary<string, string> VersionEntries => _versionEntries.Value;
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using HappyCode.NetCoreBoilerplate.Core.Providers;
using HappyCode.NetCoreBoilerplate.Core.Repositories;
using HappyCode.NetCoreBoilerplate.Core.Services;
using Microsoft.Extensions.DependencyInjection;
Expand All @@ -10,6 +11,7 @@ public static IServiceCollection AddCoreComponents(this IServiceCollection servi
{
services.AddTransient<IEmployeeRepository, EmployeeRepository>();
services.AddScoped<ICarService, CarService>();
services.AddSingleton<VersionProvider>();

return services;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<RunSettingsFilePath>$(MSBuildProjectDirectory)\HappyCode.NetCoreBoilerplate.Api.UnitTests.runsettings</RunSettingsFilePath>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="AutoFixture" />
<PackageReference Include="AutoFixture.Xunit2" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<RunSettings>
<RunConfiguration>
<EnvironmentVariables>
<TEST_ENV>TEST_VALUE</TEST_ENV>
<HC_SHA>36b90293</HC_SHA>
<HC_VERSION>9.9.9</HC_VERSION>
</EnvironmentVariables>
</RunConfiguration>
</RunSettings>
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using System;
using FluentAssertions;
using HappyCode.NetCoreBoilerplate.Api.Infrastructure.Logging;
using HappyCode.NetCoreBoilerplate.Core.Providers;
using Serilog.Events;
using Xunit;

namespace HappyCode.NetCoreBoilerplate.Api.UnitTests.Infrastructure.Logging;

public class VersionEnricherTests
{
private readonly VersionEnricher _sut;

public VersionEnricherTests()
{
_sut = new VersionEnricher(new VersionProvider());
}

[Fact]
public void Properties_should_be_available()
{
// Arrange
var logEvent = GetEmptyLogEvent();

// Act
_sut.Enrich(logEvent, null);

// Assert
logEvent.Properties["SHA"].ToString().Should().Contain("36b90293");
logEvent.Properties["VERSION"].ToString().Should().Contain("9.9.9");
}

private static LogEvent GetEmptyLogEvent()
{
return new LogEvent(DateTimeOffset.UtcNow, LogEventLevel.Verbose, null,
new MessageTemplate(Guid.NewGuid().ToString(), []), []);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<RunSettingsFilePath>$(MSBuildProjectDirectory)\HappyCode.NetCoreBoilerplate.Core.UnitTests.runsettings</RunSettingsFilePath>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="AutoFixture" />
<PackageReference Include="AutoFixture.Xunit2" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<RunSettings>
<RunConfiguration>
<EnvironmentVariables>
<TEST_ENV>TEST_VALUE</TEST_ENV>
<HC_SHA>36b90293</HC_SHA>
<HC_VERSION>9.9.9</HC_VERSION>
</EnvironmentVariables>
</RunConfiguration>
</RunSettings>
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using FluentAssertions;
using HappyCode.NetCoreBoilerplate.Core.Providers;
using Xunit;

namespace HappyCode.NetCoreBoilerplate.Core.UnitTests.Providers;

public class VersionProviderTests
{
private readonly VersionProvider _provider;

public VersionProviderTests()
{
_provider = new VersionProvider();
}

[Fact]
public void Provided_should_returns_expected_values()
{
// act
var result = _provider.VersionEntries;

// assert
result.Should().NotContainKeys("TEST_ENV", "HC_SHA", "HC_VERSION");
result.Should().ContainKeys("SHA", "VERSION");
result.Should().HaveCount(2);
}
}

0 comments on commit 2a111ee

Please sign in to comment.