diff --git a/.editorconfig b/.editorconfig index d552917..f23b7d6 100644 --- a/.editorconfig +++ b/.editorconfig @@ -17,9 +17,9 @@ indent_size = 2 indent_size = 4 tab_width = 4 -# New line preferences -end_of_line = crlf -insert_final_newline = false +# New line preferences +end_of_line = lf +insert_final_newline = true #### .NET Coding Conventions #### [*.{cs,vb}] diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..f86f00a --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,33 @@ +name: Build + +on: + pull_request: + push: + branches: + - main + +jobs: + build: + name: Build and test + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + global-json-file: global.json + + - name: Restore + run: dotnet restore CleanApiStarter.slnx + + - name: Format + run: dotnet format CleanApiStarter.slnx --verify-no-changes --no-restore + + - name: Build + run: dotnet build CleanApiStarter.slnx --no-restore --configuration Release /nr:false -v:minimal + + - name: Test + run: dotnet test CleanApiStarter.slnx --no-build --configuration Release /nr:false -v:minimal diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..5dfba3b --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,44 @@ +name: CodeQL + +on: + pull_request: + push: + branches: + - main + schedule: + - cron: '00 0 * * 1' + +permissions: + actions: read + contents: read + security-events: write + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Setup .NET + uses: actions/setup-dotnet@v5 + with: + global-json-file: global.json + + - name: Initialize CodeQL + uses: github/codeql-action/init@v4 + with: + languages: csharp + build-mode: manual + queries: security-extended,security-and-quality + + - name: Restore + run: dotnet restore CleanApiStarter.slnx + + - name: Build + run: dotnet build CleanApiStarter.slnx --no-restore --configuration Release /nr:false -v:minimal + + - name: Analyze + uses: github/codeql-action/analyze@v4 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..0715734 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,46 @@ +name: Release + +on: + release: + types: [published] + +permissions: + contents: read + +jobs: + publish: + name: Publish to NuGet.org + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Setup .NET + uses: actions/setup-dotnet@v5 + with: + global-json-file: global.json + + - name: Extract version + id: version + run: echo "value=${GITHUB_REF_NAME#v}" >> $GITHUB_OUTPUT + + - name: Update template version + run: | + jq --arg v "${{ steps.version.outputs.value }}" \ + '.symbols.caPackageVersion.defaultValue = $v' \ + .template.config/template.json > tmp.json && mv tmp.json .template.config/template.json + + - name: Pack template + env: + RELEASE_NOTES: ${{ github.event.release.body }} + run: dotnet pack CleanApiStarter.Template.csproj --configuration Release --output artifacts -p:PackageVersion=${{ steps.version.outputs.value }} -p:PackageReleaseNotes="$RELEASE_NOTES" + + - name: Upload package artifact + uses: actions/upload-artifact@v7 + with: + name: nuget-package + path: artifacts/*.nupkg + + - name: Publish to NuGet + run: dotnet nuget push artifacts/*.nupkg --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_API_KEY }} --skip-duplicate diff --git a/.github/workflows/template.yml b/.github/workflows/template.yml new file mode 100644 index 0000000..981cc62 --- /dev/null +++ b/.github/workflows/template.yml @@ -0,0 +1,46 @@ +name: Template + +on: + pull_request: + push: + branches: + - main + +jobs: + template: + name: Verify template output + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Setup .NET + uses: actions/setup-dotnet@v5 + with: + global-json-file: global.json + + - name: Pack template + run: dotnet pack CleanApiStarter.Template.csproj --configuration Release --output artifacts + + - name: Install template + run: dotnet new install artifacts/CleanApiStarter.Template.0.0.0.nupkg --force + + - name: Create sample + run: dotnet new clean-api-starter -n DemoProduct -o artifacts/DemoProduct + + - name: Restore sample + working-directory: artifacts/DemoProduct + run: dotnet restore DemoProduct.slnx + + - name: Format sample + working-directory: artifacts/DemoProduct + run: dotnet format DemoProduct.slnx --verify-no-changes --no-restore + + - name: Build sample + working-directory: artifacts/DemoProduct + run: dotnet build DemoProduct.slnx --no-restore --configuration Release /nr:false -v:minimal + + - name: Test sample + working-directory: artifacts/DemoProduct + run: dotnet test DemoProduct.slnx --no-build --configuration Release /nr:false -v:minimal diff --git a/.template.config/template.json b/.template.config/template.json new file mode 100644 index 0000000..e23e59b --- /dev/null +++ b/.template.config/template.json @@ -0,0 +1,51 @@ +{ + "$schema": "http://json.schemastore.org/template", + "author": "Chathuranga", + "classifications": [ + "Web", + "API", + "Clean Architecture", + "Aspire", + "PostgreSQL", + "OpenTelemetry" + ], + "identity": "CleanApiStarter.Template", + "name": "Clean API Starter", + "shortName": "clean-api-starter", + "sourceName": "CleanApiStarter", + "preferNameDirectory": true, + "symbols": { + "caPackageVersion": { + "type": "parameter", + "datatype": "text", + "defaultValue": "0.0.0", + "replaces": "0.0.0" + } + }, + "tags": { + "language": "C#", + "type": "solution" + }, + "sources": [ + { + "modifiers": [ + { + "exclude": [ + ".git/**", + ".idea/**", + ".junie/**", + ".vs/**", + "**/.DS_Store", + "**/bin/**", + "**/obj/**", + "artifacts/**", + ".github/workflows/release.yml", + ".github/workflows/template.yml", + "CleanApiStarter.Template.csproj", + "scripts/install-template.sh" + ] + } + ] + } + ] +} diff --git a/AGENTS.md b/AGENTS.md index 4b1a93e..e92b9af 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -146,6 +146,17 @@ This repository is a Clean Architecture API starter template named `CleanApiStar - Keep `PackageVersion` items sorted alphabetically by `Include`. - Do not add package versions directly in individual `.csproj` files. +## Template Packaging + +- This repository is also the `dotnet new` template source. +- Keep template metadata in `.template.config/template.json`. +- Keep NuGet template package metadata in `CleanApiStarter.Template.csproj`. +- Use `dotnet pack` and `dotnet nuget push` for template packaging and publishing. Do not use `nuget pack`, `nuget.exe`, or Mono. +- Use `scripts/install-template.sh` to pack and install the local template. +- Keep repo-only template packaging scripts excluded from generated template output. +- Keep CodeQL security scanning in `.github/workflows/codeql.yml`, and allow generated projects to inherit it. +- Keep release publishing triggered by GitHub Release publication with `vX.Y.Z` tags, not manual version inputs. + ## Testing - Use xUnit v3, AutoFixture.xUnit3, AutoFixture.AutoNSubstitute, NSubstitute, and Shouldly for unit tests. @@ -163,7 +174,7 @@ This repository is a Clean Architecture API starter template named `CleanApiStar - After structural or package changes, run: ```bash -dotnet restore CleanApiStarter.slnx --disable-parallel +dotnet restore CleanApiStarter.slnx dotnet build CleanApiStarter.slnx --no-restore /nr:false -v:minimal ``` diff --git a/CleanApiStarter.Template.csproj b/CleanApiStarter.Template.csproj new file mode 100644 index 0000000..f3497b3 --- /dev/null +++ b/CleanApiStarter.Template.csproj @@ -0,0 +1,31 @@ + + + net10.0 + false + true + false + true + true + $(NoWarn);NU5128 + + CleanApiStarter.Template + 0.0.0 + Clean API Starter Template + Chathuranga + Clean Architecture API starter template with .NET, Aspire, PostgreSQL, OpenTelemetry, Scalar, JWT authentication, EF Core, and tests. + dotnet-new;template;aspnetcore;api;clean-architecture;aspire;postgresql;opentelemetry;scalar;jwt;efcore + Template + LICENSE + README.md + + + + + + + + diff --git a/CleanApiStarter.slnx b/CleanApiStarter.slnx index 9c7a5fa..acb877a 100644 --- a/CleanApiStarter.slnx +++ b/CleanApiStarter.slnx @@ -2,11 +2,18 @@ + + + + + + + @@ -25,6 +32,10 @@ + + + + diff --git a/README.md b/README.md index 482e97f..d1678e0 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ The project is intentionally both a template and a working reference application - xUnit v3 unit tests with AutoFixture, AutoFixture.AutoNSubstitute, NSubstitute, and Shouldly. - MSTest API integration tests with Testcontainers for PostgreSQL. - Local coverage script that generates an HTML report with ReportGenerator. +- GitHub Actions CI with build, test, template verification, and CodeQL security scanning. ## Solution Layout @@ -76,6 +77,41 @@ The SDK is pinned in `global.json`: } ``` +## Use As A Template + +Install the template package from NuGet: + +```bash +dotnet new install CleanApiStarter.Template +``` + +Create a new solution: + +```bash +dotnet new clean-api-starter -n MyProduct +``` + +The template replaces `CleanApiStarter` in solution, project, file, and namespace names. For example, `CleanApiStarter.Api` becomes `MyProduct.Api`. + +While developing the template locally, run: + +```bash +scripts/install-template.sh +``` + +That script: + +- uninstalls the previous local template package +- packs the current repo with `CleanApiStarter.Template.csproj` +- installs the generated local `.nupkg` +- deletes the temporary package from `artifacts` + +Then create a local test solution: + +```bash +dotnet new clean-api-starter -n DemoProduct +``` + ## Run With Aspire ```bash @@ -474,10 +510,19 @@ artifacts/coverage/report/index.html Restore and build: ```bash -dotnet restore CleanApiStarter.slnx --disable-parallel +dotnet restore CleanApiStarter.slnx dotnet build CleanApiStarter.slnx --no-restore /nr:false -v:minimal ``` +## CI + +GitHub Actions workflows live in `.github/workflows`: + +- `build.yml` restores, builds, and tests the repository on pull requests and pushes to `main`. +- `codeql.yml` runs CodeQL static security analysis on pull requests, pushes to `main`, and weekly on Monday. +- `template.yml` packs the template, installs it locally, creates a sample solution, then restores, builds, and tests the generated output. +- `release.yml` publishes the template to NuGet when a GitHub Release is published with a tag such as `v1.0.0`. + ## Package Management Package versions are managed centrally in: diff --git a/scripts/install-template.sh b/scripts/install-template.sh new file mode 100755 index 0000000..e41dcbe --- /dev/null +++ b/scripts/install-template.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +set -euo pipefail + +root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +name="CleanApiStarter.Template.0.0.0.nupkg" +pkg="$root/artifacts/$name" + +cd "$root" + +dotnet new uninstall CleanApiStarter.Template 2>/dev/null || true +dotnet pack "CleanApiStarter.Template.csproj" --configuration Release --output "artifacts" +dotnet new install "$pkg" --force +rm -f "$pkg" diff --git a/src/CleanApiStarter.Api/Endpoints/GoogleLoginPage.cs b/src/CleanApiStarter.Api/Endpoints/GoogleLoginPage.cs index a36d7cd..516c2a1 100644 --- a/src/CleanApiStarter.Api/Endpoints/GoogleLoginPage.cs +++ b/src/CleanApiStarter.Api/Endpoints/GoogleLoginPage.cs @@ -97,4 +97,4 @@ async function copyAccessToken() { return app; } -} \ No newline at end of file +} diff --git a/src/CleanApiStarter.Api/Endpoints/V1/Auth.cs b/src/CleanApiStarter.Api/Endpoints/V1/Auth.cs index 13fa398..dc064cb 100644 --- a/src/CleanApiStarter.Api/Endpoints/V1/Auth.cs +++ b/src/CleanApiStarter.Api/Endpoints/V1/Auth.cs @@ -36,4 +36,4 @@ private static async Task GetCurrentUser( return Results.Ok(currentUser); } -} \ No newline at end of file +} diff --git a/src/CleanApiStarter.Api/Endpoints/V1/Projects.cs b/src/CleanApiStarter.Api/Endpoints/V1/Projects.cs index b5c74a0..c55c4a9 100644 --- a/src/CleanApiStarter.Api/Endpoints/V1/Projects.cs +++ b/src/CleanApiStarter.Api/Endpoints/V1/Projects.cs @@ -173,4 +173,4 @@ private static async Task DeleteTask( return deleted ? Results.NoContent() : Results.NotFound(); } -} \ No newline at end of file +} diff --git a/src/CleanApiStarter.Api/Endpoints/V2/Projects.cs b/src/CleanApiStarter.Api/Endpoints/V2/Projects.cs index f4e8e85..a0736b0 100644 --- a/src/CleanApiStarter.Api/Endpoints/V2/Projects.cs +++ b/src/CleanApiStarter.Api/Endpoints/V2/Projects.cs @@ -32,4 +32,4 @@ private static async Task GetProjects( projects.HasNextPage }); } -} \ No newline at end of file +} diff --git a/src/CleanApiStarter.Api/GlobalUsings.cs b/src/CleanApiStarter.Api/GlobalUsings.cs index db4fb8f..9f9d081 100644 --- a/src/CleanApiStarter.Api/GlobalUsings.cs +++ b/src/CleanApiStarter.Api/GlobalUsings.cs @@ -22,4 +22,4 @@ global using Microsoft.AspNetCore.Mvc; global using Microsoft.AspNetCore.Routing; -global using Scalar.AspNetCore; \ No newline at end of file +global using Scalar.AspNetCore; diff --git a/src/CleanApiStarter.Api/Program.cs b/src/CleanApiStarter.Api/Program.cs index 452e9af..4b435c6 100644 --- a/src/CleanApiStarter.Api/Program.cs +++ b/src/CleanApiStarter.Api/Program.cs @@ -1,40 +1,40 @@ -WebApplicationBuilder builder = WebApplication.CreateBuilder(args); - -builder.AddAspNetCoreDefaults(); - -builder.Services.AddAppSettings(builder.Configuration); -builder.Services.AddApplication(); -builder.Services.AddInfrastructure(); -builder.Services.AddScoped(); - -WebApplication app = builder.Build(); - -// Configure the HTTP request pipeline. -if (app.Environment.IsDevelopment()) -{ - app.MapOpenApi() - .WithDocumentPerVersion(); - - app.MapScalarApiReference(options => - { - IReadOnlyList descriptions = app.DescribeApiVersions(); - - for (int index = 0; index < descriptions.Count; index++) - { - ApiVersionDescription description = descriptions[index]; - bool isDefault = index == descriptions.Count - 1; - - options.AddDocument(description.GroupName, description.GroupName, isDefault: isDefault); - } - }); -} - -app.UseAspNetCoreDefaults(); -app.UseHttpsRedirection(); -app.MapGoogleLoginPage(); -app.MapEndpoints(Assembly.GetExecutingAssembly()); -app.MapDefaultEndpoints(); - -app.Run(); - -public partial class Program; \ No newline at end of file +WebApplicationBuilder builder = WebApplication.CreateBuilder(args); + +builder.AddAspNetCoreDefaults(); + +builder.Services.AddAppSettings(builder.Configuration); +builder.Services.AddApplication(); +builder.Services.AddInfrastructure(); +builder.Services.AddScoped(); + +WebApplication app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.MapOpenApi() + .WithDocumentPerVersion(); + + app.MapScalarApiReference(options => + { + IReadOnlyList descriptions = app.DescribeApiVersions(); + + for (int index = 0; index < descriptions.Count; index++) + { + ApiVersionDescription description = descriptions[index]; + bool isDefault = index == descriptions.Count - 1; + + options.AddDocument(description.GroupName, description.GroupName, isDefault: isDefault); + } + }); +} + +app.UseAspNetCoreDefaults(); +app.UseHttpsRedirection(); +app.MapGoogleLoginPage(); +app.MapEndpoints(Assembly.GetExecutingAssembly()); +app.MapDefaultEndpoints(); + +app.Run(); + +public partial class Program; diff --git a/src/CleanApiStarter.Api/Services/CurrentUser.cs b/src/CleanApiStarter.Api/Services/CurrentUser.cs index c48f384..37c0828 100644 --- a/src/CleanApiStarter.Api/Services/CurrentUser.cs +++ b/src/CleanApiStarter.Api/Services/CurrentUser.cs @@ -6,4 +6,4 @@ public class CurrentUser(IHttpContextAccessor httpContextAccessor) : IUser public List? Roles => httpContextAccessor.HttpContext?.User.FindAll(ClaimTypes.Role).Select(x => x.Value).ToList(); -} \ No newline at end of file +} diff --git a/src/CleanApiStarter.AppHost/Program.cs b/src/CleanApiStarter.AppHost/Program.cs index aa0a117..a5848d6 100644 --- a/src/CleanApiStarter.AppHost/Program.cs +++ b/src/CleanApiStarter.AppHost/Program.cs @@ -1,19 +1,19 @@ -IDistributedApplicationBuilder builder = DistributedApplication.CreateBuilder(args); - -string databasePath = Path.GetFullPath(Path.Combine(builder.AppHostDirectory, "..", "..", "database")); -string databaseMigrationsPath = Path.Combine(databasePath, "migrations"); - -IResourceBuilder postgresServer = builder.AddPostgres("postgres-server") - .WithImageTag("latest") - .WithVolume("clean-api-starter-postgres-data", "/var/lib/postgresql") - .WithInitFiles(databaseMigrationsPath) - .WithPgAdmin(); - -IResourceBuilder database = postgresServer.AddDatabase("postgres") - .WithCreationScript("SELECT 1;"); - -builder.AddProject("api") - .WithReference(database) - .WaitFor(database); - -builder.Build().Run(); \ No newline at end of file +IDistributedApplicationBuilder builder = DistributedApplication.CreateBuilder(args); + +string databasePath = Path.GetFullPath(Path.Combine(builder.AppHostDirectory, "..", "..", "database")); +string databaseMigrationsPath = Path.Combine(databasePath, "migrations"); + +IResourceBuilder postgresServer = builder.AddPostgres("postgres-server") + .WithImageTag("latest") + .WithVolume("clean-api-starter-postgres-data", "/var/lib/postgresql") + .WithInitFiles(databaseMigrationsPath) + .WithPgAdmin(); + +IResourceBuilder database = postgresServer.AddDatabase("postgres") + .WithCreationScript("SELECT 1;"); + +builder.AddProject("api") + .WithReference(database) + .WaitFor(database); + +builder.Build().Run(); diff --git a/src/CleanApiStarter.Application/Common/Interfaces/IUser.cs b/src/CleanApiStarter.Application/Common/Interfaces/IUser.cs index 00c53cc..5064363 100644 --- a/src/CleanApiStarter.Application/Common/Interfaces/IUser.cs +++ b/src/CleanApiStarter.Application/Common/Interfaces/IUser.cs @@ -4,4 +4,4 @@ public interface IUser { string? Id { get; } List? Roles { get; } -} \ No newline at end of file +} diff --git a/src/CleanApiStarter.Application/Common/Models/ArrayResult.cs b/src/CleanApiStarter.Application/Common/Models/ArrayResult.cs index a75bfe8..3e99c59 100644 --- a/src/CleanApiStarter.Application/Common/Models/ArrayResult.cs +++ b/src/CleanApiStarter.Application/Common/Models/ArrayResult.cs @@ -5,4 +5,4 @@ public sealed class ArrayResult public required IReadOnlyCollection Items { get; init; } public int Count => Items.Count; -} \ No newline at end of file +} diff --git a/src/CleanApiStarter.Application/Common/Models/PaginatedQuery.cs b/src/CleanApiStarter.Application/Common/Models/PaginatedQuery.cs index eb30234..ca80f8a 100644 --- a/src/CleanApiStarter.Application/Common/Models/PaginatedQuery.cs +++ b/src/CleanApiStarter.Application/Common/Models/PaginatedQuery.cs @@ -5,4 +5,4 @@ public sealed class PaginatedQuery public int Limit { get; set; } = 20; public int Offset { get; set; } -} \ No newline at end of file +} diff --git a/src/CleanApiStarter.Application/Common/Models/PaginatedQueryValidator.cs b/src/CleanApiStarter.Application/Common/Models/PaginatedQueryValidator.cs index fe83865..8db6a25 100644 --- a/src/CleanApiStarter.Application/Common/Models/PaginatedQueryValidator.cs +++ b/src/CleanApiStarter.Application/Common/Models/PaginatedQueryValidator.cs @@ -10,4 +10,4 @@ public PaginatedQueryValidator() RuleFor(query => query.Offset) .GreaterThanOrEqualTo(0); } -} \ No newline at end of file +} diff --git a/src/CleanApiStarter.Application/Common/Models/PaginatedResult.cs b/src/CleanApiStarter.Application/Common/Models/PaginatedResult.cs index 83f9be0..c1259db 100644 --- a/src/CleanApiStarter.Application/Common/Models/PaginatedResult.cs +++ b/src/CleanApiStarter.Application/Common/Models/PaginatedResult.cs @@ -24,4 +24,4 @@ public PaginatedResult Map(Func map TotalCount = TotalCount }; } -} \ No newline at end of file +} diff --git a/src/CleanApiStarter.Application/DependencyInjection.cs b/src/CleanApiStarter.Application/DependencyInjection.cs index 54e6f72..94651fd 100644 --- a/src/CleanApiStarter.Application/DependencyInjection.cs +++ b/src/CleanApiStarter.Application/DependencyInjection.cs @@ -1,12 +1,12 @@ -namespace CleanApiStarter.Application; - -public static class DependencyInjection -{ - public static IServiceCollection AddApplication(this IServiceCollection services) - { - services.AddValidatorsFromAssembly(typeof(DependencyInjection).Assembly); - services.AddScoped(); - - return services; - } -} \ No newline at end of file +namespace CleanApiStarter.Application; + +public static class DependencyInjection +{ + public static IServiceCollection AddApplication(this IServiceCollection services) + { + services.AddValidatorsFromAssembly(typeof(DependencyInjection).Assembly); + services.AddScoped(); + + return services; + } +} diff --git a/src/CleanApiStarter.Application/Features/Auth/AuthTokenDto.cs b/src/CleanApiStarter.Application/Features/Auth/AuthTokenDto.cs index 95156bf..d115f40 100644 --- a/src/CleanApiStarter.Application/Features/Auth/AuthTokenDto.cs +++ b/src/CleanApiStarter.Application/Features/Auth/AuthTokenDto.cs @@ -5,4 +5,4 @@ public sealed class AuthTokenDto public required string AccessToken { get; init; } public required DateTimeOffset ExpiresAt { get; init; } -} \ No newline at end of file +} diff --git a/src/CleanApiStarter.Application/Features/Auth/CurrentUserDto.cs b/src/CleanApiStarter.Application/Features/Auth/CurrentUserDto.cs index e52f126..f64b9d1 100644 --- a/src/CleanApiStarter.Application/Features/Auth/CurrentUserDto.cs +++ b/src/CleanApiStarter.Application/Features/Auth/CurrentUserDto.cs @@ -9,4 +9,4 @@ public sealed class CurrentUserDto public required string Name { get; init; } public IReadOnlyCollection Roles { get; init; } = []; -} \ No newline at end of file +} diff --git a/src/CleanApiStarter.Application/Features/Auth/GoogleSignInDto.cs b/src/CleanApiStarter.Application/Features/Auth/GoogleSignInDto.cs index c5d9da9..b1ebed4 100644 --- a/src/CleanApiStarter.Application/Features/Auth/GoogleSignInDto.cs +++ b/src/CleanApiStarter.Application/Features/Auth/GoogleSignInDto.cs @@ -3,4 +3,4 @@ namespace CleanApiStarter.Application.Features.Auth; public sealed class GoogleSignInDto { public required string IdToken { get; init; } -} \ No newline at end of file +} diff --git a/src/CleanApiStarter.Application/Features/Auth/GoogleSignInDtoValidator.cs b/src/CleanApiStarter.Application/Features/Auth/GoogleSignInDtoValidator.cs index 4f52587..7ef17c3 100644 --- a/src/CleanApiStarter.Application/Features/Auth/GoogleSignInDtoValidator.cs +++ b/src/CleanApiStarter.Application/Features/Auth/GoogleSignInDtoValidator.cs @@ -7,4 +7,4 @@ public GoogleSignInDtoValidator() RuleFor(signIn => signIn.IdToken) .NotEmpty(); } -} \ No newline at end of file +} diff --git a/src/CleanApiStarter.Application/Features/Auth/IAuthService.cs b/src/CleanApiStarter.Application/Features/Auth/IAuthService.cs index 27bcb46..1e1fc6a 100644 --- a/src/CleanApiStarter.Application/Features/Auth/IAuthService.cs +++ b/src/CleanApiStarter.Application/Features/Auth/IAuthService.cs @@ -5,4 +5,4 @@ public interface IAuthService Task SignInWithGoogleAsync(GoogleSignInDto signInDto, CancellationToken cancellationToken); Task GetCurrentUserAsync(ClaimsPrincipal principal, CancellationToken cancellationToken); -} \ No newline at end of file +} diff --git a/src/CleanApiStarter.Application/Features/Projects/CreateProjectDtoValidator.cs b/src/CleanApiStarter.Application/Features/Projects/CreateProjectDtoValidator.cs index aee321f..fb524b1 100644 --- a/src/CleanApiStarter.Application/Features/Projects/CreateProjectDtoValidator.cs +++ b/src/CleanApiStarter.Application/Features/Projects/CreateProjectDtoValidator.cs @@ -11,4 +11,4 @@ public CreateProjectDtoValidator() RuleFor(project => project.Description) .MaximumLength(2_000); } -} \ No newline at end of file +} diff --git a/src/CleanApiStarter.Application/Features/Projects/CreateProjectTaskDtoValidator.cs b/src/CleanApiStarter.Application/Features/Projects/CreateProjectTaskDtoValidator.cs index 91bac41..c06a35f 100644 --- a/src/CleanApiStarter.Application/Features/Projects/CreateProjectTaskDtoValidator.cs +++ b/src/CleanApiStarter.Application/Features/Projects/CreateProjectTaskDtoValidator.cs @@ -16,4 +16,4 @@ public CreateProjectTaskDtoValidator() .When(task => task.DueDate.HasValue) .WithMessage("Due date must be in the future."); } -} \ No newline at end of file +} diff --git a/src/CleanApiStarter.Application/Features/Projects/IProjectRepository.cs b/src/CleanApiStarter.Application/Features/Projects/IProjectRepository.cs index d0b627b..8f2f054 100644 --- a/src/CleanApiStarter.Application/Features/Projects/IProjectRepository.cs +++ b/src/CleanApiStarter.Application/Features/Projects/IProjectRepository.cs @@ -32,4 +32,4 @@ Task> GetTasksAsync( Task DeleteTaskAsync(Guid projectId, Guid taskId, CancellationToken cancellationToken); Task SaveChangesAsync(CancellationToken cancellationToken); -} \ No newline at end of file +} diff --git a/src/CleanApiStarter.Application/Features/Projects/IProjectService.cs b/src/CleanApiStarter.Application/Features/Projects/IProjectService.cs index cc6f1c9..ff0a3c6 100644 --- a/src/CleanApiStarter.Application/Features/Projects/IProjectService.cs +++ b/src/CleanApiStarter.Application/Features/Projects/IProjectService.cs @@ -29,4 +29,4 @@ Task UpdateTaskAsync( Task CompleteTaskAsync(Guid projectId, Guid taskId, CancellationToken cancellationToken); Task DeleteTaskAsync(Guid projectId, Guid taskId, CancellationToken cancellationToken); -} \ No newline at end of file +} diff --git a/src/CleanApiStarter.Application/Features/Projects/ProjectDto.cs b/src/CleanApiStarter.Application/Features/Projects/ProjectDto.cs index 640b8bd..c8d8356 100644 --- a/src/CleanApiStarter.Application/Features/Projects/ProjectDto.cs +++ b/src/CleanApiStarter.Application/Features/Projects/ProjectDto.cs @@ -18,4 +18,4 @@ public sealed class CreateProjectDto public required string Name { get; init; } public required string Description { get; init; } -} \ No newline at end of file +} diff --git a/src/CleanApiStarter.Application/Features/Projects/ProjectOperationResults.cs b/src/CleanApiStarter.Application/Features/Projects/ProjectOperationResults.cs index d441eae..2f4dfae 100644 --- a/src/CleanApiStarter.Application/Features/Projects/ProjectOperationResults.cs +++ b/src/CleanApiStarter.Application/Features/Projects/ProjectOperationResults.cs @@ -12,4 +12,4 @@ public enum ProjectTaskMutationResult Success, NotFound, AlreadyCompleted -} \ No newline at end of file +} diff --git a/src/CleanApiStarter.Application/Features/Projects/ProjectService.cs b/src/CleanApiStarter.Application/Features/Projects/ProjectService.cs index ebcc417..9d162bd 100644 --- a/src/CleanApiStarter.Application/Features/Projects/ProjectService.cs +++ b/src/CleanApiStarter.Application/Features/Projects/ProjectService.cs @@ -245,4 +245,4 @@ private static ProjectTaskDto MapTask(ProjectTask task) CompletedAt = task.CompletedAt }; } -} \ No newline at end of file +} diff --git a/src/CleanApiStarter.Application/Features/Projects/ProjectTaskDto.cs b/src/CleanApiStarter.Application/Features/Projects/ProjectTaskDto.cs index bd1803b..03f71fa 100644 --- a/src/CleanApiStarter.Application/Features/Projects/ProjectTaskDto.cs +++ b/src/CleanApiStarter.Application/Features/Projects/ProjectTaskDto.cs @@ -37,4 +37,4 @@ public sealed class UpdateProjectTaskDto public required ProjectTaskStatus Status { get; init; } public DateTime? DueDate { get; init; } -} \ No newline at end of file +} diff --git a/src/CleanApiStarter.Application/Features/Projects/UpdateProjectTaskDtoValidator.cs b/src/CleanApiStarter.Application/Features/Projects/UpdateProjectTaskDtoValidator.cs index 6b00b52..0227274 100644 --- a/src/CleanApiStarter.Application/Features/Projects/UpdateProjectTaskDtoValidator.cs +++ b/src/CleanApiStarter.Application/Features/Projects/UpdateProjectTaskDtoValidator.cs @@ -21,4 +21,4 @@ public UpdateProjectTaskDtoValidator() .When(task => task.DueDate.HasValue && task.Status != ProjectTaskStatus.Done) .WithMessage("Due date must be in the future."); } -} \ No newline at end of file +} diff --git a/src/CleanApiStarter.Application/GlobalUsings.cs b/src/CleanApiStarter.Application/GlobalUsings.cs index 72d832e..a886fb3 100644 --- a/src/CleanApiStarter.Application/GlobalUsings.cs +++ b/src/CleanApiStarter.Application/GlobalUsings.cs @@ -1,11 +1,11 @@ -global using System.Security.Claims; - -global using CleanApiStarter.Application.Common.Interfaces; -global using CleanApiStarter.Application.Common.Models; -global using CleanApiStarter.Application.Features.Projects; -global using CleanApiStarter.Domain.Entities; - -global using FluentValidation; - -global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.Logging; \ No newline at end of file +global using System.Security.Claims; + +global using CleanApiStarter.Application.Common.Interfaces; +global using CleanApiStarter.Application.Common.Models; +global using CleanApiStarter.Application.Features.Projects; +global using CleanApiStarter.Domain.Entities; + +global using FluentValidation; + +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Logging; diff --git a/src/CleanApiStarter.AspNetCore/AspNetCoreDefaultServices.cs b/src/CleanApiStarter.AspNetCore/AspNetCoreDefaultServices.cs index a70143d..2e7e5bc 100644 --- a/src/CleanApiStarter.AspNetCore/AspNetCoreDefaultServices.cs +++ b/src/CleanApiStarter.AspNetCore/AspNetCoreDefaultServices.cs @@ -113,4 +113,4 @@ private static void AddServiceDiscoveryDefaults(this IHostApplicationBuilder bui http.AddServiceDiscovery(); }); } -} \ No newline at end of file +} diff --git a/src/CleanApiStarter.AspNetCore/Extensions.cs b/src/CleanApiStarter.AspNetCore/Extensions.cs index 7394f11..3f223ad 100644 --- a/src/CleanApiStarter.AspNetCore/Extensions.cs +++ b/src/CleanApiStarter.AspNetCore/Extensions.cs @@ -65,4 +65,4 @@ public static WebApplication MapDefaultEndpoints(this WebApplication app) return app; } -} \ No newline at end of file +} diff --git a/src/CleanApiStarter.AspNetCore/GlobalUsings.cs b/src/CleanApiStarter.AspNetCore/GlobalUsings.cs index a0aabf7..0f611ae 100644 --- a/src/CleanApiStarter.AspNetCore/GlobalUsings.cs +++ b/src/CleanApiStarter.AspNetCore/GlobalUsings.cs @@ -1,36 +1,36 @@ -global using System.Diagnostics; -global using System.IdentityModel.Tokens.Jwt; -global using System.IO.Compression; -global using System.Reflection; -global using System.Security.Claims; -global using System.Text; - -global using Asp.Versioning; -global using Asp.Versioning.ApiExplorer; -global using Asp.Versioning.Builder; - -global using CleanApiStarter.Configuration; - -global using FluentValidation; -global using FluentValidation.Results; - -global using Microsoft.AspNetCore.Authentication.JwtBearer; -global using Microsoft.AspNetCore.Builder; -global using Microsoft.AspNetCore.Diagnostics; -global using Microsoft.AspNetCore.Diagnostics.HealthChecks; -global using Microsoft.AspNetCore.Http; -global using Microsoft.AspNetCore.HttpLogging; -global using Microsoft.AspNetCore.Mvc; -global using Microsoft.AspNetCore.ResponseCompression; -global using Microsoft.AspNetCore.Routing; -global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.Diagnostics.HealthChecks; -global using Microsoft.Extensions.Hosting; -global using Microsoft.Extensions.Logging; -global using Microsoft.IdentityModel.Tokens; - -global using Npgsql; - -global using OpenTelemetry.Logs; -global using OpenTelemetry.Metrics; -global using OpenTelemetry.Trace; \ No newline at end of file +global using System.Diagnostics; +global using System.IdentityModel.Tokens.Jwt; +global using System.IO.Compression; +global using System.Reflection; +global using System.Security.Claims; +global using System.Text; + +global using Asp.Versioning; +global using Asp.Versioning.ApiExplorer; +global using Asp.Versioning.Builder; + +global using CleanApiStarter.Configuration; + +global using FluentValidation; +global using FluentValidation.Results; + +global using Microsoft.AspNetCore.Authentication.JwtBearer; +global using Microsoft.AspNetCore.Builder; +global using Microsoft.AspNetCore.Diagnostics; +global using Microsoft.AspNetCore.Diagnostics.HealthChecks; +global using Microsoft.AspNetCore.Http; +global using Microsoft.AspNetCore.HttpLogging; +global using Microsoft.AspNetCore.Mvc; +global using Microsoft.AspNetCore.ResponseCompression; +global using Microsoft.AspNetCore.Routing; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Diagnostics.HealthChecks; +global using Microsoft.Extensions.Hosting; +global using Microsoft.Extensions.Logging; +global using Microsoft.IdentityModel.Tokens; + +global using Npgsql; + +global using OpenTelemetry.Logs; +global using OpenTelemetry.Metrics; +global using OpenTelemetry.Trace; diff --git a/src/CleanApiStarter.AspNetCore/IEndpointGroup.cs b/src/CleanApiStarter.AspNetCore/IEndpointGroup.cs index cc4f2e9..66a2135 100644 --- a/src/CleanApiStarter.AspNetCore/IEndpointGroup.cs +++ b/src/CleanApiStarter.AspNetCore/IEndpointGroup.cs @@ -7,4 +7,4 @@ public interface IEndpointGroup static virtual string? RoutePrefix => null; static abstract void Map(RouteGroupBuilder groupBuilder); -} \ No newline at end of file +} diff --git a/src/CleanApiStarter.AspNetCore/OpenTelemetryDefaults.cs b/src/CleanApiStarter.AspNetCore/OpenTelemetryDefaults.cs index 154ef68..70733a2 100644 --- a/src/CleanApiStarter.AspNetCore/OpenTelemetryDefaults.cs +++ b/src/CleanApiStarter.AspNetCore/OpenTelemetryDefaults.cs @@ -38,4 +38,4 @@ private static IHostApplicationBuilder ConfigureOpenTelemetry(this IHostApplicat return builder; } -} \ No newline at end of file +} diff --git a/src/CleanApiStarter.AspNetCore/ProblemDetailsExceptionHandler.cs b/src/CleanApiStarter.AspNetCore/ProblemDetailsExceptionHandler.cs index 4f270a9..fe50223 100644 --- a/src/CleanApiStarter.AspNetCore/ProblemDetailsExceptionHandler.cs +++ b/src/CleanApiStarter.AspNetCore/ProblemDetailsExceptionHandler.cs @@ -69,4 +69,4 @@ public async ValueTask TryHandleAsync( return true; } -} \ No newline at end of file +} diff --git a/src/CleanApiStarter.AspNetCore/RequestIdMiddleware.cs b/src/CleanApiStarter.AspNetCore/RequestIdMiddleware.cs index 0072cb2..003a9ef 100644 --- a/src/CleanApiStarter.AspNetCore/RequestIdMiddleware.cs +++ b/src/CleanApiStarter.AspNetCore/RequestIdMiddleware.cs @@ -20,4 +20,4 @@ private static string GetRequestId(HttpContext context) { return Activity.Current?.TraceId.ToString() ?? context.TraceIdentifier; } -} \ No newline at end of file +} diff --git a/src/CleanApiStarter.AspNetCore/UserIdHttpLoggingInterceptor.cs b/src/CleanApiStarter.AspNetCore/UserIdHttpLoggingInterceptor.cs index 4e8fff7..81c7243 100644 --- a/src/CleanApiStarter.AspNetCore/UserIdHttpLoggingInterceptor.cs +++ b/src/CleanApiStarter.AspNetCore/UserIdHttpLoggingInterceptor.cs @@ -25,4 +25,4 @@ private static string GetUserId(HttpContext httpContext) ?? httpContext.User.FindFirstValue(JwtRegisteredClaimNames.Sub) ?? AnonymousUserId; } -} \ No newline at end of file +} diff --git a/src/CleanApiStarter.AspNetCore/ValidationFilter.cs b/src/CleanApiStarter.AspNetCore/ValidationFilter.cs index ecaf242..63b2cc0 100644 --- a/src/CleanApiStarter.AspNetCore/ValidationFilter.cs +++ b/src/CleanApiStarter.AspNetCore/ValidationFilter.cs @@ -42,4 +42,4 @@ public sealed class ValidationFilter : IEndpointFilter return Results.ValidationProblem(errors, statusCode: StatusCodes.Status422UnprocessableEntity); } -} \ No newline at end of file +} diff --git a/src/CleanApiStarter.AspNetCore/WebApplicationExtensions.cs b/src/CleanApiStarter.AspNetCore/WebApplicationExtensions.cs index e6a6a58..87e5166 100644 --- a/src/CleanApiStarter.AspNetCore/WebApplicationExtensions.cs +++ b/src/CleanApiStarter.AspNetCore/WebApplicationExtensions.cs @@ -37,4 +37,4 @@ private static ApiVersion GetApiVersion(Type endpointGroupType) return new ApiVersion(majorVersion); } -} \ No newline at end of file +} diff --git a/src/CleanApiStarter.Configuration/AppSettings.cs b/src/CleanApiStarter.Configuration/AppSettings.cs index a0f32ea..19cdf16 100644 --- a/src/CleanApiStarter.Configuration/AppSettings.cs +++ b/src/CleanApiStarter.Configuration/AppSettings.cs @@ -9,4 +9,4 @@ public sealed class AppSettings [Required] [ValidateObjectMembers] public AuthenticationSettings Authentication { get; set; } = new(); -} \ No newline at end of file +} diff --git a/src/CleanApiStarter.Configuration/AuthenticationSettings.cs b/src/CleanApiStarter.Configuration/AuthenticationSettings.cs index ff38cd4..24f76fa 100644 --- a/src/CleanApiStarter.Configuration/AuthenticationSettings.cs +++ b/src/CleanApiStarter.Configuration/AuthenticationSettings.cs @@ -9,4 +9,4 @@ public sealed class AuthenticationSettings [Required] [ValidateObjectMembers] public JwtAuthenticationSettings Jwt { get; set; } = new(); -} \ No newline at end of file +} diff --git a/src/CleanApiStarter.Configuration/ConnectionStringSettings.cs b/src/CleanApiStarter.Configuration/ConnectionStringSettings.cs index deb6fbe..1299016 100644 --- a/src/CleanApiStarter.Configuration/ConnectionStringSettings.cs +++ b/src/CleanApiStarter.Configuration/ConnectionStringSettings.cs @@ -4,4 +4,4 @@ public sealed class ConnectionStringSettings { [Required] public string Postgres { get; set; } = string.Empty; -} \ No newline at end of file +} diff --git a/src/CleanApiStarter.Configuration/GlobalUsings.cs b/src/CleanApiStarter.Configuration/GlobalUsings.cs index 1fd17fc..a2250c9 100644 --- a/src/CleanApiStarter.Configuration/GlobalUsings.cs +++ b/src/CleanApiStarter.Configuration/GlobalUsings.cs @@ -2,4 +2,4 @@ global using Microsoft.Extensions.Configuration; global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.Options; \ No newline at end of file +global using Microsoft.Extensions.Options; diff --git a/src/CleanApiStarter.Configuration/GoogleAuthenticationSettings.cs b/src/CleanApiStarter.Configuration/GoogleAuthenticationSettings.cs index 1fda7db..b6f8aec 100644 --- a/src/CleanApiStarter.Configuration/GoogleAuthenticationSettings.cs +++ b/src/CleanApiStarter.Configuration/GoogleAuthenticationSettings.cs @@ -4,4 +4,4 @@ public sealed class GoogleAuthenticationSettings { [Required] public string ClientId { get; set; } = string.Empty; -} \ No newline at end of file +} diff --git a/src/CleanApiStarter.Configuration/JwtAuthenticationSettings.cs b/src/CleanApiStarter.Configuration/JwtAuthenticationSettings.cs index 9183996..083c6d8 100644 --- a/src/CleanApiStarter.Configuration/JwtAuthenticationSettings.cs +++ b/src/CleanApiStarter.Configuration/JwtAuthenticationSettings.cs @@ -14,4 +14,4 @@ public sealed class JwtAuthenticationSettings [Range(1, 1440)] public int ExpirationMinutes { get; set; } = 60; -} \ No newline at end of file +} diff --git a/src/CleanApiStarter.Configuration/OptionsRegistrationExtensions.cs b/src/CleanApiStarter.Configuration/OptionsRegistrationExtensions.cs index 051f517..6b767c7 100644 --- a/src/CleanApiStarter.Configuration/OptionsRegistrationExtensions.cs +++ b/src/CleanApiStarter.Configuration/OptionsRegistrationExtensions.cs @@ -16,4 +16,4 @@ public static IServiceCollection AddAppSettings( return services; } -} \ No newline at end of file +} diff --git a/src/CleanApiStarter.Domain/Entities/Project.cs b/src/CleanApiStarter.Domain/Entities/Project.cs index a9073d7..37555ff 100644 --- a/src/CleanApiStarter.Domain/Entities/Project.cs +++ b/src/CleanApiStarter.Domain/Entities/Project.cs @@ -15,4 +15,4 @@ public sealed class Project public List Members { get; init; } = []; public List Tasks { get; init; } = []; -} \ No newline at end of file +} diff --git a/src/CleanApiStarter.Domain/Entities/ProjectMember.cs b/src/CleanApiStarter.Domain/Entities/ProjectMember.cs index d9ec077..c55ef9f 100644 --- a/src/CleanApiStarter.Domain/Entities/ProjectMember.cs +++ b/src/CleanApiStarter.Domain/Entities/ProjectMember.cs @@ -9,4 +9,4 @@ public sealed class ProjectMember public required DateTime CreatedAt { get; init; } public Project Project { get; init; } = null!; -} \ No newline at end of file +} diff --git a/src/CleanApiStarter.Domain/Entities/ProjectTask.cs b/src/CleanApiStarter.Domain/Entities/ProjectTask.cs index ae8cc3e..c12e61c 100644 --- a/src/CleanApiStarter.Domain/Entities/ProjectTask.cs +++ b/src/CleanApiStarter.Domain/Entities/ProjectTask.cs @@ -19,4 +19,4 @@ public sealed class ProjectTask public DateTime? CompletedAt { get; set; } public Project Project { get; init; } = null!; -} \ No newline at end of file +} diff --git a/src/CleanApiStarter.Domain/Entities/ProjectTaskStatus.cs b/src/CleanApiStarter.Domain/Entities/ProjectTaskStatus.cs index 8a1a987..f553dc5 100644 --- a/src/CleanApiStarter.Domain/Entities/ProjectTaskStatus.cs +++ b/src/CleanApiStarter.Domain/Entities/ProjectTaskStatus.cs @@ -5,4 +5,4 @@ public enum ProjectTaskStatus Todo = 0, InProgress = 1, Done = 2 -} \ No newline at end of file +} diff --git a/src/CleanApiStarter.Infrastructure/DependencyInjection.cs b/src/CleanApiStarter.Infrastructure/DependencyInjection.cs index fca1035..12dcd54 100644 --- a/src/CleanApiStarter.Infrastructure/DependencyInjection.cs +++ b/src/CleanApiStarter.Infrastructure/DependencyInjection.cs @@ -1,22 +1,22 @@ -namespace CleanApiStarter.Infrastructure; - -public static class DependencyInjection -{ - public static IServiceCollection AddInfrastructure(this IServiceCollection services) - { - services.AddDbContext((serviceProvider, options) => - { - AppSettings appSettings = serviceProvider.GetRequiredService(); - options.UseNpgsql(appSettings.ConnectionStrings.Postgres); - }); - - services.AddIdentityCore() - .AddRoles() - .AddEntityFrameworkStores(); - - services.AddScoped(); - services.AddScoped(); - - return services; - } -} \ No newline at end of file +namespace CleanApiStarter.Infrastructure; + +public static class DependencyInjection +{ + public static IServiceCollection AddInfrastructure(this IServiceCollection services) + { + services.AddDbContext((serviceProvider, options) => + { + AppSettings appSettings = serviceProvider.GetRequiredService(); + options.UseNpgsql(appSettings.ConnectionStrings.Postgres); + }); + + services.AddIdentityCore() + .AddRoles() + .AddEntityFrameworkStores(); + + services.AddScoped(); + services.AddScoped(); + + return services; + } +} diff --git a/src/CleanApiStarter.Infrastructure/GlobalUsings.cs b/src/CleanApiStarter.Infrastructure/GlobalUsings.cs index f260b69..8801542 100644 --- a/src/CleanApiStarter.Infrastructure/GlobalUsings.cs +++ b/src/CleanApiStarter.Infrastructure/GlobalUsings.cs @@ -18,4 +18,4 @@ global using Microsoft.EntityFrameworkCore; global using Microsoft.EntityFrameworkCore.Metadata.Builders; global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.IdentityModel.Tokens; \ No newline at end of file +global using Microsoft.IdentityModel.Tokens; diff --git a/src/CleanApiStarter.Infrastructure/Identity/ApplicationUser.cs b/src/CleanApiStarter.Infrastructure/Identity/ApplicationUser.cs index 2996a8e..c801b9f 100644 --- a/src/CleanApiStarter.Infrastructure/Identity/ApplicationUser.cs +++ b/src/CleanApiStarter.Infrastructure/Identity/ApplicationUser.cs @@ -1,3 +1,3 @@ namespace CleanApiStarter.Infrastructure.Identity; -public sealed class ApplicationUser : IdentityUser; \ No newline at end of file +public sealed class ApplicationUser : IdentityUser; diff --git a/src/CleanApiStarter.Infrastructure/Identity/GoogleAuthService.cs b/src/CleanApiStarter.Infrastructure/Identity/GoogleAuthService.cs index d6e61ce..367a101 100644 --- a/src/CleanApiStarter.Infrastructure/Identity/GoogleAuthService.cs +++ b/src/CleanApiStarter.Infrastructure/Identity/GoogleAuthService.cs @@ -148,4 +148,4 @@ private static void ThrowIfFailed(IdentityResult result) string errors = string.Join("; ", result.Errors.Select(error => error.Description)); throw new InvalidOperationException(errors); } -} \ No newline at end of file +} diff --git a/src/CleanApiStarter.Infrastructure/Persistence/ApplicationDbContext.cs b/src/CleanApiStarter.Infrastructure/Persistence/ApplicationDbContext.cs index 85b954f..d0e5c7e 100644 --- a/src/CleanApiStarter.Infrastructure/Persistence/ApplicationDbContext.cs +++ b/src/CleanApiStarter.Infrastructure/Persistence/ApplicationDbContext.cs @@ -15,4 +15,4 @@ protected override void OnModelCreating(ModelBuilder builder) builder.ApplyConfigurationsFromAssembly(typeof(ApplicationDbContext).Assembly); } -} \ No newline at end of file +} diff --git a/src/CleanApiStarter.Infrastructure/Persistence/Configuration/ProjectConfiguration.cs b/src/CleanApiStarter.Infrastructure/Persistence/Configuration/ProjectConfiguration.cs index 566888a..b919bb6 100644 --- a/src/CleanApiStarter.Infrastructure/Persistence/Configuration/ProjectConfiguration.cs +++ b/src/CleanApiStarter.Infrastructure/Persistence/Configuration/ProjectConfiguration.cs @@ -28,4 +28,4 @@ public void Configure(EntityTypeBuilder builder) builder.Property(project => project.CreatedAt) .HasColumnName("created_at"); } -} \ No newline at end of file +} diff --git a/src/CleanApiStarter.Infrastructure/Persistence/Configuration/ProjectMemberConfiguration.cs b/src/CleanApiStarter.Infrastructure/Persistence/Configuration/ProjectMemberConfiguration.cs index 3816dca..f1a3cf1 100644 --- a/src/CleanApiStarter.Infrastructure/Persistence/Configuration/ProjectMemberConfiguration.cs +++ b/src/CleanApiStarter.Infrastructure/Persistence/Configuration/ProjectMemberConfiguration.cs @@ -27,4 +27,4 @@ public void Configure(EntityTypeBuilder builder) .HasForeignKey(member => member.ProjectId) .OnDelete(DeleteBehavior.Cascade); } -} \ No newline at end of file +} diff --git a/src/CleanApiStarter.Infrastructure/Persistence/Configuration/ProjectTaskConfiguration.cs b/src/CleanApiStarter.Infrastructure/Persistence/Configuration/ProjectTaskConfiguration.cs index 4382072..aa0dfb5 100644 --- a/src/CleanApiStarter.Infrastructure/Persistence/Configuration/ProjectTaskConfiguration.cs +++ b/src/CleanApiStarter.Infrastructure/Persistence/Configuration/ProjectTaskConfiguration.cs @@ -43,4 +43,4 @@ public void Configure(EntityTypeBuilder builder) .HasForeignKey(task => task.ProjectId) .OnDelete(DeleteBehavior.Cascade); } -} \ No newline at end of file +} diff --git a/src/CleanApiStarter.Infrastructure/Repositories/ProjectRepository.cs b/src/CleanApiStarter.Infrastructure/Repositories/ProjectRepository.cs index 1b7a196..04563a4 100644 --- a/src/CleanApiStarter.Infrastructure/Repositories/ProjectRepository.cs +++ b/src/CleanApiStarter.Infrastructure/Repositories/ProjectRepository.cs @@ -155,4 +155,4 @@ private IQueryable ProjectTasksForMember(Guid projectId, string use .Where(task => task.ProjectId == projectId) .Where(task => task.Project.Members.Any(member => member.UserId == userId)); } -} \ No newline at end of file +} diff --git a/tests/CleanApiStarter.Api.IntegrationTests/Features/Projects/ProjectsTests.cs b/tests/CleanApiStarter.Api.IntegrationTests/Features/Projects/ProjectsTests.cs index 253ff64..eebc218 100644 --- a/tests/CleanApiStarter.Api.IntegrationTests/Features/Projects/ProjectsTests.cs +++ b/tests/CleanApiStarter.Api.IntegrationTests/Features/Projects/ProjectsTests.cs @@ -52,4 +52,4 @@ public async Task Projects_PostAuthenticatedRequest_CreatesProject() } public TestContext TestContext { get; set; } -} \ No newline at end of file +} diff --git a/tests/CleanApiStarter.Api.IntegrationTests/GlobalUsings.cs b/tests/CleanApiStarter.Api.IntegrationTests/GlobalUsings.cs index 1435a22..3fa1cfb 100644 --- a/tests/CleanApiStarter.Api.IntegrationTests/GlobalUsings.cs +++ b/tests/CleanApiStarter.Api.IntegrationTests/GlobalUsings.cs @@ -6,4 +6,4 @@ global using Microsoft.VisualStudio.TestTools.UnitTesting; -global using Shouldly; \ No newline at end of file +global using Shouldly; diff --git a/tests/CleanApiStarter.Application.UnitTests/Features/Projects/ProjectServiceTests.cs b/tests/CleanApiStarter.Application.UnitTests/Features/Projects/ProjectServiceTests.cs index fd03231..73c9e68 100644 --- a/tests/CleanApiStarter.Application.UnitTests/Features/Projects/ProjectServiceTests.cs +++ b/tests/CleanApiStarter.Application.UnitTests/Features/Projects/ProjectServiceTests.cs @@ -37,4 +37,4 @@ public async Task CompleteTaskAsync_TaskIsAlreadyCompleted_ReturnsAlreadyComplet result.ShouldBe(ProjectTaskMutationResult.AlreadyCompleted); _ = projectRepository.DidNotReceive().SaveChangesAsync(Arg.Any()); } -} \ No newline at end of file +} diff --git a/tests/CleanApiStarter.Application.UnitTests/GlobalUsings.cs b/tests/CleanApiStarter.Application.UnitTests/GlobalUsings.cs index 6c472a4..02c2906 100644 --- a/tests/CleanApiStarter.Application.UnitTests/GlobalUsings.cs +++ b/tests/CleanApiStarter.Application.UnitTests/GlobalUsings.cs @@ -11,4 +11,4 @@ global using Shouldly; -global using Xunit; \ No newline at end of file +global using Xunit; diff --git a/tests/CleanApiStarter.Tests/Common/ApiApplicationFactory.cs b/tests/CleanApiStarter.Tests/Common/ApiApplicationFactory.cs index 1277724..feab947 100644 --- a/tests/CleanApiStarter.Tests/Common/ApiApplicationFactory.cs +++ b/tests/CleanApiStarter.Tests/Common/ApiApplicationFactory.cs @@ -97,4 +97,4 @@ private static IEnumerable GetDatabaseScriptPaths() throw new DirectoryNotFoundException("Could not find database/migrations from the test output directory."); } -} \ No newline at end of file +} diff --git a/tests/CleanApiStarter.Tests/Common/AutoNSubstituteDataAttribute.cs b/tests/CleanApiStarter.Tests/Common/AutoNSubstituteDataAttribute.cs index 09eb258..1ff103c 100644 --- a/tests/CleanApiStarter.Tests/Common/AutoNSubstituteDataAttribute.cs +++ b/tests/CleanApiStarter.Tests/Common/AutoNSubstituteDataAttribute.cs @@ -10,4 +10,4 @@ private static IFixture CreateFixture() return fixture; } -} \ No newline at end of file +} diff --git a/tests/CleanApiStarter.Tests/GlobalUsings.cs b/tests/CleanApiStarter.Tests/GlobalUsings.cs index c425319..5aa93fe 100644 --- a/tests/CleanApiStarter.Tests/GlobalUsings.cs +++ b/tests/CleanApiStarter.Tests/GlobalUsings.cs @@ -13,4 +13,4 @@ global using Npgsql; -global using Testcontainers.PostgreSql; \ No newline at end of file +global using Testcontainers.PostgreSql;