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;