From d5dd762d99516ee44667e0cc00f4e522d1e7c9c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BChler?= Date: Thu, 30 Nov 2023 14:40:49 +0100 Subject: [PATCH] feat: upgrade dependencies and .net framework --- .editorconfig | 5 + .github/workflows/codeql.yml | 2 +- .github/workflows/test.yml | 4 +- Directory.Build.props | 9 +- Dockerfile | 4 +- .../Controller/PostControllerTest.cs | 105 ++++++++---------- .../Controller/UserControllerTest.cs | 57 ++++------ MumbleApi.Test/MumbleApi.Test.csproj | 15 +-- MumbleApi.Test/Services/PostUpdatesTest.cs | 31 ++---- .../Application/HttpContextExtensions.cs | 4 +- .../ZitadelAuthorizeAttribute.cs | 4 +- MumbleApi/Controller/PostController.cs | 61 +++++----- MumbleApi/Controller/UserController.cs | 31 ++---- MumbleApi/Database/DataContext.cs | 7 +- MumbleApi/Database/UlidConverter.cs | 9 +- MumbleApi/Errors/ForbiddenException.cs | 14 ++- MumbleApi/Errors/PostInvalidException.cs | 14 ++- MumbleApi/Errors/PostIsAReplyException.cs | 14 ++- MumbleApi/Errors/PostNotFoundException.cs | 14 ++- MumbleApi/Errors/StorageException.cs | 9 +- MumbleApi/Errors/UserNotFoundException.cs | 14 ++- MumbleApi/MumbleApi.csproj | 23 ++-- MumbleApi/OpenApi/ServerSentEventFilter.cs | 3 + MumbleApi/Program.cs | 5 +- MumbleApi/Services/PostUpdates.cs | 8 +- MumbleApi/Services/Posts.cs | 41 +++---- MumbleApi/Services/Storage.cs | 12 +- MumbleApi/Services/Users.cs | 31 ++---- 28 files changed, 243 insertions(+), 307 deletions(-) diff --git a/.editorconfig b/.editorconfig index a914522..bc20077 100644 --- a/.editorconfig +++ b/.editorconfig @@ -22,6 +22,11 @@ end_of_line = crlf insert_final_newline = true #### Custom StyleCop Rules #### +# https://rules.sonarsource.com/csharp/RSPEC-6608/ +dotnet_diagnostic.S6608.severity = None + +# https://rules.sonarsource.com/csharp/RSPEC-3925/ +dotnet_diagnostic.S3925.severity = None # https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA0001.md dotnet_diagnostic.SA0001.severity = None diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index ee21b2e..1a6c7c6 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -28,7 +28,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 7.x + dotnet-version: 8.x - name: Build run: dotnet build --configuration Release diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6bb2656..b017eea 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -19,7 +19,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 7.x + dotnet-version: 8.x - name: Linting run: dotnet format --verify-no-changes @@ -42,7 +42,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 7.x + dotnet-version: 8.x - name: Tools run: dotnet tool restore diff --git a/Directory.Build.props b/Directory.Build.props index 4a227ac..faf2029 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -7,14 +7,15 @@ + Condition="$(MSBuildProjectExtension) == '.csproj'" /> + Condition="$(MSBuildProjectExtension) == '.csproj'" /> + diff --git a/Dockerfile b/Dockerfile index 3cdb953..c73f44a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ ### Build -FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build WORKDIR /app @@ -17,7 +17,7 @@ RUN dotnet publish \ MumbleApi ### Deploy -FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS final +FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS final ARG BUILD_VERSION=unknown ARG COMMIT_SHA=unknown diff --git a/MumbleApi.Test/Controller/PostControllerTest.cs b/MumbleApi.Test/Controller/PostControllerTest.cs index cef5602..5a87ff9 100644 --- a/MumbleApi.Test/Controller/PostControllerTest.cs +++ b/MumbleApi.Test/Controller/PostControllerTest.cs @@ -9,16 +9,9 @@ namespace MumbleApi.Test.Controller; -public class PostControllerTest : IClassFixture +public class PostControllerTest(WebAppFactory factory) : IClassFixture { - private readonly WebAppFactory _factory; - - public PostControllerTest(WebAppFactory factory) - { - _factory = factory; - } - - public static TheoryData> CreatePostData => + public static TheoryData> CreatePostData => new() { // Create a post with text only. @@ -29,7 +22,7 @@ public PostControllerTest(WebAppFactory factory) new StringContent("new post text"), "text" }, }, - result => { result.Text.Should().Be("new post text"); } + result => result.Text.Should().Be("new post text") }, // Create post with media only. @@ -536,7 +529,7 @@ public PostControllerTest(WebAppFactory factory) }, }; - public static TheoryData> ReplacePostData => + public static TheoryData> ReplacePostData => new() { // Create a post with text only. @@ -606,7 +599,7 @@ public PostControllerTest(WebAppFactory factory) [InlineData("DELETE", "/posts/id/likes")] public async Task ReturnsUnauthorizedOnProtectedRoute(string method, string uri) { - var client = _factory.CreateUnauthorizedClient(); + var client = factory.CreateUnauthorizedClient(); var result = await client.SendAsync(new HttpRequestMessage(new HttpMethod(method), uri)); result.StatusCode.Should().Be(HttpStatusCode.Unauthorized); } @@ -614,8 +607,8 @@ public async Task ReturnsUnauthorizedOnProtectedRoute(string method, string uri) [Fact] public async Task FetchEmptyPosts() { - await _factory.PrepareTestData(TestData.Empty); - var client = _factory.CreateClient(); + await factory.PrepareTestData(TestData.Empty); + var client = factory.CreateClient(); var result = await client.GetFromJsonAsync>("/posts"); if (result is null) @@ -638,8 +631,8 @@ public async Task FetchEmptyPosts() [InlineData("/posts/00000000000000000000000002/replies?offset=2&limit=1", true, false)] public async Task PaginateCorrectly(string url, bool nextIsNull, bool prevIsNull) { - await _factory.PrepareTestData(TestData.PostsWithoutLikes); - var client = _factory.CreateClient(); + await factory.PrepareTestData(TestData.PostsWithoutLikes); + var client = factory.CreateClient(); var result = await client.GetFromJsonAsync>(url); if (result is null) @@ -656,8 +649,8 @@ public async Task PaginateCorrectly(string url, bool nextIsNull, bool prevIsNull [InlineData("/posts/00000000000000000000000002/replies")] public async Task NoInfoAboutLikeWithoutAuth(string url) { - await _factory.PrepareTestData(TestData.PostsWithoutLikes); - var client = _factory.CreateUnauthorizedClient(); + await factory.PrepareTestData(TestData.PostsWithoutLikes); + var client = factory.CreateUnauthorizedClient(); var result = await client.GetFromJsonAsync>(url); if (result is null) @@ -673,8 +666,8 @@ public async Task NoInfoAboutLikeWithoutAuth(string url) [InlineData("/posts/00000000000000000000000002/replies")] public async Task ContainInfoAboutLikeWithAuth(string url) { - await _factory.PrepareTestData(TestData.PostsWithoutLikes); - var client = _factory.CreateClient(); + await factory.PrepareTestData(TestData.PostsWithoutLikes); + var client = factory.CreateClient(); var result = await client.GetFromJsonAsync>(url); if (result is null) @@ -689,12 +682,10 @@ public async Task ContainInfoAboutLikeWithAuth(string url) [MemberData(nameof(CreatePostData))] public async Task CreatePost(string url, MultipartFormDataContent content, Action verify) { - await _factory.PrepareTestData(TestData.PostsWithoutLikes); - var client = _factory.CreateClient(); - using var request = new HttpRequestMessage(HttpMethod.Post, url) - { - Content = content, - }; + await factory.PrepareTestData(TestData.PostsWithoutLikes); + var client = factory.CreateClient(); + using var request = new HttpRequestMessage(HttpMethod.Post, url); + request.Content = content; var response = await client.SendAsync(request); var result = await response.EnsureSuccessStatusCode().Content.ReadFromJsonAsync(); @@ -713,12 +704,10 @@ public async Task CreatePost(string url, MultipartFormDataContent content, Actio [MemberData(nameof(ErroneousResultData))] public async Task ErroneousResult(HttpMethod method, string url, HttpContent? content, HttpStatusCode result) { - await _factory.PrepareTestData(TestData.PostsWithoutLikes); - var client = _factory.CreateClient(); - using var request = new HttpRequestMessage(method, url) - { - Content = content, - }; + await factory.PrepareTestData(TestData.PostsWithoutLikes); + var client = factory.CreateClient(); + using var request = new HttpRequestMessage(method, url); + request.Content = content; var response = await client.SendAsync(request); response.StatusCode.Should().Be(result); @@ -727,8 +716,8 @@ public async Task ErroneousResult(HttpMethod method, string url, HttpContent? co [Fact] public async Task FetchPostById() { - await _factory.PrepareTestData(TestData.PostsWithoutLikes); - var client = _factory.CreateUnauthorizedClient(); + await factory.PrepareTestData(TestData.PostsWithoutLikes); + var client = factory.CreateUnauthorizedClient(); var result = await client.GetFromJsonAsync("/posts/00000000000000000000000001"); if (result is null) @@ -743,8 +732,8 @@ public async Task FetchPostById() [Fact] public async Task FetchPostByIdWithLikeInfo() { - await _factory.PrepareTestData(TestData.PostsWithoutLikes); - var client = _factory.CreateClient(); + await factory.PrepareTestData(TestData.PostsWithoutLikes); + var client = factory.CreateClient(); var result = await client.GetFromJsonAsync("/posts/00000000000000000000000001"); if (result is null) @@ -760,12 +749,10 @@ public async Task FetchPostByIdWithLikeInfo() [MemberData(nameof(ReplacePostData))] public async Task ReplacePost(MultipartFormDataContent content, Action verify) { - await _factory.PrepareTestData(TestData.PostsWithoutLikes); - var client = _factory.CreateClient(); - using var request = new HttpRequestMessage(HttpMethod.Put, "/posts/00000000000000000000000001") - { - Content = content, - }; + await factory.PrepareTestData(TestData.PostsWithoutLikes); + var client = factory.CreateClient(); + using var request = new HttpRequestMessage(HttpMethod.Put, "/posts/00000000000000000000000001"); + request.Content = content; var response = await client.SendAsync(request); var result = await response.EnsureSuccessStatusCode().Content.ReadFromJsonAsync(); @@ -782,12 +769,10 @@ public async Task ReplacePost(MultipartFormDataContent content, Action [Fact] public async Task PatchPostWithText() { - await _factory.PrepareTestData(TestData.PostsWithoutLikes); - var client = _factory.CreateClient(); - using var request = new HttpRequestMessage(HttpMethod.Patch, "/posts/00000000000000000000000001") - { - Content = new StringContent(@"{""text"": ""new post text""}", new MediaTypeHeaderValue("application/json")), - }; + await factory.PrepareTestData(TestData.PostsWithoutLikes); + var client = factory.CreateClient(); + using var request = new HttpRequestMessage(HttpMethod.Patch, "/posts/00000000000000000000000001"); + request.Content = new StringContent("""{"text": "new post text"}""", new MediaTypeHeaderValue("application/json")); var response = await client.SendAsync(request); response.StatusCode.Should().Be(HttpStatusCode.NoContent); @@ -799,8 +784,8 @@ public async Task PatchPostWithText() [Fact] public async Task DeleteAPost() { - await _factory.PrepareTestData(TestData.PostsWithoutLikes); - var client = _factory.CreateClient(); + await factory.PrepareTestData(TestData.PostsWithoutLikes); + var client = factory.CreateClient(); var response = await client.DeleteAsync("/posts/00000000000000000000000001"); response.StatusCode.Should().Be(HttpStatusCode.NoContent); @@ -811,8 +796,8 @@ public async Task DeleteAPost() [Fact] public async Task AttachMediaOnPost() { - await _factory.PrepareTestData(TestData.PostsWithoutLikes); - var client = _factory.CreateClient(); + await factory.PrepareTestData(TestData.PostsWithoutLikes); + var client = factory.CreateClient(); var response = await client.PutAsync("/posts/00000000000000000000000001/media", new MultipartFormDataContent { { @@ -835,8 +820,8 @@ public async Task AttachMediaOnPost() [Fact] public async Task ReplaceMediaOnPost() { - await _factory.PrepareTestData(TestData.PostsWithoutLikes); - var client = _factory.CreateClient(); + await factory.PrepareTestData(TestData.PostsWithoutLikes); + var client = factory.CreateClient(); var response = await client.PutAsync("/posts/00000000000000000000000002/media", new MultipartFormDataContent { { @@ -859,8 +844,8 @@ public async Task ReplaceMediaOnPost() [Fact] public async Task DeleteMediaFromPost() { - await _factory.PrepareTestData(TestData.PostsWithoutLikes); - var client = _factory.CreateClient(); + await factory.PrepareTestData(TestData.PostsWithoutLikes); + var client = factory.CreateClient(); var response = await client.DeleteAsync("/posts/00000000000000000000000003/media"); response.StatusCode.Should().Be(HttpStatusCode.NoContent); @@ -871,8 +856,8 @@ public async Task DeleteMediaFromPost() [Fact] public async Task LikePost() { - await _factory.PrepareTestData(TestData.PostsWithoutLikes); - var client = _factory.CreateClient(); + await factory.PrepareTestData(TestData.PostsWithoutLikes); + var client = factory.CreateClient(); // Perform the put twice to see if it's idempotent. var response = await client.PutAsync("/posts/00000000000000000000000001/likes", null); @@ -887,8 +872,8 @@ public async Task LikePost() [Fact] public async Task UnlikePost() { - await _factory.PrepareTestData(TestData.PostsWithLikes); - var client = _factory.CreateClient(); + await factory.PrepareTestData(TestData.PostsWithLikes); + var client = factory.CreateClient(); var result = await client.GetFromJsonAsync("/posts/00000000000000000000000001"); result?.LikedBySelf.Should().BeTrue(); diff --git a/MumbleApi.Test/Controller/UserControllerTest.cs b/MumbleApi.Test/Controller/UserControllerTest.cs index 3db1233..e83b4f4 100644 --- a/MumbleApi.Test/Controller/UserControllerTest.cs +++ b/MumbleApi.Test/Controller/UserControllerTest.cs @@ -7,15 +7,8 @@ namespace MumbleApi.Test.Controller; -public class UserControllerTest : IClassFixture +public class UserControllerTest(WebAppFactory factory) : IClassFixture { - private readonly WebAppFactory _factory; - - public UserControllerTest(WebAppFactory factory) - { - _factory = factory; - } - public static TheoryData ErroneousResultData => new() { @@ -111,7 +104,7 @@ public UserControllerTest(WebAppFactory factory) [InlineData("DELETE", "/users/id/followers")] public async Task ReturnsUnauthorizedOnProtectedRoute(string method, string uri) { - var client = _factory.CreateUnauthorizedClient(); + var client = factory.CreateUnauthorizedClient(); var result = await client.SendAsync(new HttpRequestMessage(new HttpMethod(method), uri)); result.StatusCode.Should().Be(HttpStatusCode.Unauthorized); } @@ -119,8 +112,8 @@ public async Task ReturnsUnauthorizedOnProtectedRoute(string method, string uri) [Fact] public async Task GetEmptyUsers() { - await _factory.PrepareTestData(TestData.Empty); - var client = _factory.CreateClient(); + await factory.PrepareTestData(TestData.Empty); + var client = factory.CreateClient(); var result = await client.GetFromJsonAsync>("/users"); if (result is null) @@ -143,8 +136,8 @@ public async Task GetCorrectInformationForAuthStatus(string url) { async Task Unauthed() { - await _factory.PrepareTestData(TestData.UsersWithFollowers); - var client = _factory.CreateUnauthorizedClient(); + await factory.PrepareTestData(TestData.UsersWithFollowers); + var client = factory.CreateUnauthorizedClient(); var result = await client.GetStringAsync(url); result.Should().NotContain("firstname"); @@ -152,8 +145,8 @@ async Task Unauthed() async Task Authed() { - await _factory.PrepareTestData(TestData.UsersWithFollowers); - var client = _factory.CreateClient(); + await factory.PrepareTestData(TestData.UsersWithFollowers); + var client = factory.CreateClient(); var result = await client.GetStringAsync(url); result.Should().Contain("firstname"); @@ -172,8 +165,8 @@ async Task Authed() [InlineData("/users/1337/followees?offset=0&limit=1", true, true)] public async Task PaginateCorrectly(string url, bool nextIsNull, bool prevIsNull) { - await _factory.PrepareTestData(TestData.UsersWithFollowers); - var client = _factory.CreateClient(); + await factory.PrepareTestData(TestData.UsersWithFollowers); + var client = factory.CreateClient(); var result = await client.GetFromJsonAsync>(url); if (result is null) @@ -188,8 +181,8 @@ public async Task PaginateCorrectly(string url, bool nextIsNull, bool prevIsNull [Fact] public async Task UploadAvatar() { - await _factory.PrepareTestData(TestData.UsersWithFollowers); - var client = _factory.CreateClient(); + await factory.PrepareTestData(TestData.UsersWithFollowers); + var client = factory.CreateClient(); var request = new HttpRequestMessage(HttpMethod.Put, "/users/avatar") { Content = new MultipartFormDataContent @@ -215,8 +208,8 @@ public async Task UploadAvatar() [Fact] public async Task DeleteAvatar() { - await _factory.PrepareTestData(TestData.UsersWithFollowers); - var client = _factory.CreateUserClient(TestData.UserTestyTester.Id); + await factory.PrepareTestData(TestData.UsersWithFollowers); + var client = factory.CreateUserClient(TestData.UserTestyTester.Id); var result = await client.DeleteAsync("/users/avatar"); result.StatusCode.Should().Be(HttpStatusCode.NoContent); @@ -227,8 +220,8 @@ public async Task DeleteAvatar() [Fact] public async Task UpdateProfile() { - await _factory.PrepareTestData(TestData.UsersWithFollowers); - var client = _factory.CreateClient(); + await factory.PrepareTestData(TestData.UsersWithFollowers); + var client = factory.CreateClient(); var request = new HttpRequestMessage(HttpMethod.Patch, "/users") { Content = JsonContent.Create(new @@ -251,8 +244,8 @@ public async Task UpdateProfile() [Fact] public async Task FollowUser() { - await _factory.PrepareTestData(TestData.UsersWithFollowers); - var client = _factory.CreateClient(); + await factory.PrepareTestData(TestData.UsersWithFollowers); + var client = factory.CreateClient(); var result = await client.PutAsync($"/users/{TestData.UserJackJohnson.Id}/followers", null); result.StatusCode.Should().Be(HttpStatusCode.NoContent); result = await client.PutAsync($"/users/{TestData.UserJackJohnson.Id}/followers", null); @@ -265,8 +258,8 @@ public async Task FollowUser() [Fact] public async Task UnfollowUser() { - await _factory.PrepareTestData(TestData.UsersWithFollowers); - var client = _factory.CreateClient(); + await factory.PrepareTestData(TestData.UsersWithFollowers); + var client = factory.CreateClient(); var result = await client.DeleteAsync($"/users/{TestData.UserTestyTester.Id}/followers"); result.StatusCode.Should().Be(HttpStatusCode.NoContent); result = await client.DeleteAsync($"/users/{TestData.UserTestyTester.Id}/followers"); @@ -280,12 +273,10 @@ public async Task UnfollowUser() [MemberData(nameof(ErroneousResultData))] public async Task ErroneousResult(HttpMethod method, string url, HttpContent? content, HttpStatusCode result) { - await _factory.PrepareTestData(TestData.UsersWithFollowers); - var client = _factory.CreateClient(); - using var request = new HttpRequestMessage(method, url) - { - Content = content, - }; + await factory.PrepareTestData(TestData.UsersWithFollowers); + var client = factory.CreateClient(); + using var request = new HttpRequestMessage(method, url); + request.Content = content; var response = await client.SendAsync(request); response.StatusCode.Should().Be(result); diff --git a/MumbleApi.Test/MumbleApi.Test.csproj b/MumbleApi.Test/MumbleApi.Test.csproj index 3e16c3b..3c3c58e 100644 --- a/MumbleApi.Test/MumbleApi.Test.csproj +++ b/MumbleApi.Test/MumbleApi.Test.csproj @@ -1,7 +1,8 @@ - net7.0 + net8.0 + 12 enable enable false @@ -9,15 +10,15 @@ - - - - - + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/MumbleApi.Test/Services/PostUpdatesTest.cs b/MumbleApi.Test/Services/PostUpdatesTest.cs index 1c69ce4..d63e5d4 100644 --- a/MumbleApi.Test/Services/PostUpdatesTest.cs +++ b/MumbleApi.Test/Services/PostUpdatesTest.cs @@ -5,7 +5,7 @@ namespace MumbleApi.Test.Services; -public class PostUpdatesTest : IClassFixture +public class PostUpdatesTest(WebAppFactory factory) : IClassFixture { private static readonly JsonSerializerOptions Json = new() { @@ -13,13 +13,6 @@ public class PostUpdatesTest : IClassFixture DictionaryKeyPolicy = JsonNamingPolicy.CamelCase, }; - private readonly WebAppFactory _factory; - - public PostUpdatesTest(WebAppFactory factory) - { - _factory = factory; - } - public static TheoryData?, bool>> ReceiveCorrectPostEventData => new() { @@ -101,15 +94,15 @@ public async Task ReceiveCorrectPostEvent( HttpContent? content, Func?, bool> verify) { - await _factory.PrepareTestData(TestData.PostsWithLikes); - var client = _factory.CreateClient(); + await factory.PrepareTestData(TestData.PostsWithLikes); + var client = factory.CreateClient(); await using var eventStream = await client.GetStreamAsync("/posts/_sse"); using var request = new HttpRequestMessage( method, - url) - { Content = content, }; + url); + request.Content = content; await client.SendAsync(request); var result = await GetServerEvent(eventStream); @@ -125,15 +118,15 @@ public async Task ReceiveCorrectPostLikeEvent( HttpContent? content, Func?, bool> verify) { - await _factory.PrepareTestData(TestData.PostsWithLikes); - var client = _factory.CreateClient(); + await factory.PrepareTestData(TestData.PostsWithLikes); + var client = factory.CreateClient(); await using var eventStream = await client.GetStreamAsync("/posts/_sse"); using var request = new HttpRequestMessage( method, - url) - { Content = content, }; + url); + request.Content = content; await client.SendAsync(request); var result = await GetServerEvent(eventStream); @@ -144,8 +137,8 @@ public async Task ReceiveCorrectPostLikeEvent( [Fact] public async Task ReceiveCorrectPostDeletedEvent() { - await _factory.PrepareTestData(TestData.PostsWithLikes); - var client = _factory.CreateClient(); + await factory.PrepareTestData(TestData.PostsWithLikes); + var client = factory.CreateClient(); await using var eventStream = await client.GetStreamAsync("/posts/_sse"); @@ -172,7 +165,7 @@ public async Task ReceiveCorrectPostDeletedEvent() var ev = new ServerEvent(); while (!sr.EndOfStream) { - var cts = new CancellationTokenSource(1000); + using var cts = new CancellationTokenSource(1000); switch (await sr.ReadLineAsync(cts.Token)) { case "": diff --git a/MumbleApi/Application/HttpContextExtensions.cs b/MumbleApi/Application/HttpContextExtensions.cs index 22237eb..34aaa62 100644 --- a/MumbleApi/Application/HttpContextExtensions.cs +++ b/MumbleApi/Application/HttpContextExtensions.cs @@ -1,5 +1,7 @@ using System.Security.Claims; +using MumbleApi.Errors; + using Zitadel.Authentication; namespace MumbleApi.Application; @@ -10,5 +12,5 @@ public static class HttpContextExtensions context.User.FindFirstValue(OidcClaimTypes.Subject); public static string UserId(this HttpContext context) => context.User.FindFirstValue(OidcClaimTypes.Subject) ?? - throw new Exception("No UserID Found."); + throw new UserNotFoundException("No UserID Found."); } diff --git a/MumbleApi/Authentication/ZitadelAuthorizeAttribute.cs b/MumbleApi/Authentication/ZitadelAuthorizeAttribute.cs index 01ad7e4..89f2fb4 100644 --- a/MumbleApi/Authentication/ZitadelAuthorizeAttribute.cs +++ b/MumbleApi/Authentication/ZitadelAuthorizeAttribute.cs @@ -4,6 +4,4 @@ namespace MumbleApi.Authentication; -public class ZitadelAuthorizeAttribute : AuthorizeAttribute -{ -} +public class ZitadelAuthorizeAttribute : AuthorizeAttribute; diff --git a/MumbleApi/Controller/PostController.cs b/MumbleApi/Controller/PostController.cs index d52868d..52c1fca 100644 --- a/MumbleApi/Controller/PostController.cs +++ b/MumbleApi/Controller/PostController.cs @@ -17,17 +17,8 @@ namespace MumbleApi.Controller; [Produces("application/json")] [SwaggerTag("Manage posts in the Mumble system.")] [OptionalZitadelAuthorize] -public class PostController : ControllerBase +public class PostController(IPosts posts, IPostUpdates updates) : ControllerBase { - private readonly IPosts _posts; - private readonly IPostUpdates _updates; - - public PostController(IPosts posts, IPostUpdates updates) - { - _posts = posts; - _updates = updates; - } - /// /// Fetch/Search a paginated list of posts. /// @@ -37,12 +28,12 @@ public PostController(IPosts posts, IPostUpdates updates) [SwaggerResponse(200, "Success", typeof(PaginatedResult))] public async Task Search([FromQuery] PostSearchParameters search) { - var (posts, total) = await _posts.GetPaginatedPostsWithLikes(search); + var (dbPosts, total) = await posts.GetPaginatedPostsWithLikes(search); return Ok(new PaginatedResult { Count = Convert.ToUInt32(total), - Data = posts.Select(Post.FromEntity(HttpContext.OptionalUserId())).ToList(), + Data = dbPosts.Select(Post.FromEntity(HttpContext.OptionalUserId())).ToList(), Next = total > search.Offset + search.Limit ? $"{Url.ActionLink()}{(search with { Offset = search.Offset + search.Limit }).ToQueryString()}" : null, @@ -80,19 +71,19 @@ public async Task Create([FromForm][SwaggerRequestBody(Required = if (data.Media is not null) { await using var file = data.Media.OpenReadStream(); - postEntity = await _posts.CreatePost( + postEntity = await posts.CreatePost( userId, text: data.Text, media: (file, data.Media.ContentType)); } else { - postEntity = await _posts.CreatePost( + postEntity = await posts.CreatePost( userId, text: data.Text); } - await _updates.NewPost(Post.FromEntity(postEntity)); + await updates.NewPost(Post.FromEntity(postEntity)); return Ok(Post.FromEntity(postEntity, userId)); } @@ -110,7 +101,7 @@ public async Task Create([FromForm][SwaggerRequestBody(Required = [SwaggerResponse(404, "Not Found")] public async Task GetById(Ulid id) { - var post = await _posts.GetPostById(id); + var post = await posts.GetPostById(id); if (post is null) { return NotFound(); @@ -151,7 +142,7 @@ [FromForm] [SwaggerRequestBody(Required = true)] if (data.Media is not null) { await using var file = data.Media.OpenReadStream(); - postEntity = await _posts.ReplacePost( + postEntity = await posts.ReplacePost( userId, id, data.Text, @@ -159,13 +150,13 @@ [FromForm] [SwaggerRequestBody(Required = true)] } else { - postEntity = await _posts.ReplacePost( + postEntity = await posts.ReplacePost( userId, id, data.Text); } - await _updates.PostUpdated(Post.FromEntity(postEntity)); + await updates.PostUpdated(Post.FromEntity(postEntity)); return Ok(Post.FromEntity(postEntity, userId)); } @@ -207,8 +198,8 @@ [FromBody] [SwaggerRequestBody(Required = true)] if (data.Text is not null) { var userId = HttpContext.UserId(); - var post = await _posts.UpdatePost(userId, id, data.Text); - await _updates.PostUpdated(Post.FromEntity(post)); + var post = await posts.UpdatePost(userId, id, data.Text); + await updates.PostUpdated(Post.FromEntity(post)); } } catch (PostNotFoundException) @@ -242,8 +233,8 @@ public async Task Delete(Ulid id) try { var userId = HttpContext.UserId(); - await _posts.DeletePost(userId, id); - await _updates.PostDeleted(id); + await posts.DeletePost(userId, id); + await updates.PostDeleted(id); } catch (PostNotFoundException) { @@ -284,8 +275,8 @@ [FromForm] [SwaggerRequestBody(Required = true)] { await using var file = uploadData.Media.OpenReadStream(); var userId = HttpContext.UserId(); - var post = await _posts.UpdatePostMedia(userId, id, (file, uploadData.Media.ContentType)); - await _updates.PostUpdated(Post.FromEntity(post)); + var post = await posts.UpdatePostMedia(userId, id, (file, uploadData.Media.ContentType)); + await updates.PostUpdated(Post.FromEntity(post)); return Ok(post.MediaUrl); } @@ -316,8 +307,8 @@ public async Task DeleteMedia(Ulid id) try { var userId = HttpContext.UserId(); - var post = await _posts.UpdatePostMedia(userId, id); - await _updates.PostUpdated(Post.FromEntity(post)); + var post = await posts.UpdatePostMedia(userId, id); + await updates.PostUpdated(Post.FromEntity(post)); } catch (PostInvalidException) { @@ -350,7 +341,7 @@ public async Task GetReplies(Ulid id, [FromQuery] PaginationParam { try { - var (replies, total) = await _posts.GetPaginatedReplies(id, pagination); + var (replies, total) = await posts.GetPaginatedReplies(id, pagination); return Ok(new PaginatedResult { Count = Convert.ToUInt32(total), @@ -404,7 +395,7 @@ [FromForm] [SwaggerRequestBody(Required = true)] if (data.Media is not null) { await using var file = data.Media.OpenReadStream(); - postEntity = await _posts.CreatePost( + postEntity = await posts.CreatePost( userId, id, data.Text, @@ -412,13 +403,13 @@ [FromForm] [SwaggerRequestBody(Required = true)] } else { - postEntity = await _posts.CreatePost( + postEntity = await posts.CreatePost( userId, id, data.Text); } - await _updates.NewPost(Reply.FromEntity(postEntity)); + await updates.NewPost(Reply.FromEntity(postEntity)); return Ok(Reply.FromEntity(postEntity, userId)); } @@ -452,9 +443,9 @@ public async Task Like(Ulid id) try { var userId = HttpContext.UserId(); - if (await _posts.LikePost(userId, id)) + if (await posts.LikePost(userId, id)) { - await _updates.PostLiked(userId, id); + await updates.PostLiked(userId, id); } return NoContent(); @@ -480,9 +471,9 @@ public async Task Unlike(Ulid id) try { var userId = HttpContext.UserId(); - if (await _posts.UnlikePost(userId, id)) + if (await posts.UnlikePost(userId, id)) { - await _updates.PostUnliked(userId, id); + await updates.PostUnliked(userId, id); } return NoContent(); diff --git a/MumbleApi/Controller/UserController.cs b/MumbleApi/Controller/UserController.cs index 363d85b..6df8242 100644 --- a/MumbleApi/Controller/UserController.cs +++ b/MumbleApi/Controller/UserController.cs @@ -15,15 +15,8 @@ namespace MumbleApi.Controller; [OptionalZitadelAuthorize] [Produces("application/json")] [SwaggerTag("Users in the Mumble System.")] -public class UserController : ControllerBase +public class UserController(IUsers users) : ControllerBase { - private readonly IUsers _users; - - public UserController(IUsers users) - { - _users = users; - } - /// /// Fetch a paginated list of users. /// @@ -34,7 +27,7 @@ public UserController(IUsers users) [SwaggerResponse(200, "Success", typeof(PaginatedResult))] public async Task Get([FromQuery] PaginationParameters pagination) { - var (users, total) = await _users.GetPaginatedUsers(pagination); + var (dbUsers, total) = await users.GetPaginatedUsers(pagination); var loggedIn = HttpContext.User.Identity?.IsAuthenticated == true; var next = total > pagination.Offset + pagination.Limit @@ -48,14 +41,14 @@ public async Task Get([FromQuery] PaginationParameters pagination ? Ok(new PaginatedResult { Count = Convert.ToUInt32(total), - Data = users.Select(Models.User.FromEntity).ToList(), + Data = dbUsers.Select(Models.User.FromEntity).ToList(), Next = next, Previous = prev, }) : Ok(new PaginatedResult { Count = Convert.ToUInt32(total), - Data = users.Select(PublicUser.FromEntity).ToList(), + Data = dbUsers.Select(PublicUser.FromEntity).ToList(), Next = next, Previous = prev, }); @@ -72,7 +65,7 @@ public async Task Get([FromQuery] PaginationParameters pagination [SwaggerResponse(404, "Not Found")] public async Task GetById(string id) { - var user = await _users.GetUserById(id); + var user = await users.GetUserById(id); if (user is null) { return NotFound(); @@ -114,7 +107,7 @@ public async Task UpdateProfile([FromBody] UpdateUserData data) try { - await _users.UpdateUser(HttpContext.UserId(), data.Firstname, data.Lastname, data.Username); + await users.UpdateUser(HttpContext.UserId(), data.Firstname, data.Lastname, data.Username); return NoContent(); } catch (UserNotFoundException) @@ -145,7 +138,7 @@ public async Task UploadAvatar([FromForm][SwaggerRequestBody(Requ } await using var file = data.Media.OpenReadStream(); - var newUrl = await _users.UpdateUserAvatar(HttpContext.UserId(), (file, data.Media.ContentType)); + var newUrl = await users.UpdateUserAvatar(HttpContext.UserId(), (file, data.Media.ContentType)); return Ok(newUrl); } @@ -160,7 +153,7 @@ public async Task UploadAvatar([FromForm][SwaggerRequestBody(Requ [SwaggerResponse(204, "Success - No Content")] public async Task DeleteAvatar() { - await _users.UpdateUserAvatar(HttpContext.UserId()); + await users.UpdateUserAvatar(HttpContext.UserId()); return NoContent(); } @@ -175,7 +168,7 @@ public async Task DeleteAvatar() [SwaggerResponse(200, "Success", typeof(PaginatedResult))] public async Task GetFollowers(string id, [FromQuery] PaginationParameters pagination) { - var (followers, total) = await _users.GetPaginatedFollowers(id, pagination); + var (followers, total) = await users.GetPaginatedFollowers(id, pagination); var loggedIn = User.Identity?.IsAuthenticated == true; var next = total > pagination.Offset + pagination.Limit @@ -213,7 +206,7 @@ public async Task GetFollowers(string id, [FromQuery] PaginationP [SwaggerResponse(200, "Success", typeof(PaginatedResult))] public async Task GetFollowees(string id, [FromQuery] PaginationParameters pagination) { - var (followees, total) = await _users.GetPaginatedFollowees(id, pagination); + var (followees, total) = await users.GetPaginatedFollowees(id, pagination); var loggedIn = User.Identity?.IsAuthenticated == true; var next = total > pagination.Offset + pagination.Limit @@ -254,7 +247,7 @@ public async Task FollowUser(string id) { try { - await _users.FollowUser(HttpContext.UserId(), id); + await users.FollowUser(HttpContext.UserId(), id); return NoContent(); } catch (UserNotFoundException) @@ -276,7 +269,7 @@ public async Task UnfollowUser(string id) { try { - await _users.UnfollowUser(HttpContext.UserId(), id); + await users.UnfollowUser(HttpContext.UserId(), id); return NoContent(); } catch (UserNotFoundException) diff --git a/MumbleApi/Database/DataContext.cs b/MumbleApi/Database/DataContext.cs index 48e7ae2..c707e38 100644 --- a/MumbleApi/Database/DataContext.cs +++ b/MumbleApi/Database/DataContext.cs @@ -4,13 +4,8 @@ namespace MumbleApi.Database; -public class DataContext : DbContext +public class DataContext(DbContextOptions options) : DbContext(options) { - public DataContext(DbContextOptions options) - : base(options) - { - } - #nullable disable public DbSet Posts { get; set; } diff --git a/MumbleApi/Database/UlidConverter.cs b/MumbleApi/Database/UlidConverter.cs index 4f649c6..1557b59 100644 --- a/MumbleApi/Database/UlidConverter.cs +++ b/MumbleApi/Database/UlidConverter.cs @@ -2,10 +2,5 @@ namespace MumbleApi.Database; -public class UlidConverter : ValueConverter -{ - public UlidConverter() - : base(ulid => ulid.ToString(), @string => Ulid.Parse(@string)) - { - } -} +public class UlidConverter() : ValueConverter(ulid => ulid.ToString() ?? string.Empty, + @string => Ulid.Parse(@string)); diff --git a/MumbleApi/Errors/ForbiddenException.cs b/MumbleApi/Errors/ForbiddenException.cs index 8ad5b1d..61e25a2 100644 --- a/MumbleApi/Errors/ForbiddenException.cs +++ b/MumbleApi/Errors/ForbiddenException.cs @@ -1,16 +1,18 @@ -using System.Runtime.Serialization; +namespace MumbleApi.Errors; -namespace MumbleApi.Errors; - -[Serializable] public class ForbiddenException : Exception { public ForbiddenException() { } - protected ForbiddenException(SerializationInfo info, StreamingContext context) - : base(info, context) + public ForbiddenException(string? message) + : base(message) + { + } + + public ForbiddenException(string? message, Exception? innerException) + : base(message, innerException) { } } diff --git a/MumbleApi/Errors/PostInvalidException.cs b/MumbleApi/Errors/PostInvalidException.cs index a69340c..3e57e2f 100644 --- a/MumbleApi/Errors/PostInvalidException.cs +++ b/MumbleApi/Errors/PostInvalidException.cs @@ -1,16 +1,18 @@ -using System.Runtime.Serialization; +namespace MumbleApi.Errors; -namespace MumbleApi.Errors; - -[Serializable] public class PostInvalidException : Exception { public PostInvalidException() { } - protected PostInvalidException(SerializationInfo info, StreamingContext context) - : base(info, context) + public PostInvalidException(string? message) + : base(message) + { + } + + public PostInvalidException(string? message, Exception? innerException) + : base(message, innerException) { } } diff --git a/MumbleApi/Errors/PostIsAReplyException.cs b/MumbleApi/Errors/PostIsAReplyException.cs index c05cf3c..8d4bf9f 100644 --- a/MumbleApi/Errors/PostIsAReplyException.cs +++ b/MumbleApi/Errors/PostIsAReplyException.cs @@ -1,16 +1,18 @@ -using System.Runtime.Serialization; +namespace MumbleApi.Errors; -namespace MumbleApi.Errors; - -[Serializable] public class PostIsAReplyException : Exception { public PostIsAReplyException() { } - protected PostIsAReplyException(SerializationInfo info, StreamingContext context) - : base(info, context) + public PostIsAReplyException(string? message) + : base(message) + { + } + + public PostIsAReplyException(string? message, Exception? innerException) + : base(message, innerException) { } } diff --git a/MumbleApi/Errors/PostNotFoundException.cs b/MumbleApi/Errors/PostNotFoundException.cs index c193ea2..ed83ea1 100644 --- a/MumbleApi/Errors/PostNotFoundException.cs +++ b/MumbleApi/Errors/PostNotFoundException.cs @@ -1,16 +1,18 @@ -using System.Runtime.Serialization; +namespace MumbleApi.Errors; -namespace MumbleApi.Errors; - -[Serializable] public class PostNotFoundException : Exception { public PostNotFoundException() { } - protected PostNotFoundException(SerializationInfo info, StreamingContext context) - : base(info, context) + public PostNotFoundException(string? message) + : base(message) + { + } + + public PostNotFoundException(string? message, Exception? innerException) + : base(message, innerException) { } } diff --git a/MumbleApi/Errors/StorageException.cs b/MumbleApi/Errors/StorageException.cs index 84e29d3..fa809eb 100644 --- a/MumbleApi/Errors/StorageException.cs +++ b/MumbleApi/Errors/StorageException.cs @@ -1,8 +1,5 @@ -using System.Runtime.Serialization; +namespace MumbleApi.Errors; -namespace MumbleApi.Errors; - -[Serializable] public class StorageException : Exception { public StorageException() @@ -14,8 +11,8 @@ public StorageException(string message) { } - protected StorageException(SerializationInfo info, StreamingContext context) - : base(info, context) + public StorageException(string? message, Exception? innerException) + : base(message, innerException) { } } diff --git a/MumbleApi/Errors/UserNotFoundException.cs b/MumbleApi/Errors/UserNotFoundException.cs index 763199e..5befdc6 100644 --- a/MumbleApi/Errors/UserNotFoundException.cs +++ b/MumbleApi/Errors/UserNotFoundException.cs @@ -1,16 +1,18 @@ -using System.Runtime.Serialization; +namespace MumbleApi.Errors; -namespace MumbleApi.Errors; - -[Serializable] public class UserNotFoundException : Exception { public UserNotFoundException() { } - protected UserNotFoundException(SerializationInfo info, StreamingContext context) - : base(info, context) + public UserNotFoundException(string? message) + : base(message) + { + } + + public UserNotFoundException(string? message, Exception? innerException) + : base(message, innerException) { } } diff --git a/MumbleApi/MumbleApi.csproj b/MumbleApi/MumbleApi.csproj index 14527f5..a6cf221 100644 --- a/MumbleApi/MumbleApi.csproj +++ b/MumbleApi/MumbleApi.csproj @@ -1,25 +1,26 @@ - net7.0 + net8.0 + 12 enable enable true true - + - - - - - - - + + + + + + + - - + + diff --git a/MumbleApi/OpenApi/ServerSentEventFilter.cs b/MumbleApi/OpenApi/ServerSentEventFilter.cs index e84c210..83f3279 100644 --- a/MumbleApi/OpenApi/ServerSentEventFilter.cs +++ b/MumbleApi/OpenApi/ServerSentEventFilter.cs @@ -268,6 +268,9 @@ private sealed class LikeInfo { public Ulid PostId { get; set; } + /// + /// The userid of the user that liked the post. + /// /// 179944860378202369 public string UserId { get; set; } = string.Empty; } diff --git a/MumbleApi/Program.cs b/MumbleApi/Program.cs index 1ac8a77..8eacf25 100644 --- a/MumbleApi/Program.cs +++ b/MumbleApi/Program.cs @@ -1,3 +1,4 @@ +using System.Configuration; using System.Net; using System.Reflection; using System.Security.Claims; @@ -21,6 +22,8 @@ using Zitadel.Authentication; using Zitadel.Extensions; +using IPNetwork = Microsoft.AspNetCore.HttpOverrides.IPNetwork; + var builder = WebApplication.CreateBuilder(args); var config = builder.Configuration @@ -28,7 +31,7 @@ .AddJsonFile("appsettings.Secrets.json", true) .Build() #endif - .Get() ?? throw new("Could not read config."); + .Get() ?? new(); builder.Services.AddSingleton(config); builder.Services.AddLogging( diff --git a/MumbleApi/Services/PostUpdates.cs b/MumbleApi/Services/PostUpdates.cs index b2e75a2..d8d3be9 100644 --- a/MumbleApi/Services/PostUpdates.cs +++ b/MumbleApi/Services/PostUpdates.cs @@ -8,7 +8,8 @@ namespace MumbleApi.Services; -internal class PostUpdates : ServerSentEventsService, IPostUpdates +internal class PostUpdates(IOptions> options) + : ServerSentEventsService(options), IPostUpdates { private static readonly JsonSerializerOptions Json = new() { @@ -16,11 +17,6 @@ internal class PostUpdates : ServerSentEventsService, IPostUpdates DictionaryKeyPolicy = JsonNamingPolicy.CamelCase, }; - public PostUpdates(IOptions> options) - : base(options) - { - } - public Task NewPost(PostBase post) => SendEventAsync(new ServerSentEvent { diff --git a/MumbleApi/Services/Posts.cs b/MumbleApi/Services/Posts.cs index ad63653..8535d38 100644 --- a/MumbleApi/Services/Posts.cs +++ b/MumbleApi/Services/Posts.cs @@ -10,21 +10,12 @@ namespace MumbleApi.Services; -internal class Posts : IPosts +internal class Posts(IDbContextFactory factory, IStorage storage) : IPosts { - private readonly IDbContextFactory _factory; - private readonly IStorage _storage; - - public Posts(IDbContextFactory factory, IStorage storage) - { - _factory = factory; - _storage = storage; - } - public async Task<(IEnumerable Posts, int TotalCount)> GetPaginatedPostsWithLikes( PostSearchParameters parameters) { - await using var db = await _factory.CreateDbContextAsync(); + await using var db = await factory.CreateDbContextAsync(); var query = db.Posts .Include(p => p.Replies) @@ -80,7 +71,7 @@ await query public async Task GetPostById(Ulid id) { - await using var db = await _factory.CreateDbContextAsync(); + await using var db = await factory.CreateDbContextAsync(); var query = db.Posts .Include(p => p.Replies) @@ -104,7 +95,7 @@ public async Task CreatePost( throw new PostInvalidException(); } - await using var db = await _factory.CreateDbContextAsync(); + await using var db = await factory.CreateDbContextAsync(); if (parentId is not null) { @@ -133,7 +124,7 @@ public async Task CreatePost( { post.MediaType = media.Value.MediaType; post.MediaUrl = - await _storage.UploadFile(Guid.NewGuid().ToString(), media.Value.MediaType, media.Value.File); + await storage.UploadFile(Guid.NewGuid().ToString(), media.Value.MediaType, media.Value.File); } await db.Posts.AddAsync(post); @@ -153,7 +144,7 @@ public async Task ReplacePost( throw new PostInvalidException(); } - await using var db = await _factory.CreateDbContextAsync(); + await using var db = await factory.CreateDbContextAsync(); var post = await db.Posts .Include(p => p.Replies) @@ -176,7 +167,7 @@ public async Task ReplacePost( post.Text = text; if (post.MediaId is not null) { - await _storage.DeleteFileIfPossible(post.MediaId); + await storage.DeleteFileIfPossible(post.MediaId); } post.MediaType = post.MediaUrl = null; @@ -185,7 +176,7 @@ public async Task ReplacePost( { post.MediaType = media.Value.MediaType; post.MediaUrl = - await _storage.UploadFile(Guid.NewGuid().ToString(), media.Value.MediaType, media.Value.File); + await storage.UploadFile(Guid.NewGuid().ToString(), media.Value.MediaType, media.Value.File); } await db.SaveChangesAsync(); @@ -194,7 +185,7 @@ public async Task ReplacePost( public async Task UpdatePost(string userId, Ulid postId, string text) { - await using var db = await _factory.CreateDbContextAsync(); + await using var db = await factory.CreateDbContextAsync(); var post = await db.Posts .Include(p => p.Replies) @@ -227,7 +218,7 @@ public async Task UpdatePost(string userId, Ulid postId, string text) public async Task UpdatePostMedia(string userId, Ulid postId, (Stream File, string MediaType)? media = null) { - await using var db = await _factory.CreateDbContextAsync(); + await using var db = await factory.CreateDbContextAsync(); var post = await db.Posts .Include(p => p.Replies) @@ -254,7 +245,7 @@ public async Task UpdatePostMedia(string userId, Ulid postId, (Stream File if (post.MediaId is not null) { - await _storage.DeleteFileIfPossible(post.MediaId); + await storage.DeleteFileIfPossible(post.MediaId); } post.MediaType = post.MediaUrl = null; @@ -262,7 +253,7 @@ public async Task UpdatePostMedia(string userId, Ulid postId, (Stream File { post.MediaType = media.Value.MediaType; post.MediaUrl = - await _storage.UploadFile(Guid.NewGuid().ToString(), media.Value.MediaType, media.Value.File); + await storage.UploadFile(Guid.NewGuid().ToString(), media.Value.MediaType, media.Value.File); } await db.SaveChangesAsync(); @@ -271,7 +262,7 @@ public async Task UpdatePostMedia(string userId, Ulid postId, (Stream File public async Task DeletePost(string userId, Ulid postId) { - await using var db = await _factory.CreateDbContextAsync(); + await using var db = await factory.CreateDbContextAsync(); var post = await db.Posts .Where(p => p.Id == postId) @@ -295,7 +286,7 @@ public async Task DeletePost(string userId, Ulid postId) public async Task LikePost(string userId, Ulid postId) { - await using var db = await _factory.CreateDbContextAsync(); + await using var db = await factory.CreateDbContextAsync(); if (await db.Posts.SingleOrDefaultAsync(p => p.Id == postId && p.Deleted == null) is null) { @@ -312,7 +303,7 @@ insert into likes(post_id, user_id) public async Task UnlikePost(string userId, Ulid postId) { - await using var db = await _factory.CreateDbContextAsync(); + await using var db = await factory.CreateDbContextAsync(); if (await db.Posts.SingleOrDefaultAsync(p => p.Id == postId && p.Deleted == null) is null) { @@ -330,7 +321,7 @@ delete from likes Ulid postId, PaginationParameters pagination) { - await using var db = await _factory.CreateDbContextAsync(); + await using var db = await factory.CreateDbContextAsync(); var parent = await db.Posts.SingleOrDefaultAsync(p => p.Id == postId); diff --git a/MumbleApi/Services/Storage.cs b/MumbleApi/Services/Storage.cs index 24bc888..5c26675 100644 --- a/MumbleApi/Services/Storage.cs +++ b/MumbleApi/Services/Storage.cs @@ -9,16 +9,10 @@ namespace MumbleApi.Services; -internal class Storage : IStorage +internal class Storage(AppConfig config) : IStorage { - private readonly string _bucketName; - private readonly StorageClient _gcs; - - public Storage(AppConfig config) - { - _bucketName = config.Storage.Bucket; - _gcs = StorageClient.Create(GoogleCredential.FromJson(config.Storage.ServiceAccountKey)); - } + private readonly string _bucketName = config.Storage.Bucket; + private readonly StorageClient _gcs = StorageClient.Create(GoogleCredential.FromJson(config.Storage.ServiceAccountKey)); public async Task UploadFile(string filename, string contentType, Stream file) { diff --git a/MumbleApi/Services/Users.cs b/MumbleApi/Services/Users.cs index c64b700..c176be7 100644 --- a/MumbleApi/Services/Users.cs +++ b/MumbleApi/Services/Users.cs @@ -10,20 +10,11 @@ namespace MumbleApi.Services; -internal class Users : IUsers +internal class Users(IDbContextFactory factory, IStorage storage) : IUsers { - private readonly IDbContextFactory _factory; - private readonly IStorage _storage; - - public Users(IDbContextFactory factory, IStorage storage) - { - _factory = factory; - _storage = storage; - } - public async Task<(IEnumerable Users, int TotalCount)> GetPaginatedUsers(PaginationParameters pagination) { - await using var db = await _factory.CreateDbContextAsync(); + await using var db = await factory.CreateDbContextAsync(); var query = db.Users .OrderBy(u => u.Id); @@ -39,13 +30,13 @@ public Users(IDbContextFactory factory, IStorage storage) public async Task GetUserById(string id) { - await using var db = await _factory.CreateDbContextAsync(); + await using var db = await factory.CreateDbContextAsync(); return await db.Users.SingleOrDefaultAsync(u => u.Id == id); } public async Task UpdateUser(string id, string? firstname, string? lastname, string? username) { - await using var db = await _factory.CreateDbContextAsync(); + await using var db = await factory.CreateDbContextAsync(); var user = await db.Users.SingleOrDefaultAsync(u => u.Id == id); if (user is null) @@ -62,12 +53,12 @@ public async Task UpdateUser(string id, string? firstname, string? lastname, str public async Task UpdateUserAvatar(string id, (Stream File, string MediaType)? avatar = null) { - await using var db = await _factory.CreateDbContextAsync(); + await using var db = await factory.CreateDbContextAsync(); var user = await db.Users.SingleAsync(u => u.Id == id); if (user.AvatarId is not null) { - await _storage.DeleteFileIfPossible(user.AvatarId); + await storage.DeleteFileIfPossible(user.AvatarId); } user.AvatarUrl = user.AvatarMediaType = null; @@ -75,7 +66,7 @@ public async Task UpdateUser(string id, string? firstname, string? lastname, str { user.AvatarMediaType = avatar.Value.MediaType; user.AvatarUrl = - await _storage.UploadFile(Guid.NewGuid().ToString(), avatar.Value.MediaType, avatar.Value.File); + await storage.UploadFile(Guid.NewGuid().ToString(), avatar.Value.MediaType, avatar.Value.File); } await db.SaveChangesAsync(); @@ -86,7 +77,7 @@ public async Task UpdateUser(string id, string? firstname, string? lastname, str string id, PaginationParameters pagination) { - await using var db = await _factory.CreateDbContextAsync(); + await using var db = await factory.CreateDbContextAsync(); var query = db.Follows .Where(f => f.FolloweeId == id) @@ -105,7 +96,7 @@ public async Task UpdateUser(string id, string? firstname, string? lastname, str string id, PaginationParameters pagination) { - await using var db = await _factory.CreateDbContextAsync(); + await using var db = await factory.CreateDbContextAsync(); var query = db.Follows .Where(f => f.FollowerId == id) @@ -122,7 +113,7 @@ public async Task UpdateUser(string id, string? firstname, string? lastname, str public async Task FollowUser(string userId, string followeeId) { - await using var db = await _factory.CreateDbContextAsync(); + await using var db = await factory.CreateDbContextAsync(); try { await db.Database.ExecuteSqlInterpolatedAsync($@" @@ -138,7 +129,7 @@ insert into follows(follower_id, followee_id) public async Task UnfollowUser(string userId, string followeeId) { - await using var db = await _factory.CreateDbContextAsync(); + await using var db = await factory.CreateDbContextAsync(); if (!await db.Users.AnyAsync(u => u.Id == followeeId)) {