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
31 changes: 16 additions & 15 deletions .github/workflows/ci_build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,39 +10,41 @@ on:
- 'releases/**'
pull_request:
branches: [ main ]
paths:
- '**/*.cs'
- '**/*.csproj'

jobs:
build:

name: build-${{matrix.os}}-${{matrix.dotnet}}
name: build-${{matrix.os}}
runs-on: ${{ matrix.os }}
defaults:
run:
working-directory: src
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macOS-latest]
dotnet: ['8.x'] # Just a single target for now

steps:
- uses: actions/checkout@v3
- name: Setup .NET Core

- name: Setup .NET 8
uses: actions/setup-dotnet@v3
with:
dotnet-version: ${{ matrix.dotnet }}
dotnet-version: 8.x

- name: Setup .NET 9
uses: actions/setup-dotnet@v3
with:
dotnet-version: 9.x

- name: Install dependencies
run: dotnet restore

- name: Build
run: dotnet build --configuration Release --no-restore

- name: Test
run: dotnet test --no-restore --verbosity normal --logger trx --collect:"XPlat Code Coverage"
run: dotnet test --no-build --configuration Release --verbosity normal --logger trx --collect:"XPlat Code Coverage"

- name: Combine Coverage Reports # This is because one report is produced per project, and we want one result for all of them.
uses: danielpalme/ReportGenerator-GitHub-Action@5.2.4
if: github.actor != 'nektos/act' && matrix.os == 'ubuntu-latest' # Skip if running locally with act
Expand All @@ -55,15 +57,15 @@ jobs:
tag: "${{ github.run_number }}_${{ github.run_id }}" # Optional tag or build version.
customSettings: "" # Optional custom settings (separated by semicolon). See: https://github.com/danielpalme/ReportGenerator/wiki/Settings.
toolpath: "reportgeneratortool" # Default directory for installing the dotnet tool.

- name: Upload Combined Coverage XML
uses: actions/upload-artifact@v4
if: github.actor != 'nektos/act' && matrix.os == 'ubuntu-latest' # Skip if running locally with act
with:
name: coverage
path: ${{ github.workspace }}/Cobertura.xml
retention-days: 5

- name: Publish Code Coverage Report
uses: irongut/CodeCoverageSummary@v1.3.0
if: github.actor != 'nektos/act' && matrix.os == 'ubuntu-latest' # Skip if running locally with act
Expand All @@ -77,7 +79,7 @@ jobs:
indicators: true
output: both
thresholds: "10 30" # Red, Yellow

- name: Add Coverage PR Comment
uses: marocchino/sticky-pull-request-comment@v2
if: github.actor != 'nektos/act' && matrix.os == 'ubuntu-latest' && github.event_name == 'pull_request' # Skip if running locally with act, or if not a pull request
Expand All @@ -92,10 +94,9 @@ jobs:
name: test-results
path: ${{ github.workspace }}/**/TestResults/**/*
retention-days: 5

- name: Publish Test Results
uses: EnricoMi/publish-unit-test-result-action@v2.16.1
if: github.actor != 'nektos/act' && matrix.os == 'ubuntu-latest' && always() # Skip if running locally with act
with:
trx_files: "${{ github.workspace }}/**/*.trx"

38 changes: 27 additions & 11 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,6 @@ name: publish

on:
workflow_dispatch: # Allow running the workflow manually from the GitHub UI
push:
branches:
- main # Run the workflow when pushing to the main branch
- 'releases/**' # Run the workflow when pushing to a release branch
pull_request:
branches:
- '*' # Run the workflow for all pull requests
release:
types:
- published # Run the workflow when a new GitHub release is published
Expand All @@ -32,8 +25,15 @@ jobs:
with:
fetch-depth: 0 # Get all history to allow automatic versioning using MinVer

- name: Setup .NET
- name: Setup .NET 8
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.x

- name: Setup .NET 9
uses: actions/setup-dotnet@v4
with:
dotnet-version: 9.x

- run: dotnet pack --configuration Release --output ${{ env.NuGetDirectory }}

Expand All @@ -52,8 +52,17 @@ jobs:
working-directory: src
steps:
- uses: actions/checkout@v3
- name: Setup .NET

- name: Setup .NET 8
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.x

- name: Setup .NET 9
uses: actions/setup-dotnet@v4
with:
dotnet-version: 9.x

- name: Run tests
run: dotnet test --configuration Release

Expand All @@ -74,8 +83,15 @@ jobs:
name: nuget
path: ${{ env.NuGetDirectory }}

