From a734a56ca6597dacbea5c62311de2ddaca68cb95 Mon Sep 17 00:00:00 2001 From: Mostafa Atef Date: Mon, 27 Apr 2026 07:38:16 +0300 Subject: [PATCH 01/15] fix indentation error in dev-ci file --- .github/workflows/dev-ci.yml | 70 ++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/.github/workflows/dev-ci.yml b/.github/workflows/dev-ci.yml index fe28b2f..4ce87a1 100644 --- a/.github/workflows/dev-ci.yml +++ b/.github/workflows/dev-ci.yml @@ -8,42 +8,42 @@ on: workflow_dispatch: jobs: -unit-tests: - name: Unit Tests - runs-on: ubuntu-latest + unit-tests: + name: Unit Tests + runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v6 + steps: + - name: Checkout + uses: actions/checkout@v6 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v4 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v4 - - name: Build test image - uses: docker/build-push-action@v7 - with: - context: . - file: Docker/Dockerfile.test - push: false - load: true - tags: socialmedia-test:${{ github.sha }} - cache-from: type=gha,scope=test-image - cache-to: type=gha,mode=max,scope=test-image - - name: Run unit tests - run: | - mkdir -p ${{ github.workspace }}/test-results/unit - docker run --rm \ - -v ${{ github.workspace }}/test-results/unit:/app/test-results \ - socialmedia-test:${{ github.sha }} \ - --no-build \ - --filter "Category=Unit" \ - --logger "trx;LogFileName=unit-results.trx" \ - --results-directory /app/test-results + - name: Build test image + uses: docker/build-push-action@v7 + with: + context: . + file: Docker/Dockerfile.test + push: false + load: true + tags: socialmedia-test:${{ github.sha }} + cache-from: type=gha,scope=test-image + cache-to: type=gha,mode=max,scope=test-image + - name: Run unit tests + run: | + mkdir -p ${{ github.workspace }}/test-results/unit + docker run --rm \ + -v ${{ github.workspace }}/test-results/unit:/app/test-results \ + socialmedia-test:${{ github.sha }} \ + --no-build \ + --filter "Category=Unit" \ + --logger "trx;LogFileName=unit-results.trx" \ + --results-directory /app/test-results - - name: Publish unit test results - uses: dorny/test-reporter@v3 - if: always() - with: - name: Unit Test Results - path: test-results/unit/*.trx - reporter: dotnet-trx + - name: Publish unit test results + uses: dorny/test-reporter@v3 + if: always() + with: + name: Unit Test Results + path: test-results/unit/*.trx + reporter: dotnet-trx From 63dfd3b645b62ef33dd807dc8c8d5baef1f2529d Mon Sep 17 00:00:00 2001 From: Mostafa Atef Date: Mon, 27 Apr 2026 07:51:40 +0300 Subject: [PATCH 02/15] remove Ryuk from caching as testcontainers manages it --- .github/workflows/main-ci.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/main-ci.yml b/.github/workflows/main-ci.yml index 77ed615..59cfb52 100644 --- a/.github/workflows/main-ci.yml +++ b/.github/workflows/main-ci.yml @@ -80,22 +80,19 @@ jobs: uses: actions/cache@v5 with: path: /tmp/docker-images - key: docker-images-${{ runner.os }}-mssql-2022-ryuk-0.14.0 + key: docker-images-${{ runner.os }}-mssql-2022-latest - name: Load cached images if: steps.cache-docker.outputs.cache-hit == 'true' run: | docker load < /tmp/docker-images/mssql.tar - docker load < /tmp/docker-images/ryuk.tar - name: Pull and save images if: steps.cache-docker.outputs.cache-hit != 'true' run: | docker pull mcr.microsoft.com/mssql/server:2022-latest - docker pull testcontainersofficial/ryuk:0.14.0 mkdir -p /tmp/docker-images docker save mcr.microsoft.com/mssql/server:2022-latest > /tmp/docker-images/mssql.tar - docker save testcontainersofficial/ryuk:0.14.0 > /tmp/docker-images/ryuk.tar - name: Run integration tests env: From 7656690c959a4f6dcc622afb7f9b00df66061882 Mon Sep 17 00:00:00 2001 From: Mostafa Atef Date: Mon, 27 Apr 2026 08:15:52 +0300 Subject: [PATCH 03/15] remove the images cache steps --- .github/workflows/main-ci.yml | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/.github/workflows/main-ci.yml b/.github/workflows/main-ci.yml index 59cfb52..e95b0c2 100644 --- a/.github/workflows/main-ci.yml +++ b/.github/workflows/main-ci.yml @@ -75,28 +75,7 @@ jobs: cache-from: type=gha,scope=test-image cache-to: type=gha,mode=max,scope=test-image - - name: Cache Testcontainers Docker images - id: cache-docker - uses: actions/cache@v5 - with: - path: /tmp/docker-images - key: docker-images-${{ runner.os }}-mssql-2022-latest - - - name: Load cached images - if: steps.cache-docker.outputs.cache-hit == 'true' - run: | - docker load < /tmp/docker-images/mssql.tar - - - name: Pull and save images - if: steps.cache-docker.outputs.cache-hit != 'true' - run: | - docker pull mcr.microsoft.com/mssql/server:2022-latest - mkdir -p /tmp/docker-images - docker save mcr.microsoft.com/mssql/server:2022-latest > /tmp/docker-images/mssql.tar - - name: Run integration tests - env: - TESTCONTAINERS_RYUK_DISABLED: "false" run: | mkdir -p ${{ github.workspace }}/test-results/integration docker run --rm \ From ff7cd21dca0f41aa4ef87eae686a4445065fb3b6 Mon Sep 17 00:00:00 2001 From: Mostafa Atef Date: Mon, 27 Apr 2026 08:30:25 +0300 Subject: [PATCH 04/15] Update integration test configuration to use in-memory connection string at startup --- .../IntegrationTestWebAppFactory.cs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/tests/SocialMedia.IntegrationTests/Infrastructure/IntegrationTestWebAppFactory.cs b/tests/SocialMedia.IntegrationTests/Infrastructure/IntegrationTestWebAppFactory.cs index 7659e6b..abfbdc0 100644 --- a/tests/SocialMedia.IntegrationTests/Infrastructure/IntegrationTestWebAppFactory.cs +++ b/tests/SocialMedia.IntegrationTests/Infrastructure/IntegrationTestWebAppFactory.cs @@ -5,6 +5,7 @@ using Microsoft.AspNetCore.TestHost; using Microsoft.Data.SqlClient; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Hosting; @@ -34,18 +35,28 @@ public class IntegrationTestWebAppFactory : WebApplicationFactory, IAsy protected override void ConfigureWebHost(IWebHostBuilder builder) { base.ConfigureWebHost(builder); + var testConnectionString = _msSqlContainer.GetConnectionString(); + + builder.ConfigureAppConfiguration((_, configurationBuilder) => + { + configurationBuilder.AddInMemoryCollection(new Dictionary + { + ["ConnectionStrings:sqlserverConnectionString"] = testConnectionString + }); + }); + builder.ConfigureTestServices(services => { services.RemoveAll(typeof(DbContextOptions)); services.AddDbContext(options => { - options.UseSqlServer(_msSqlContainer.GetConnectionString()); + options.UseSqlServer(testConnectionString); }); services.RemoveAll(typeof(IHostedService)); services.RemoveAll(typeof(ISqlConnectionFactory)); - services.AddSingleton(new SqlConnectionFactory(_msSqlContainer.GetConnectionString())); + services.AddSingleton(new SqlConnectionFactory(testConnectionString)); }); } From a2528184b17900392b880db56f3e65c8cded0b02 Mon Sep 17 00:00:00 2001 From: Mostafa Atef Date: Mon, 27 Apr 2026 08:57:37 +0300 Subject: [PATCH 05/15] Refactor caching configuration in CI workflows and Dockerfile --- .github/workflows/dev-ci.yml | 4 ++-- .github/workflows/main-ci.yml | 8 ++++---- Docker/Dockerfile.test | 9 +++++---- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/.github/workflows/dev-ci.yml b/.github/workflows/dev-ci.yml index 4ce87a1..4b6a401 100644 --- a/.github/workflows/dev-ci.yml +++ b/.github/workflows/dev-ci.yml @@ -27,8 +27,8 @@ jobs: push: false load: true tags: socialmedia-test:${{ github.sha }} - cache-from: type=gha,scope=test-image - cache-to: type=gha,mode=max,scope=test-image + cache-from: type=gha + cache-to: type=gha,mode=max - name: Run unit tests run: | mkdir -p ${{ github.workspace }}/test-results/unit diff --git a/.github/workflows/main-ci.yml b/.github/workflows/main-ci.yml index e95b0c2..d939572 100644 --- a/.github/workflows/main-ci.yml +++ b/.github/workflows/main-ci.yml @@ -31,8 +31,8 @@ jobs: push: false load: true tags: socialmedia-test:${{ github.sha }} - cache-from: type=gha,scope=test-image - cache-to: type=gha,mode=max,scope=test-image + cache-from: type=gha + cache-to: type=gha,mode=max - name: Run unit tests run: | mkdir -p ${{ github.workspace }}/test-results/unit @@ -72,8 +72,8 @@ jobs: push: false load: true tags: socialmedia-test:${{ github.sha }} - cache-from: type=gha,scope=test-image - cache-to: type=gha,mode=max,scope=test-image + cache-from: type=gha + cache-to: type=gha,mode=max - name: Run integration tests run: | diff --git a/Docker/Dockerfile.test b/Docker/Dockerfile.test index 6fef0a1..d4870fe 100644 --- a/Docker/Dockerfile.test +++ b/Docker/Dockerfile.test @@ -1,4 +1,5 @@ FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build + WORKDIR /app ENV NUGET_PACKAGES=/root/.nuget/packages @@ -13,14 +14,14 @@ COPY ./tests/SocialMedia.Application.UnitTests/*.csproj ./tests/SocialMedia.Ap COPY ./tests/SocialMedia.IntegrationTests/*.csproj ./tests/SocialMedia.IntegrationTests/ COPY ./SocialMedia.slnx . -RUN --mount=type=cache,id=nuget-http,target=/root/.local/share/NuGet/http-cache \ - --mount=type=cache,id=nuget-global,target=$NUGET_PACKAGES \ +RUN --mount=type=cache,target=/root/.local/share/NuGet/http-cache \ + --mount=type=cache,target=$NUGET_PACKAGES \ dotnet restore ./SocialMedia.slnx COPY . . -RUN --mount=type=cache,id=nuget-http,target=/root/.local/share/NuGet/http-cache \ - --mount=type=cache,id=nuget-global,target=$NUGET_PACKAGES \ +RUN --mount=type=cache,target=/root/.local/share/NuGet/http-cache \ + --mount=type=cache,target=$NUGET_PACKAGES \ dotnet build ./SocialMedia.slnx \ --no-restore \ -c Debug From 179e1db9312e66f5e78db41fe8a6a6c9ddbf800c Mon Sep 17 00:00:00 2001 From: Mostafa Atef Date: Mon, 27 Apr 2026 09:09:24 +0300 Subject: [PATCH 06/15] Set SQL connection string environment variable in integration test factory --- .../Infrastructure/IntegrationTestWebAppFactory.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/SocialMedia.IntegrationTests/Infrastructure/IntegrationTestWebAppFactory.cs b/tests/SocialMedia.IntegrationTests/Infrastructure/IntegrationTestWebAppFactory.cs index abfbdc0..8744c21 100644 --- a/tests/SocialMedia.IntegrationTests/Infrastructure/IntegrationTestWebAppFactory.cs +++ b/tests/SocialMedia.IntegrationTests/Infrastructure/IntegrationTestWebAppFactory.cs @@ -26,11 +26,12 @@ namespace SocialMedia.IntegrationTests.Infrastructure; public class IntegrationTestWebAppFactory : WebApplicationFactory, IAsyncLifetime { + private const string SqlConnectionStringEnvKey = "ConnectionStrings__sqlserverConnectionString"; private readonly MsSqlContainer _msSqlContainer = new MsSqlBuilder("mcr.microsoft.com/mssql/server:2022-latest") .WithCleanUp(true) .Build(); public HttpClient HttpClient { get; private set; } = null!; - private SqlConnection _dbConnection; + private SqlConnection _dbConnection = null!; private Respawner _respawner = null!; protected override void ConfigureWebHost(IWebHostBuilder builder) { @@ -63,6 +64,7 @@ protected override void ConfigureWebHost(IWebHostBuilder builder) public async ValueTask InitializeAsync() { await _msSqlContainer.StartAsync(); + Environment.SetEnvironmentVariable(SqlConnectionStringEnvKey, _msSqlContainer.GetConnectionString()); HttpClient = CreateClient(); _dbConnection = new SqlConnection(_msSqlContainer.GetConnectionString()); await _dbConnection.OpenAsync(); @@ -88,5 +90,6 @@ public override async ValueTask DisposeAsync() await _dbConnection.DisposeAsync(); await base.DisposeAsync(); await _msSqlContainer.DisposeAsync(); + Environment.SetEnvironmentVariable(SqlConnectionStringEnvKey, null); } } From 7643fcd01a1a84cdda136fcb2fce160c87c0decc Mon Sep 17 00:00:00 2001 From: Mostafa Atef Date: Mon, 27 Apr 2026 09:11:59 +0300 Subject: [PATCH 07/15] Update Dockerfiles to remove --no-restore flag and set build configuration to Release --- Docker/Dockerfile.prod | 1 - Docker/Dockerfile.test | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/Docker/Dockerfile.prod b/Docker/Dockerfile.prod index ed30be8..c6b765f 100644 --- a/Docker/Dockerfile.prod +++ b/Docker/Dockerfile.prod @@ -25,7 +25,6 @@ COPY . . RUN --mount=type=cache,id=nuget-http,target=/root/.local/share/NuGet/http-cache \ --mount=type=cache,id=nuget-global,target=$NUGET_PACKAGES \ dotnet build ./SocialMedia.slnx \ - --no-restore \ -c Release diff --git a/Docker/Dockerfile.test b/Docker/Dockerfile.test index d4870fe..d744f60 100644 --- a/Docker/Dockerfile.test +++ b/Docker/Dockerfile.test @@ -23,8 +23,7 @@ COPY . . RUN --mount=type=cache,target=/root/.local/share/NuGet/http-cache \ --mount=type=cache,target=$NUGET_PACKAGES \ dotnet build ./SocialMedia.slnx \ - --no-restore \ - -c Debug + -c Release ENTRYPOINT ["dotnet", "test"] CMD ["--no-build"] \ No newline at end of file From 2361abac7db2217a30090d6340583fa83b50e7d4 Mon Sep 17 00:00:00 2001 From: Mostafa Atef Date: Mon, 27 Apr 2026 09:20:52 +0300 Subject: [PATCH 08/15] Refactor Dockerfile.test to streamline cache usage and revert build configuration to Debug --- Docker/Dockerfile.test | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Docker/Dockerfile.test b/Docker/Dockerfile.test index d744f60..7b3be78 100644 --- a/Docker/Dockerfile.test +++ b/Docker/Dockerfile.test @@ -14,16 +14,14 @@ COPY ./tests/SocialMedia.Application.UnitTests/*.csproj ./tests/SocialMedia.Ap COPY ./tests/SocialMedia.IntegrationTests/*.csproj ./tests/SocialMedia.IntegrationTests/ COPY ./SocialMedia.slnx . -RUN --mount=type=cache,target=/root/.local/share/NuGet/http-cache \ - --mount=type=cache,target=$NUGET_PACKAGES \ +RUN --mount=type=cache,target=$NUGET_PACKAGES \ dotnet restore ./SocialMedia.slnx COPY . . -RUN --mount=type=cache,target=/root/.local/share/NuGet/http-cache \ - --mount=type=cache,target=$NUGET_PACKAGES \ +RUN --mount=type=cache,target=$NUGET_PACKAGES \ dotnet build ./SocialMedia.slnx \ - -c Release + -c Debug ENTRYPOINT ["dotnet", "test"] CMD ["--no-build"] \ No newline at end of file From c1eb46727b5dc7ab10fabfabd2cabf49bfde99e0 Mon Sep 17 00:00:00 2001 From: Mostafa Atef Date: Mon, 27 Apr 2026 09:33:23 +0300 Subject: [PATCH 09/15] Add appsettings.Testing.json for integration tests and update configuration to use it --- .../IntegrationTestWebAppFactory.cs | 5 +- .../appsettings.Testing.json | 59 +++++++++++++++++++ 2 files changed, 60 insertions(+), 4 deletions(-) create mode 100644 tests/SocialMedia.IntegrationTests/appsettings.Testing.json diff --git a/tests/SocialMedia.IntegrationTests/Infrastructure/IntegrationTestWebAppFactory.cs b/tests/SocialMedia.IntegrationTests/Infrastructure/IntegrationTestWebAppFactory.cs index 8744c21..ee8cbed 100644 --- a/tests/SocialMedia.IntegrationTests/Infrastructure/IntegrationTestWebAppFactory.cs +++ b/tests/SocialMedia.IntegrationTests/Infrastructure/IntegrationTestWebAppFactory.cs @@ -40,10 +40,7 @@ protected override void ConfigureWebHost(IWebHostBuilder builder) builder.ConfigureAppConfiguration((_, configurationBuilder) => { - configurationBuilder.AddInMemoryCollection(new Dictionary - { - ["ConnectionStrings:sqlserverConnectionString"] = testConnectionString - }); + configurationBuilder.AddJsonFile("appsettings.Testing.json", optional: false); }); builder.ConfigureTestServices(services => diff --git a/tests/SocialMedia.IntegrationTests/appsettings.Testing.json b/tests/SocialMedia.IntegrationTests/appsettings.Testing.json new file mode 100644 index 0000000..3de8abe --- /dev/null +++ b/tests/SocialMedia.IntegrationTests/appsettings.Testing.json @@ -0,0 +1,59 @@ +{ + "Serilog": { + "Using": ["Serilog.Sinks.Console", "Serilog.Sinks.Seq"], + "MinimumLevel": { + "Default": "Information", + "Override": { + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information", + "System": "Warning" + } + }, + "Enrich": ["FromLogContext", "WithMachineName", "WithThreadId"], + "WriteTo": [ + { + "Name": "Console", + "Args": { + "outputTemplate": "[{Timestamp:HH:mm:ss} {Level:u3}] {SourceContext} - {Message:lj}{NewLine}{Exception}" + } + }, + { + "Name": "Seq", + "Args": { + "serverUrl": "http://social-seq:5341" + } + } + ] + }, + "ConnectionStrings": { + "sqlServerConnectionString": "Data Source=;Initial Catalog=SocialMedia;User ID=sa;Password=;Connect Timeout=30;Trust Server Certificate=True;Authentication=SqlPassword;" + }, + "jwt": { + "Issuer": "SocialMedia.WebApi", + "Audience": "SocialMedia.Frontend", + "Lifetime": 60, + "RefreshTokenLifetime": 120, + "SigningKey": "your-secure-signing-key" + }, + "email": { + "Smtp": "smtp.gmail.com", + "Port": 587, + "Username": "your-email@gmail.com", + "Password": "your-app-password", + "From": "your-email@gmail.com", + "BaseUrl": "https://localhost:5000", + "EmailVerificationPath": "/api/auth/verify-email?token={token}", + "PasswordResetPath": "/api/auth/reset-password?token={token}" + }, + "supabase": { + "Url": "https://test-supabase-url.supabase.co", + "Key": "test-supabase-key" + }, + "durations": { + "EmailVerificationTokenExpiryMinutes": 30, + "PasswordResetTokenExpiryMinutes": 30 + }, + "FileUpload": { + "Provider": "Server" + } +} From baa851aa8ecc65c951cea7ae42c137e3bf5bd008 Mon Sep 17 00:00:00 2001 From: Mostafa Atef Date: Mon, 27 Apr 2026 09:42:34 +0300 Subject: [PATCH 10/15] Add appsettings.Testing.json to project file for integration tests --- .../SocialMedia.IntegrationTests.csproj | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/SocialMedia.IntegrationTests/SocialMedia.IntegrationTests.csproj b/tests/SocialMedia.IntegrationTests/SocialMedia.IntegrationTests.csproj index ff6358d..726f8de 100644 --- a/tests/SocialMedia.IntegrationTests/SocialMedia.IntegrationTests.csproj +++ b/tests/SocialMedia.IntegrationTests/SocialMedia.IntegrationTests.csproj @@ -26,4 +26,10 @@ + + + Always + + + From b7df4939f6fe57ec07a1362dd9349de77e160376 Mon Sep 17 00:00:00 2001 From: Mostafa Atef Date: Mon, 27 Apr 2026 11:13:15 +0300 Subject: [PATCH 11/15] Add appsettings.Testing.json for integration tests and update configuration --- Docker/Dockerfile.test | 1 + src/SocialMedia.WebApi/SocialMedia.WebApi.csproj | 6 ++++-- .../SocialMedia.WebApi}/appsettings.Testing.json | 2 +- .../Infrastructure/IntegrationTestWebAppFactory.cs | 12 +++--------- .../SocialMedia.IntegrationTests.csproj | 6 ------ 5 files changed, 9 insertions(+), 18 deletions(-) rename {tests/SocialMedia.IntegrationTests => src/SocialMedia.WebApi}/appsettings.Testing.json (93%) diff --git a/Docker/Dockerfile.test b/Docker/Dockerfile.test index 7b3be78..fa933e9 100644 --- a/Docker/Dockerfile.test +++ b/Docker/Dockerfile.test @@ -3,6 +3,7 @@ FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build WORKDIR /app ENV NUGET_PACKAGES=/root/.nuget/packages +ENV ASPNETCORE_ENVIRONMENT=Testing COPY Directory.Packages.props . COPY Directory.Build.props . diff --git a/src/SocialMedia.WebApi/SocialMedia.WebApi.csproj b/src/SocialMedia.WebApi/SocialMedia.WebApi.csproj index 9307c6f..ed77b38 100644 --- a/src/SocialMedia.WebApi/SocialMedia.WebApi.csproj +++ b/src/SocialMedia.WebApi/SocialMedia.WebApi.csproj @@ -28,8 +28,10 @@ - - + + + + diff --git a/tests/SocialMedia.IntegrationTests/appsettings.Testing.json b/src/SocialMedia.WebApi/appsettings.Testing.json similarity index 93% rename from tests/SocialMedia.IntegrationTests/appsettings.Testing.json rename to src/SocialMedia.WebApi/appsettings.Testing.json index 3de8abe..a4c2594 100644 --- a/tests/SocialMedia.IntegrationTests/appsettings.Testing.json +++ b/src/SocialMedia.WebApi/appsettings.Testing.json @@ -33,7 +33,7 @@ "Audience": "SocialMedia.Frontend", "Lifetime": 60, "RefreshTokenLifetime": 120, - "SigningKey": "your-secure-signing-key" + "SigningKey": "your-secure-signing-key-for-jwt-tokensaklhg" }, "email": { "Smtp": "smtp.gmail.com", diff --git a/tests/SocialMedia.IntegrationTests/Infrastructure/IntegrationTestWebAppFactory.cs b/tests/SocialMedia.IntegrationTests/Infrastructure/IntegrationTestWebAppFactory.cs index ee8cbed..3eb3e96 100644 --- a/tests/SocialMedia.IntegrationTests/Infrastructure/IntegrationTestWebAppFactory.cs +++ b/tests/SocialMedia.IntegrationTests/Infrastructure/IntegrationTestWebAppFactory.cs @@ -26,7 +26,6 @@ namespace SocialMedia.IntegrationTests.Infrastructure; public class IntegrationTestWebAppFactory : WebApplicationFactory, IAsyncLifetime { - private const string SqlConnectionStringEnvKey = "ConnectionStrings__sqlserverConnectionString"; private readonly MsSqlContainer _msSqlContainer = new MsSqlBuilder("mcr.microsoft.com/mssql/server:2022-latest") .WithCleanUp(true) .Build(); @@ -38,11 +37,6 @@ protected override void ConfigureWebHost(IWebHostBuilder builder) base.ConfigureWebHost(builder); var testConnectionString = _msSqlContainer.GetConnectionString(); - builder.ConfigureAppConfiguration((_, configurationBuilder) => - { - configurationBuilder.AddJsonFile("appsettings.Testing.json", optional: false); - }); - builder.ConfigureTestServices(services => { services.RemoveAll(typeof(DbContextOptions)); @@ -61,9 +55,10 @@ protected override void ConfigureWebHost(IWebHostBuilder builder) public async ValueTask InitializeAsync() { await _msSqlContainer.StartAsync(); - Environment.SetEnvironmentVariable(SqlConnectionStringEnvKey, _msSqlContainer.GetConnectionString()); + var testConnectionString = _msSqlContainer.GetConnectionString(); + HttpClient = CreateClient(); - _dbConnection = new SqlConnection(_msSqlContainer.GetConnectionString()); + _dbConnection = new SqlConnection(testConnectionString); await _dbConnection.OpenAsync(); _respawner = await Respawner.CreateAsync(_dbConnection, new RespawnerOptions { @@ -87,6 +82,5 @@ public override async ValueTask DisposeAsync() await _dbConnection.DisposeAsync(); await base.DisposeAsync(); await _msSqlContainer.DisposeAsync(); - Environment.SetEnvironmentVariable(SqlConnectionStringEnvKey, null); } } diff --git a/tests/SocialMedia.IntegrationTests/SocialMedia.IntegrationTests.csproj b/tests/SocialMedia.IntegrationTests/SocialMedia.IntegrationTests.csproj index 726f8de..ff6358d 100644 --- a/tests/SocialMedia.IntegrationTests/SocialMedia.IntegrationTests.csproj +++ b/tests/SocialMedia.IntegrationTests/SocialMedia.IntegrationTests.csproj @@ -26,10 +26,4 @@ - - - Always - - - From 97fc804efd6e4740f22fb3910924aa1fd237ba69 Mon Sep 17 00:00:00 2001 From: Mostafa Atef Date: Mon, 27 Apr 2026 11:29:05 +0300 Subject: [PATCH 12/15] Refactor CI workflows to use .NET setup and streamline unit test execution --- .github/workflows/dev-ci.yml | 78 ++++++++++++++++++----------------- .github/workflows/main-ci.yml | 38 +++++++++-------- 2 files changed, 60 insertions(+), 56 deletions(-) diff --git a/.github/workflows/dev-ci.yml b/.github/workflows/dev-ci.yml index 4b6a401..0250454 100644 --- a/.github/workflows/dev-ci.yml +++ b/.github/workflows/dev-ci.yml @@ -9,41 +9,43 @@ on: jobs: unit-tests: - name: Unit Tests - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v6 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v4 - - - name: Build test image - uses: docker/build-push-action@v7 - with: - context: . - file: Docker/Dockerfile.test - push: false - load: true - tags: socialmedia-test:${{ github.sha }} - cache-from: type=gha - cache-to: type=gha,mode=max - - name: Run unit tests - run: | - mkdir -p ${{ github.workspace }}/test-results/unit - docker run --rm \ - -v ${{ github.workspace }}/test-results/unit:/app/test-results \ - socialmedia-test:${{ github.sha }} \ - --no-build \ - --filter "Category=Unit" \ - --logger "trx;LogFileName=unit-results.trx" \ - --results-directory /app/test-results - - - name: Publish unit test results - uses: dorny/test-reporter@v3 - if: always() - with: - name: Unit Test Results - path: test-results/unit/*.trx - reporter: dotnet-trx + name: Unit Tests + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Setup .NET + uses: actions/setup-dotnet@v5 + with: + dotnet-version: 10.0.x + + - name: Cache NuGet packages + uses: actions/cache@v5 + with: + path: ~/.nuget/packages + key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/Directory.Packages.props') }} + restore-keys: | + ${{ runner.os }}-nuget- + + - name: Restore dependencies + run: dotnet restore + + - name: Build + run: dotnet build --no-restore --configuration Release + + - name: Run unit tests + run: | + dotnet test --no-build --configuration Release \ + --filter "Category=Unit" \ + --logger "trx;LogFileName=test-results.trx" \ + --results-directory test-results/unit + + - name: Publish unit test results + uses: dorny/test-reporter@v3 + if: always() + with: + name: Unit Test Results + path: test-results/unit/*.trx + reporter: dotnet-trx diff --git a/.github/workflows/main-ci.yml b/.github/workflows/main-ci.yml index d939572..7bb3b2c 100644 --- a/.github/workflows/main-ci.yml +++ b/.github/workflows/main-ci.yml @@ -20,29 +20,31 @@ jobs: - name: Checkout uses: actions/checkout@v6 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v4 + - name: Setup .NET + uses: actions/setup-dotnet@v5 + with: + dotnet-version: 10.0.x - - name: Build test image - uses: docker/build-push-action@v7 + - name: Cache NuGet packages + uses: actions/cache@v5 with: - context: . - file: Docker/Dockerfile.test - push: false - load: true - tags: socialmedia-test:${{ github.sha }} - cache-from: type=gha - cache-to: type=gha,mode=max + path: ~/.nuget/packages + key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/Directory.Packages.props') }} + restore-keys: | + ${{ runner.os }}-nuget- + + - name: Restore dependencies + run: dotnet restore + + - name: Build + run: dotnet build --no-restore --configuration Release + - name: Run unit tests run: | - mkdir -p ${{ github.workspace }}/test-results/unit - docker run --rm \ - -v ${{ github.workspace }}/test-results/unit:/app/test-results \ - socialmedia-test:${{ github.sha }} \ - --no-build \ + dotnet test --no-build --configuration Release \ --filter "Category=Unit" \ - --logger "trx;LogFileName=unit-results.trx" \ - --results-directory /app/test-results + --logger "trx;LogFileName=test-results.trx" \ + --results-directory test-results/unit - name: Publish unit test results uses: dorny/test-reporter@v3 From 59b09d85e8bf702eb83ec6cb1039a52e7da79135 Mon Sep 17 00:00:00 2001 From: Mostafa Atef Date: Mon, 27 Apr 2026 11:32:19 +0300 Subject: [PATCH 13/15] fix indentation in dev-ci --- .github/workflows/dev-ci.yml | 80 ++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/.github/workflows/dev-ci.yml b/.github/workflows/dev-ci.yml index 0250454..52af5fe 100644 --- a/.github/workflows/dev-ci.yml +++ b/.github/workflows/dev-ci.yml @@ -9,43 +9,43 @@ on: jobs: unit-tests: - name: Unit Tests - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v6 - - - name: Setup .NET - uses: actions/setup-dotnet@v5 - with: - dotnet-version: 10.0.x - - - name: Cache NuGet packages - uses: actions/cache@v5 - with: - path: ~/.nuget/packages - key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/Directory.Packages.props') }} - restore-keys: | - ${{ runner.os }}-nuget- - - - name: Restore dependencies - run: dotnet restore - - - name: Build - run: dotnet build --no-restore --configuration Release - - - name: Run unit tests - run: | - dotnet test --no-build --configuration Release \ - --filter "Category=Unit" \ - --logger "trx;LogFileName=test-results.trx" \ - --results-directory test-results/unit - - - name: Publish unit test results - uses: dorny/test-reporter@v3 - if: always() - with: - name: Unit Test Results - path: test-results/unit/*.trx - reporter: dotnet-trx + name: Unit Tests + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Setup .NET + uses: actions/setup-dotnet@v5 + with: + dotnet-version: 10.0.x + + - name: Cache NuGet packages + uses: actions/cache@v5 + with: + path: ~/.nuget/packages + key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/Directory.Packages.props') }} + restore-keys: | + ${{ runner.os }}-nuget- + + - name: Restore dependencies + run: dotnet restore + + - name: Build + run: dotnet build --no-restore --configuration Release + + - name: Run unit tests + run: | + dotnet test --no-build --configuration Release \ + --filter "Category=Unit" \ + --logger "trx;LogFileName=test-results.trx" \ + --results-directory test-results/unit + + - name: Publish unit test results + uses: dorny/test-reporter@v3 + if: always() + with: + name: Unit Test Results + path: test-results/unit/*.trx + reporter: dotnet-trx From 6c17b219b06f781a280e9b73c1a14a242c1df67f Mon Sep 17 00:00:00 2001 From: Mostafa Atef Date: Mon, 27 Apr 2026 12:43:55 +0300 Subject: [PATCH 14/15] Add deploy workflow for building and pushing Docker image --- .github/workflows/deploy.yml | 49 ++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 .github/workflows/deploy.yml diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..c307f2f --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,49 @@ +name: Deploy + +on: + push: + branches: [main] + workflow_dispatch: + +env: + IMAGE_NAME: ${{ secrets.DOCKERHUB_USERNAME }}/socialmedia-webapi + +jobs: + build-and-push: + name: Build and Push Docker Image + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v4 + + - name: Log in to Docker Hub + uses: docker/login-action@v4 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Extract metadata (tags & labels) + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.IMAGE_NAME }} + tags: | + type=ref,event=branch + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=sha,prefix=sha-,format=short + + - name: Build and push + uses: docker/build-push-action@v7 + with: + context: . + file: Dockerfile.prod + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max From 1138213f8ff07e32c712c64c9f8d1efb97e989e1 Mon Sep 17 00:00:00 2001 From: Mostafa Atef Date: Tue, 28 Apr 2026 03:37:06 +0300 Subject: [PATCH 15/15] Update ChatHub with authorization and client interface, refactor Dockerfile paths and enhance README --- .github/workflows/deploy.yml | 2 +- .github/workflows/main-ci.yml | 2 +- README.md | 145 +++++++++++------- docker-compose/dev.yaml | 2 +- {Docker => docker}/Dockerfile.dev | 0 {Docker => docker}/Dockerfile.prod | 0 {Docker => docker}/Dockerfile.test | 0 .../CustomValidations/EnumValueAttribute.cs | 33 ---- .../Reacts/Requests/ReactToCommentRequest.cs | 2 - .../Reacts/Requests/ReactToPostRequest.cs | 2 - src/SocialMedia.WebApi/Hubs/ChatHub.cs | 9 +- src/SocialMedia.WebApi/Hubs/IChatClient.cs | 10 ++ 12 files changed, 105 insertions(+), 102 deletions(-) rename {Docker => docker}/Dockerfile.dev (100%) rename {Docker => docker}/Dockerfile.prod (100%) rename {Docker => docker}/Dockerfile.test (100%) delete mode 100644 src/SocialMedia.Application/CustomValidations/EnumValueAttribute.cs create mode 100644 src/SocialMedia.WebApi/Hubs/IChatClient.cs diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index c307f2f..5470bf2 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -41,7 +41,7 @@ jobs: uses: docker/build-push-action@v7 with: context: . - file: Dockerfile.prod + file: docker/Dockerfile.prod push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} diff --git a/.github/workflows/main-ci.yml b/.github/workflows/main-ci.yml index 7bb3b2c..2a6f4c9 100644 --- a/.github/workflows/main-ci.yml +++ b/.github/workflows/main-ci.yml @@ -70,7 +70,7 @@ jobs: uses: docker/build-push-action@v7 with: context: . - file: Docker/Dockerfile.test + file: docker/Dockerfile.test push: false load: true tags: socialmedia-test:${{ github.sha }} diff --git a/README.md b/README.md index e16786f..177b276 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # Social Media API -A comprehensive Social Media REST API built with ASP.NET Core following Clean Architecture principles. This API provides full social media functionality including user authentication, posts, comments, reactions, following system, and real-time communication. +A comprehensive Social Media REST API built with ASP.NET Core following Clean Architecture, CQRS, and MediatR, separating read and write models using table projections populated through domain events, the Outbox pattern, and background services. This API provides full social media functionality including user authentication, posts, comments, reactions, following system, and real-time communication. -## 🚀 Features +## Features ### Authentication & Authorization @@ -46,52 +46,91 @@ A comprehensive Social Media REST API built with ASP.NET Core following Clean Ar - Connection management for online/offline status - Message status tracking (sent, delivered, read) -## 🏗️ Architecture +## Architecture -The project follows Clean Architecture principles with the following layers: +The project follows Clean Architecture principles and uses physical CQRS with a clear separation between write models (EF Core) and read models (Dapper projections). Write operations are handled via commands, while read operations use optimized projection tables. Domain events, the Outbox pattern, and background services coordinate consistent side effects and populate read models. + +Project layout: ``` src/ ├── SocialMedia.WebApi/ # Presentation Layer │ ├── Controllers/ # API Controllers -│ ├── Hubs/ # SignalR Hubs for real-time communication -│ ├── Program.cs # Application entry point -│ └── appsettings.json # Configuration +│ ├── Hubs/ # SignalR Hubs for real-time communication +│ ├── Middlewares/ # Request pipeline and logging +│ ├── Filters/ # API filters +│ ├── Program.cs # Application entry point +│ └── appsettings.json # Configuration ├── SocialMedia.Application/ # Application Layer -│ ├── Services/ # Business logic services -│ ├── ServiceContracts/ # Service interfaces -│ ├── Dtos/ # Data Transfer Objects -│ └── CustomValidations/ # Custom validation attributes -├── SocialMedia.Core/ # Domain Layer -│ ├── Entities/ # Domain entities -│ ├── Enumerations/ # Domain enums -│ └── RepositoryContracts/ # Repository interfaces -└── SocialMedia.Infrastructure/ # Infrastructure Layer - ├── Database/ # Entity Framework DbContext - ├── Repositories/ # Repository implementations - ├── Auth/ # JWT & Password services - ├── Email/ # Email services - └── FileUploading/ # File upload services +│ ├── Abstractions/ # Cross-cutting contracts +│ ├── Auth/ # Auth commands, queries, and responses +│ ├── Comments/ # Comment commands, queries, and responses +│ ├── Posts/ # Post commands, queries, and responses +│ ├── Reacts/ # React commands, queries, and responses +│ ├── Users/ # User commands, queries, and responses +│ ├── Behaviors/ # MediatR pipeline behaviors +│ ├── Dtos/ # Cross-layer DTOs +│ └── Options/ # Options and configuration models +├── SocialMedia.Core/ # Domain Layer +│ ├── Entities/ # Domain entities +│ ├── Enumerations/ # Domain enums +│ ├── Events/ # Domain events +│ └── RepositoryContracts/ # Repository interfaces +└── SocialMedia.Infrastructure/ # Infrastructure Layer + ├── Database/ # Entity Framework DbContext + ├── Data/ # Dapper read models and projections + ├── Repositories/ # Repository implementations + ├── Auth/ # JWT & Password services + ├── Email/ # Email services + ├── FileUploading/ # File upload services + └── Outbox/ # Outbox processing + +tests/ +├── SocialMedia.Application.UnitTests/ +│ ├── Auth/ +│ ├── Posts/ +│ ├── Reacts/ +│ ├── Users/ +│ └── Comments/ +└── SocialMedia.IntegrationTests/ + ├── Auth/ + ├── Posts/ + ├── Reacts/ + ├── Users/ + └── Comments/ + +Docker/ +├── Dockerfile.dev +├── Dockerfile.prod +└── Dockerfile.test + +docker-compose/ +└── dev.yaml + ``` -## 🛠️ Technology Stack +## Technology Stack -- **Framework**: ASP.NET Core 8.0 +- **Framework**: ASP.NET Core 10.0 - **Database**: SQL Server with Entity Framework Core +- **Read Models**: Dapper - **Authentication**: JWT Bearer tokens - **Real-time Communication**: SignalR - **Email Service**: SMTP with Gmail - **File Storage**: Server storage + Supabase +- **Mediation & Validation**: MediatR + FluentValidation +- **Observability**: Serilog + Seq - **Architecture**: Clean Architecture -- **Patterns**: Repository Pattern, Unit of Work +- **Patterns**: CQRS, Repository Pattern, Outbox, Domain Events +- **Package Management**: Central Package Management (Directory.Packages.props) - **Containerization**: Docker & Docker Compose -## 📋 Prerequisites +## Prerequisites - Docker & Docker Compose - Git -## 🐳 Installation & Deployment +## Installation & Deployment The application is designed to run using Docker Compose, which provides a complete containerized environment with all necessary services. @@ -133,54 +172,44 @@ The application is designed to run using Docker Compose, which provides a comple "Url": "https://your-supabase-url.supabase.co", "Key": "your-supabase-anon-key" }, - "EmailVerificationTokenExpiryMinutes": 30, - "PasswordResetTokenExpiryMinutes": 30 + "durations": { + "EmailVerificationTokenExpiryMinutes": 30, + "PasswordResetTokenExpiryMinutes": 30 + }, + "FileUpload": { + "Provider": "Server" + } } ``` 3. **Run with Docker Compose** ```bash - docker-compose up -d + docker compose -f docker-compose/dev.yaml up -d ``` 4. **Verify Services** - - **API Service**: Available at `http://localhost:5001` - - **SQL Server**: Available at `localhost:1234` - - **Database**: Automatically created and migrated on startup + +- **API Service**: Available at `http://localhost:5039` +- **SQL Server**: Available at `localhost:1234` +- **Seq UI**: Available at `http://localhost:8081` +- **Database**: Automatically created and migrated on startup ### Docker Configuration Details -- **API Port**: 5001 (external) → 5000 (internal) +- **API Port**: 5039 (external) → 5000 (internal) - **Database Port**: 1234 (external) → 1433 (internal) +- **Seq UI Port**: 8081 (external) → 80 (internal) +- **Seq Ingestion Port**: 5341 (external) → 5341 (internal) - **Database Credentials**: SA user with password `YourStrong@Passw0rd` - **Persistent Volumes**: - Database data persisted in `db_data` volume - Uploaded files persisted in `api_wwwroot` volume -- **Environment**: Production environment by default - **Auto-migration**: Database migrations run automatically on container startup -## 📊 Database Schema - -### Core Entities - -- **User**: User profiles with authentication data -- **Post**: User posts with content and metadata -- **Comment**: Comments on posts with nested replies support -- **PostReact**: Reactions on posts -- **CommentReact**: Reactions on comments -- **FollowerFollowing**: Many-to-many relationship for user connections -- **Avatar**: User profile pictures -- **PostAttachment**: File attachments for posts -- **Message**: Real-time chat messages -- **MessageStatus**: Message delivery status tracking -- **Group**: Chat groups for real-time communication -- **UserConnection**: Active SignalR connections for users - -### Enumerations - -- **ReactType**: Like, Love, Laugh, Angry, Sad -- **AttachmentType**: Image, Video, Document -- **StorageProvider**: Server, Supabase -- **MessageStatusType**: Sent, Delivered, Read -- **GroupType**: Private, Group chat types +## Testing & CI + +- **Tests**: Unit and integration coverage across the codebase, with integration tests using real dependencies spun up via Testcontainers. +- **CI/CD**: GitHub Actions pipeline runs unit and integration tests and publishes results. +- **Deploy**: Automated Docker Hub image build and push after successful pipeline runs. +- **Containerization**: Docker + Docker Compose with environment-specific Dockerfiles for development and production. diff --git a/docker-compose/dev.yaml b/docker-compose/dev.yaml index 69affbc..f41b59b 100644 --- a/docker-compose/dev.yaml +++ b/docker-compose/dev.yaml @@ -2,7 +2,7 @@ services: social-api: build: context: ../ - dockerfile: Docker/Dockerfile.dev + dockerfile: docker/Dockerfile.dev container_name: SocialMedia.API ports: - 5039:5000 diff --git a/Docker/Dockerfile.dev b/docker/Dockerfile.dev similarity index 100% rename from Docker/Dockerfile.dev rename to docker/Dockerfile.dev diff --git a/Docker/Dockerfile.prod b/docker/Dockerfile.prod similarity index 100% rename from Docker/Dockerfile.prod rename to docker/Dockerfile.prod diff --git a/Docker/Dockerfile.test b/docker/Dockerfile.test similarity index 100% rename from Docker/Dockerfile.test rename to docker/Dockerfile.test diff --git a/src/SocialMedia.Application/CustomValidations/EnumValueAttribute.cs b/src/SocialMedia.Application/CustomValidations/EnumValueAttribute.cs deleted file mode 100644 index bfc7a79..0000000 --- a/src/SocialMedia.Application/CustomValidations/EnumValueAttribute.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.ComponentModel.DataAnnotations; - -namespace SocialMedia.Application.CustomValidations; - -public class EnumValueAttribute : ValidationAttribute -{ - - private Type _enumType; - private bool _isRequired; - public EnumValueAttribute(Type enumType, bool isRequired = false) - { - _enumType = enumType; - _isRequired = isRequired; - } - protected override ValidationResult? IsValid(object? value, ValidationContext validationContext) - { - if (value is not null) - { - if (Enum.IsDefined(_enumType, value)) - { - return ValidationResult.Success; - } - return new ValidationResult(ErrorMessage ?? $"Invlaid {validationContext.MemberName} field value"); - } - if (_isRequired) - { - return new ValidationResult(ErrorMessage ?? $"{validationContext.MemberName} field is required"); - } - return ValidationResult.Success; - } - -} diff --git a/src/SocialMedia.WebApi/Controllers/Reacts/Requests/ReactToCommentRequest.cs b/src/SocialMedia.WebApi/Controllers/Reacts/Requests/ReactToCommentRequest.cs index 91eb871..d535052 100644 --- a/src/SocialMedia.WebApi/Controllers/Reacts/Requests/ReactToCommentRequest.cs +++ b/src/SocialMedia.WebApi/Controllers/Reacts/Requests/ReactToCommentRequest.cs @@ -1,10 +1,8 @@ -using SocialMedia.Application.CustomValidations; using SocialMedia.Core.Enumerations; namespace SocialMedia.WebApi.Controllers.Reacts.Requests; public class ReactToCommentRequest { - [EnumValue(typeof(ReactType), true, ErrorMessage = "React type is required and valid values are from (1 - 5).")] public ReactType ReactType { get; set; } } diff --git a/src/SocialMedia.WebApi/Controllers/Reacts/Requests/ReactToPostRequest.cs b/src/SocialMedia.WebApi/Controllers/Reacts/Requests/ReactToPostRequest.cs index d592c95..078eb6a 100644 --- a/src/SocialMedia.WebApi/Controllers/Reacts/Requests/ReactToPostRequest.cs +++ b/src/SocialMedia.WebApi/Controllers/Reacts/Requests/ReactToPostRequest.cs @@ -1,10 +1,8 @@ -using SocialMedia.Application.CustomValidations; using SocialMedia.Core.Enumerations; namespace SocialMedia.WebApi.Controllers.Reacts.Requests; public class ReactToPostRequest { - [EnumValue(typeof(ReactType), true, ErrorMessage = "React type is required and valid values are from (1 - 5).")] public ReactType ReactType { get; set; } } \ No newline at end of file diff --git a/src/SocialMedia.WebApi/Hubs/ChatHub.cs b/src/SocialMedia.WebApi/Hubs/ChatHub.cs index fcb937d..5c68c30 100644 --- a/src/SocialMedia.WebApi/Hubs/ChatHub.cs +++ b/src/SocialMedia.WebApi/Hubs/ChatHub.cs @@ -10,7 +10,8 @@ namespace SocialMedia.WebApi.Hubs; [SignalRHub] -public class ChatHub : Hub +[Authorize] +public class ChatHub : Hub { private readonly IUnitOfWork _unitOfWork; private readonly IUserService _userService; @@ -39,7 +40,7 @@ public override async Task OnConnectedAsync() { await Groups.AddToGroupAsync(Context.ConnectionId, group.Id.ToString()); if (userFirstConnection) - await Clients.Group(group.Id.ToString()).SendAsync("DeliveredMessages", new DeliveredMessagesDto() + await Clients.Group(group.Id.ToString()).DeliveredMessages(new DeliveredMessagesDto() { GroudId = group.Id, RecieverId = user.Id @@ -99,7 +100,7 @@ public async Task SendFirstDirectMessage(SendFirstDirectMessageDto SendFirstDire public async Task ReadMessagesInGroup(ReadMessagesInGroupDto readMessagesInGroupDto) { await _unitOfWork.Users.UpdateDeliveredMessagesToSeen(readMessagesInGroupDto.RecieverId, readMessagesInGroupDto.GroupId); - await Clients.Group(readMessagesInGroupDto.GroupId.ToString()).SendAsync("SeenMessages", readMessagesInGroupDto); + await Clients.Group(readMessagesInGroupDto.GroupId.ToString()).SeenMessages(readMessagesInGroupDto); } private async Task _saveAndBroadcastMessage(Group group, UserDto tokenUser, string message) @@ -123,7 +124,7 @@ private async Task _saveAndBroadcastMessage(Group group, UserDto tokenUser, stri } group.Messages.Add(msg); await _unitOfWork.SaveChangesAsync(); - await Clients.Groups(group.Id.ToString()).SendAsync("NewMessage", new MessageDto + await Clients.Groups(group.Id.ToString()).NewMessage(new MessageDto { Id = msg.Id, GroupId = group.Id, diff --git a/src/SocialMedia.WebApi/Hubs/IChatClient.cs b/src/SocialMedia.WebApi/Hubs/IChatClient.cs new file mode 100644 index 0000000..0275082 --- /dev/null +++ b/src/SocialMedia.WebApi/Hubs/IChatClient.cs @@ -0,0 +1,10 @@ +using SocialMedia.Application.Dtos; + +namespace SocialMedia.WebApi.Hubs; + +public interface IChatClient +{ + Task DeliveredMessages(DeliveredMessagesDto deliveredMessagesDto); + Task SeenMessages(ReadMessagesInGroupDto readMessagesInGroupDto); + Task NewMessage(MessageDto messageDto); +}