Skip to content

Commit e71c7eb

Browse files
committed
Refactor Web Part 2
1 parent fc33177 commit e71c7eb

File tree

31 files changed

+239
-115
lines changed

31 files changed

+239
-115
lines changed

src/Directory.Build.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
<AnalysisLevel>latest</AnalysisLevel>
1414
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
1515
<NuGetAuditLevel>critical</NuGetAuditLevel>
16+
<Nullable>enable</Nullable>
1617
</PropertyGroup>
1718

1819
<PropertyGroup Condition="'$(Configuration)' == 'Release'">

src/LinkDotNet.Blog.Domain/Entity.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22

33
public abstract class Entity
44
{
5-
public string Id { get; set; } = string.Empty;
5+
public string Id { get; set; } = default!;
66
}

src/LinkDotNet.Blog.Infrastructure/Persistence/CachedRepository.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public CachedRepository(IRepository<T> repository, IMemoryCache memoryCache)
2222

2323
public ValueTask<HealthCheckResult> PerformHealthCheckAsync() => repository.PerformHealthCheckAsync();
2424

25-
public async ValueTask<T> GetByIdAsync(string id) =>
25+
public async ValueTask<T?> GetByIdAsync(string id) =>
2626
(await memoryCache.GetOrCreateAsync(id, async entry =>
2727
{
2828
entry.SlidingExpiration = TimeSpan.FromDays(7);

src/LinkDotNet.Blog.Infrastructure/Persistence/IRepository.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public interface IRepository<TEntity>
1212
{
1313
ValueTask<HealthCheckResult> PerformHealthCheckAsync();
1414

15-
ValueTask<TEntity> GetByIdAsync(string id);
15+
ValueTask<TEntity?> GetByIdAsync(string id);
1616

1717
ValueTask<IPagedList<TEntity>> GetAllAsync(
1818
Expression<Func<TEntity, bool>>? filter = null,

src/LinkDotNet.Blog.Infrastructure/Persistence/InMemory/Repository.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@ public sealed class Repository<TEntity> : IRepository<TEntity>
1515

1616
public ValueTask<HealthCheckResult> PerformHealthCheckAsync() => ValueTask.FromResult(HealthCheckResult.Healthy());
1717

18-
public ValueTask<TEntity> GetByIdAsync(string id)
18+
public ValueTask<TEntity?> GetByIdAsync(string id)
1919
{
20-
var entity = entities.First(b => b.Id == id);
21-
return new ValueTask<TEntity>(entity);
20+
var entity = entities.Find(b => b.Id == id);
21+
return new ValueTask<TEntity?>(entity);
2222
}
2323

2424
public ValueTask<IPagedList<TEntity>> GetAllAsync(Expression<Func<TEntity, bool>>? filter = null,

src/LinkDotNet.Blog.Infrastructure/Persistence/MongoDB/Repository.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public async ValueTask<HealthCheckResult> PerformHealthCheckAsync()
3636
}
3737
}
3838

39-
public async ValueTask<TEntity> GetByIdAsync(string id)
39+
public async ValueTask<TEntity?> GetByIdAsync(string id)
4040
{
4141
var filter = Builders<TEntity>.Filter.Eq(e => e.Id, id);
4242
var result = await Collection.FindAsync(filter);

src/LinkDotNet.Blog.Infrastructure/Persistence/RavenDb/Repository.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public async ValueTask<HealthCheckResult> PerformHealthCheckAsync()
3333
}
3434
}
3535

36-
public async ValueTask<TEntity> GetByIdAsync(string id)
36+
public async ValueTask<TEntity?> GetByIdAsync(string id)
3737
{
3838
using var session = documentStore.OpenAsyncSession();
3939
return await session.LoadAsync<TEntity>(id);

src/LinkDotNet.Blog.Infrastructure/Persistence/Sql/Repository.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,10 @@ public async ValueTask<HealthCheckResult> PerformHealthCheckAsync()
3636
}
3737
}
3838

39-
public async ValueTask<TEntity> GetByIdAsync(string id)
39+
public async ValueTask<TEntity?> GetByIdAsync(string id)
4040
{
4141
var blogDbContext = await dbContextFactory.CreateDbContextAsync();
42-
return await blogDbContext.Set<TEntity>().FirstAsync(b => b.Id == id);
42+
return await blogDbContext.Set<TEntity>().FirstOrDefaultAsync(b => b.Id == id);
4343
}
4444

4545
public ValueTask<IPagedList<TEntity>> GetAllAsync(Expression<Func<TEntity, bool>>? filter = null,

src/LinkDotNet.Blog.Web/Features/Archive/ArchivePage.razor

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
</div>
4545

4646
@code {
47-
private IReadOnlyCollection<IGrouping<int, BlogPostPerYear>> blogPostsPerYear = [];
47+
private IReadOnlyCollection<IGrouping<int, BlogPostPerYear>>? blogPostsPerYear;
4848
private int blogPostCount;
4949

5050
protected override async Task OnInitializedAsync()

src/LinkDotNet.Blog.Web/Features/ShowBlogPost/ShowBlogPostPage.razor

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,8 @@ else if (BlogPost is not null)
123123

124124
private async Task UpdateLikes(bool hasLiked)
125125
{
126-
BlogPost = await BlogPostRepository.GetByIdAsync(BlogPostId);
126+
BlogPost = await BlogPostRepository.GetByIdAsync(BlogPostId)
127+
?? throw new InvalidOperationException("Blog post not found");
127128
BlogPost.Likes = hasLiked ? BlogPost.Likes + 1 : BlogPost.Likes - 1;
128129
await BlogPostRepository.StoreAsync(BlogPost);
129130
}

tests/LinkDotNet.Blog.IntegrationTests/Web/Controller/RssFeedControllerTests.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,9 @@ public async Task ShouldCreateRssFeed()
2828
{
2929
HttpContext = httpContext,
3030
};
31-
var config = Options.Create(new ApplicationConfiguration
32-
{
33-
BlogName = "Test"
34-
});
31+
var config = Options.Create(new ApplicationConfigurationBuilder()
32+
.WithBlogName("Test")
33+
.Build());
3534

3635
var introduction = new IntroductionBuilder()
3736
.WithDescription("Description")

tests/LinkDotNet.Blog.IntegrationTests/Web/Features/Home/IndexTests.cs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -138,19 +138,19 @@ public async Task ShouldSetPageToFirstIfOutOfRange(int? page)
138138
cut.FindAll(".blog-card").Count.Should().Be(10);
139139
}
140140

141-
private static (ApplicationConfiguration ApplicationConfiguration, Introduction Introduction) CreateSampleAppConfiguration(string profilePictureUri = null)
141+
private static (ApplicationConfiguration ApplicationConfiguration, Introduction Introduction)
142+
CreateSampleAppConfiguration(string profilePictureUri = null)
142143
{
143-
return (new ApplicationConfiguration
144+
return (new ApplicationConfigurationBuilder()
145+
.WithBlogName(string.Empty)
146+
.WithBlogPostsPerPage(10)
147+
.Build(),
148+
new Introduction
144149
{
145-
BlogName = string.Empty,
146-
BlogPostsPerPage = 10,
147-
},
148-
new Introduction
149-
{
150-
Description = string.Empty,
151-
BackgroundUrl = string.Empty,
152-
ProfilePictureUrl = profilePictureUri ?? string.Empty,
153-
});
150+
Description = string.Empty,
151+
BackgroundUrl = string.Empty,
152+
ProfilePictureUrl = profilePictureUri ?? string.Empty,
153+
});
154154
}
155155

156156
private async Task CreatePublishedBlogPosts(int amount)

tests/LinkDotNet.Blog.IntegrationTests/Web/Features/ShowBlogPost/ShowBlogPostPageTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ private void RegisterComponents(BunitContext ctx, ILocalStorageService localStor
121121
ctx.Services.AddScoped(_ => localStorageService ?? Substitute.For<ILocalStorageService>());
122122
ctx.Services.AddScoped(_ => Substitute.For<IToastService>());
123123
ctx.Services.AddScoped(_ => Substitute.For<IUserRecordService>());
124-
ctx.Services.AddScoped(_ => Options.Create(new ApplicationConfiguration()));
124+
ctx.Services.AddScoped(_ => Options.Create(new ApplicationConfigurationBuilder().Build()));
125125
ctx.Services.AddScoped(_ => Substitute.For<IInstantJobRegistry>());
126126
}
127127
}

tests/LinkDotNet.Blog.IntegrationTests/Web/Features/SimilarBlogPostJobTests.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public async Task ShouldCalculateSimilarBlogPosts()
3030
await Repository.StoreAsync(blogPost1);
3131
await Repository.StoreAsync(blogPost2);
3232
await Repository.StoreAsync(blogPost3);
33-
var config = Options.Create(new ApplicationConfiguration { ShowSimilarPosts = true });
33+
var config = Options.Create(new ApplicationConfigurationBuilder().WithShowSimilarPosts(true).Build());
3434

3535
var job = new SimilarBlogPostJob(Repository, similarBlogPostRepository, config);
3636
var context = Substitute.For<IJobExecutionContext>();
@@ -50,7 +50,7 @@ public async Task ShouldNotCalculateWhenDisabledInApplicationConfiguration()
5050
await Repository.StoreAsync(blogPost1);
5151
await Repository.StoreAsync(blogPost2);
5252
await Repository.StoreAsync(blogPost3);
53-
var config = Options.Create(new ApplicationConfiguration { ShowSimilarPosts = false });
53+
var config = Options.Create(new ApplicationConfigurationBuilder().WithShowSimilarPosts(false).Build());
5454

5555
var job = new SimilarBlogPostJob(Repository, similarBlogPostRepository, config);
5656
var context = Substitute.For<IJobExecutionContext>();
@@ -70,7 +70,7 @@ public async Task ShouldNotCalculateWhenNotTriggeredAsInstantJob()
7070
await Repository.StoreAsync(blogPost1);
7171
await Repository.StoreAsync(blogPost2);
7272
await Repository.StoreAsync(blogPost3);
73-
var config = Options.Create(new ApplicationConfiguration { ShowSimilarPosts = true });
73+
var config = Options.Create(new ApplicationConfigurationBuilder().WithShowSimilarPosts(true).Build());
7474

7575
var job = new SimilarBlogPostJob(Repository, similarBlogPostRepository, config);
7676
await job.RunAsync(Substitute.For<IJobExecutionContext>(), CancellationToken.None);

tests/LinkDotNet.Blog.IntegrationTests/Web/RegistrationExtensions/RavenDbRegistrationExtensionsTests.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using LinkDotNet.Blog.Domain;
22
using LinkDotNet.Blog.Infrastructure.Persistence;
3+
using LinkDotNet.Blog.TestUtilities;
34
using LinkDotNet.Blog.Web;
45
using LinkDotNet.Blog.Web.RegistrationExtensions;
56
using Microsoft.Extensions.DependencyInjection;
@@ -13,11 +14,10 @@ public class RavenDbRegistrationExtensionsTests
1314
public void ShouldGetValidRepository()
1415
{
1516
var serviceCollection = new ServiceCollection();
16-
var appConfig = Options.Create(new ApplicationConfiguration
17-
{
18-
ConnectionString = "http://localhost",
19-
DatabaseName = "Blog",
20-
});
17+
var appConfig = Options.Create(new ApplicationConfigurationBuilder()
18+
.WithBlogName("Blog")
19+
.WithConnectionString("http://localhost")
20+
.Build());
2121
serviceCollection.AddScoped(_ => appConfig);
2222

2323
serviceCollection.UseRavenDbAsStorageProvider();

tests/LinkDotNet.Blog.IntegrationTests/Web/RegistrationExtensions/SqliteRegistrationExtensionsTests.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using LinkDotNet.Blog.Domain;
22
using LinkDotNet.Blog.Infrastructure.Persistence;
3+
using LinkDotNet.Blog.TestUtilities;
34
using LinkDotNet.Blog.Web;
45
using LinkDotNet.Blog.Web.RegistrationExtensions;
56
using Microsoft.Extensions.Options;
@@ -13,10 +14,9 @@ public class SqliteRegistrationExtensionsTests
1314
public void ShouldGetValidRepository()
1415
{
1516
var serviceCollection = new ServiceCollection();
16-
var appConfig = Options.Create(new ApplicationConfiguration()
17-
{
18-
ConnectionString = "Filename=:memory:",
19-
});
17+
var appConfig = Options.Create(new ApplicationConfigurationBuilder()
18+
.WithConnectionString("Filename=:memory:")
19+
.Build());
2020
serviceCollection.AddScoped(_ => appConfig);
2121
serviceCollection.AddLogging();
2222

tests/LinkDotNet.Blog.IntegrationTests/Web/RegistrationExtensions/StorageProviderExtensionsTests.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Linq;
33
using LinkDotNet.Blog.Domain;
44
using LinkDotNet.Blog.Infrastructure.Persistence;
5+
using LinkDotNet.Blog.TestUtilities;
56
using LinkDotNet.Blog.Web;
67
using LinkDotNet.Blog.Web.RegistrationExtensions;
78
using Microsoft.Extensions.Configuration;
@@ -48,7 +49,10 @@ public void ShouldHaveCacheRepositoryOnlyForBlogPosts()
4849
var collection = new ServiceCollection();
4950
var config = Substitute.For<IConfiguration>();
5051
config["PersistenceProvider"].Returns("Sqlite");
51-
collection.AddScoped(_ => Options.Create(new ApplicationConfiguration { ConnectionString = "Filename=:memory:" }));
52+
collection.AddScoped(_ => Options.Create(new ApplicationConfigurationBuilder()
53+
.WithConnectionString("Filename=:memory:")
54+
.Build()));
55+
5256
collection.AddLogging();
5357

5458
collection.AddStorageProvider(config);

tests/LinkDotNet.Blog.IntegrationTests/Web/Shared/NavMenuTests.cs

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public NavMenuTests()
2020
[Fact]
2121
public void ShouldNavigateToSearchPage()
2222
{
23-
Services.AddScoped(_ => Options.Create(new ApplicationConfiguration()));
23+
Services.AddScoped(_ => Options.Create(new ApplicationConfigurationBuilder().Build()));
2424
AddAuthorization();
2525
var navigationManager = Services.GetRequiredService<NavigationManager>();
2626
var cut = Render<NavMenu>();
@@ -34,10 +34,9 @@ public void ShouldNavigateToSearchPage()
3434
[Fact]
3535
public void ShouldDisplayAboutMePage()
3636
{
37-
var config = Options.Create(new ApplicationConfiguration
38-
{
39-
IsAboutMeEnabled = true
40-
});
37+
var config = Options.Create(new ApplicationConfigurationBuilder()
38+
.WithIsAboutMeEnabled(true)
39+
.Build());
4140
Services.AddScoped(_ => config);
4241
AddAuthorization();
4342

@@ -65,10 +64,9 @@ public void ShouldPassCorrectUriToComponent()
6564
[Fact]
6665
public void ShouldShowBrandImageIfAvailable()
6766
{
68-
var config = Options.Create(new ApplicationConfiguration
69-
{
70-
BlogBrandUrl = "http://localhost/img.png",
71-
});
67+
var config = Options.Create(new ApplicationConfigurationBuilder()
68+
.WithBlogBrandUrl("http://localhost/img.png")
69+
.Build());
7270
Services.AddScoped(_ => config);
7371

7472
var profileInfoConfig = Options.Create(new ProfileInformationBuilder().Build());
@@ -89,11 +87,10 @@ public void ShouldShowBrandImageIfAvailable()
8987
[InlineData("")]
9088
public void ShouldShowBlogNameWhenNotBrand(string brandUrl)
9189
{
92-
var config = Options.Create(new ApplicationConfiguration
93-
{
94-
BlogBrandUrl = brandUrl,
95-
BlogName = "Steven",
96-
});
90+
var config = Options.Create(new ApplicationConfigurationBuilder()
91+
.WithBlogBrandUrl(brandUrl)
92+
.WithBlogName("Steven")
93+
.Build());
9794
Services.AddScoped(_ => config);
9895

9996
var profileInfoConfig = Options.Create(new ProfileInformationBuilder().Build());
@@ -106,6 +103,6 @@ public void ShouldShowBlogNameWhenNotBrand(string brandUrl)
106103
var brandImage = cut.Find(".nav-brand");
107104
var image = brandImage as IHtmlAnchorElement;
108105
image.Should().NotBeNull();
109-
image.TextContent.Should().Be("Steven");
106+
image!.TextContent.Should().Be("Steven");
110107
}
111108
}

tests/LinkDotNet.Blog.TestUtilities/ApplicationConfigurationBuilder.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ public class ApplicationConfigurationBuilder
1717
private bool showReadingIndicator;
1818
private string? patreonName;
1919
private bool showSimilarPosts;
20+
private string? blogBrandUrl;
2021

2122
public ApplicationConfigurationBuilder WithBlogName(string blogName)
2223
{
@@ -96,6 +97,12 @@ public ApplicationConfigurationBuilder WithShowSimilarPosts(bool showSimilarPost
9697
return this;
9798
}
9899

100+
public ApplicationConfigurationBuilder WithBlogBrandUrl(string blogBrandUrl)
101+
{
102+
this.blogBrandUrl = blogBrandUrl;
103+
return this;
104+
}
105+
99106
public ApplicationConfiguration Build()
100107
{
101108
return new ApplicationConfiguration
@@ -113,6 +120,7 @@ public ApplicationConfiguration Build()
113120
ShowReadingIndicator = showReadingIndicator,
114121
PatreonName = patreonName,
115122
ShowSimilarPosts = showSimilarPosts,
123+
BlogBrandUrl = blogBrandUrl,
116124
};
117125
}
118126
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
using LinkDotNet.Blog.Web.Authentication.OpenIdConnect;
2+
3+
namespace LinkDotNet.Blog.TestUtilities;
4+
5+
public class AuthInformationBuilder
6+
{
7+
private string clientId = "clientId";
8+
private string clientSecret = "client";
9+
private string domain = "domain";
10+
private string provider = "provider";
11+
12+
public AuthInformationBuilder WithClientId(string clientId)
13+
{
14+
this.clientId = clientId;
15+
return this;
16+
}
17+
18+
public AuthInformationBuilder WithClientSecret(string clientSecret)
19+
{
20+
this.clientSecret = clientSecret;
21+
return this;
22+
}
23+
24+
public AuthInformationBuilder WithDomain(string domain)
25+
{
26+
this.domain = domain;
27+
return this;
28+
}
29+
30+
public AuthInformationBuilder WithProvider(string provider)
31+
{
32+
this.provider = provider;
33+
return this;
34+
}
35+
36+
public AuthInformation Build()
37+
{
38+
return new AuthInformation
39+
{
40+
ClientId = clientId,
41+
ClientSecret = clientSecret,
42+
Domain = domain,
43+
Provider = provider,
44+
};
45+
}
46+
}

0 commit comments

Comments
 (0)