- name: Setup .NET Core
- name: Setup .NET 8
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.x

- name: Setup .NET 9
uses: actions/setup-dotnet@v4
with:
dotnet-version: 9.x

# Publish all NuGet packages to NuGet.org
# Use --skip-duplicate to prevent errors if a package with the same version already exists.
Expand All @@ -84,4 +100,4 @@ jobs:
run: |
foreach($file in (Get-ChildItem "${{ env.NuGetDirectory }}" -Recurse -Include *.nupkg)) {
dotnet nuget push $file --api-key "${{ secrets.NUGET_APIKEY }}" --source https://api.nuget.org/v3/index.json --skip-duplicate
}
}
10 changes: 5 additions & 5 deletions src/F23.Kernel.AspNetCore/F23.Kernel.AspNetCore.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Version>0.1.0</Version>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Version>0.2.0</Version>
<Authors>feature[23]</Authors>
<Copyright>feature[23]</Copyright>
<PackageProjectUrl>https://github.com/feature23/kernel</PackageProjectUrl>
Expand All @@ -23,18 +24,17 @@
<ItemGroup>
<InternalsVisibleTo Include="F23.Kernel.Tests" />
</ItemGroup>

<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\F23.Kernel\F23.Kernel.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="F23.Hateoas" Version="1.0.0" />
<!-- <PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.3.0" />-->
<PackageReference Include="F23.Hateoas" Version="2.0.0" />
</ItemGroup>

</Project>
2 changes: 1 addition & 1 deletion src/F23.Kernel.AspNetCore/MinimalApiResultExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ public static IResult ToMinimalApiResult<T>(this Result<T> result, Func<T, IResu
SuccessResult<T> success when successMap != null
=> successMap(success.Value),
SuccessResult<T> success
=> HttpResults.Ok(new HypermediaResponse(success.Value)),
=> HttpResults.Ok(new HypermediaResponse(success.Value!)), // [!]: TODO: Fix nullability issue in F23.Hateoas
PreconditionFailedResult<T> { Reason: PreconditionFailedReason.NotFound } when useProblemDetails
=> result.ToProblemHttpResult(HttpStatusCode.NotFound),
PreconditionFailedResult<T> { Reason: PreconditionFailedReason.NotFound } when !useProblemDetails
Expand Down
2 changes: 1 addition & 1 deletion src/F23.Kernel.AspNetCore/MvcResultExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ public static IActionResult ToActionResult<T>(this Result<T> result, Func<T, IAc
SuccessResult<T> success when successMap != null
=> successMap(success.Value),
SuccessResult<T> success
=> new OkObjectResult(new HypermediaResponse(success.Value)),
=> new OkObjectResult(new HypermediaResponse(success.Value!)), // [!]: TODO: Fix nullability issue in F23.Hateoas
PreconditionFailedResult<T> { Reason: PreconditionFailedReason.NotFound } when useProblemDetails
=> result.ToProblemDetailsResult(HttpStatusCode.NotFound),
PreconditionFailedResult<T> { Reason: PreconditionFailedReason.NotFound } when !useProblemDetails
Expand Down
28 changes: 16 additions & 12 deletions src/F23.Kernel.Examples.AspNetCore/Core/ResultsEndpoints.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
using F23.Kernel;
using F23.Kernel.AspNetCore;
using F23.Kernel.Results;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Mvc;

namespace F23.Kernel.Examples.AspNetCore.Core;

Expand All @@ -18,43 +16,49 @@ public static class ResultsEndpoints
public static void MapResultsEndpoints(this WebApplication app)
{
var group = app.MapGroup("/minimal-apis/results")
.WithTags("Results - Minimal APIs")
.WithOpenApi();
.WithTags("Results - Minimal APIs");

group.MapGet("/success-no-value", SuccessNoValue)
.WithName("MinimalApiSuccessNoValue")
.WithSummary("Demonstrates a successful result with no value")
.WithDescription("Returns 204 No Content");
.WithDescription("Returns 204 No Content")
.Produces(StatusCodes.Status204NoContent);

group.MapGet("/success-with-value", SuccessWithValue)
.WithName("MinimalApiSuccessWithValue")
.WithSummary("Demonstrates a successful result with a value")
.WithDescription("Returns 200 OK with the value in the response body");
.WithDescription("Returns 200 OK with the value in the response body")
.Produces(StatusCodes.Status200OK);

group.MapGet("/validation-failed", ValidationFailed)
.WithName("MinimalApiValidationFailed")
.WithSummary("Demonstrates a validation failed result")
.WithDescription("Returns 400 Bad Request with validation errors");
.WithDescription("Returns 400 Bad Request with validation errors")
.Produces<HttpValidationProblemDetails>(StatusCodes.Status400BadRequest);

group.MapGet("/unauthorized", Unauthorized)
.WithName("MinimalApiUnauthorized")
.WithSummary("Demonstrates an unauthorized result")
.WithDescription("Returns 403 Forbidden");
.WithDescription("Returns 403 Forbidden")
.Produces<ProblemDetails>(StatusCodes.Status403Forbidden);

group.MapGet("/not-found", NotFound)
.WithName("MinimalApiNotFound")
.WithSummary("Demonstrates a precondition failed result (not found)")
.WithDescription("Returns 404 Not Found");
.WithDescription("Returns 404 Not Found")
.Produces<ProblemDetails>(StatusCodes.Status404NotFound);

group.MapGet("/concurrency-mismatch", ConcurrencyMismatch)
.WithName("MinimalApiConcurrencyMismatch")
.WithSummary("Demonstrates a precondition failed result (concurrency mismatch)")
.WithDescription("Returns 412 Precondition Failed");
.WithDescription("Returns 412 Precondition Failed")
.Produces<ProblemDetails>(StatusCodes.Status412PreconditionFailed);

group.MapGet("/conflict", Conflict)
.WithName("MinimalApiConflict")
.WithSummary("Demonstrates a precondition failed result (conflict)")
.WithDescription("Returns 409 Conflict");
.WithDescription("Returns 409 Conflict")
.Produces<ProblemDetails>(StatusCodes.Status409Conflict);
}

private static IResult SuccessNoValue()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.10"/>
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2"/>
<PackageReference Include="Swashbuckle.AspNetCore" Version="9.0.6" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using F23.Kernel;
using F23.Kernel.AspNetCore;
using F23.Kernel.Results;
using Microsoft.AspNetCore.Mvc;
Expand All @@ -18,6 +17,7 @@ public class ResultsController : ControllerBase
/// </summary>
/// <returns>204 No Content</returns>
[HttpGet("success-no-value")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public IActionResult SuccessNoValue()
{
var result = Result.Success();
Expand All @@ -29,6 +29,7 @@ public IActionResult SuccessNoValue()
/// </summary>
/// <returns>200 OK with the value in the response body</returns>
[HttpGet("success-with-value")]
[ProducesResponseType(StatusCodes.Status200OK)]
public IActionResult SuccessWithValue()
{
var data = new { message = "Operation completed successfully", timestamp = DateTime.UtcNow };
Expand All @@ -41,6 +42,7 @@ public IActionResult SuccessWithValue()
/// </summary>
/// <returns>400 Bad Request with validation errors</returns>
[HttpGet("validation-failed")]
[ProducesResponseType<ValidationProblemDetails>(StatusCodes.Status400BadRequest)]
public IActionResult ValidationFailed()
{
var errors = new[]
Expand All @@ -58,6 +60,7 @@ public IActionResult ValidationFailed()
/// </summary>
/// <returns>403 Forbidden</returns>
[HttpGet("unauthorized")]
[ProducesResponseType<ProblemDetails>(StatusCodes.Status403Forbidden)]
public IActionResult UnauthorizedDemo()
{
var result = Result.Unauthorized("User does not have permission to access this resource");
Expand All @@ -69,6 +72,7 @@ public IActionResult UnauthorizedDemo()
/// </summary>
/// <returns>404 Not Found</returns>
[HttpGet("not-found")]
[ProducesResponseType<ProblemDetails>(StatusCodes.Status404NotFound)]
public IActionResult NotFoundDemo()
{
var result = Result.PreconditionFailed(
Expand All @@ -83,6 +87,7 @@ public IActionResult NotFoundDemo()
/// </summary>
/// <returns>412 Precondition Failed</returns>
[HttpGet("concurrency-mismatch")]
[ProducesResponseType<ProblemDetails>(StatusCodes.Status412PreconditionFailed)]
public IActionResult ConcurrencyMismatch()
{
var result = Result.PreconditionFailed(
Expand All @@ -97,6 +102,7 @@ public IActionResult ConcurrencyMismatch()
/// </summary>
/// <returns>409 Conflict</returns>
[HttpGet("conflict")]
[ProducesResponseType<ProblemDetails>(StatusCodes.Status409Conflict)]
public IActionResult ConflictDemo()
{
var result = Result.PreconditionFailed(
Expand Down
Loading