Skip to content

Commit b4ab31a

Browse files
committed
Remove query parameter when recording clicked url
1 parent 6f99875 commit b4ab31a

File tree

3 files changed

+77
-14
lines changed

3 files changed

+77
-14
lines changed
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using System.Threading.Tasks;
4+
using Bunit;
5+
using FluentAssertions;
6+
using LinkDotNet.Blog.TestUtilities;
7+
using LinkDotNet.Blog.Web.Shared.Admin.Dashboard;
8+
using LinkDotNet.Domain;
9+
using LinkDotNet.Infrastructure.Persistence;
10+
using Microsoft.Extensions.DependencyInjection;
11+
using Xunit;
12+
13+
namespace LinkDotNet.Blog.IntegrationTests.Web.Pages.Admin.Dashboard
14+
{
15+
public class VisitCountPerPageTests : SqlDatabaseTestBase<BlogPost>
16+
{
17+
[Fact]
18+
public async Task ShouldShowCounts()
19+
{
20+
var blogPost = new BlogPostBuilder().WithTitle("I was clicked").Build();
21+
await Repository.StoreAsync(blogPost);
22+
using var ctx = new TestContext();
23+
ctx.Services.AddScoped<IRepository<BlogPost>>(_ => Repository);
24+
var visits = new List<KeyValuePair<string, int>>();
25+
visits.Add(new KeyValuePair<string, int>($"blogPost/{blogPost.Id}", 5));
26+
var pageVisitCounts = visits.OrderByDescending(s => s.Value);
27+
28+
var cut = ctx.RenderComponent<VisitCountPerPage>(p => p.Add(
29+
s => s.PageVisitCount, pageVisitCounts));
30+
31+
cut.WaitForState(() => cut.FindAll("td").Any());
32+
var elements = cut.FindAll("td").Select(t => t.InnerHtml).ToList();
33+
elements.Should().Contain("I was clicked");
34+
elements.Should().Contain("5");
35+
}
36+
}
37+
}

LinkDotNet.Blog.UnitTests/Web/Shared/UserRecordServiceTests.cs

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
using LinkDotNet.Blog.Web.Shared;
88
using LinkDotNet.Domain;
99
using LinkDotNet.Infrastructure.Persistence;
10-
using Microsoft.Extensions.Logging;
1110
using Moq;
1211
using Xunit;
1312

@@ -31,15 +30,14 @@ public UserRecordServiceTests()
3130
repositoryMock.Object,
3231
fakeNavigationManager,
3332
fakeAuthenticationStateProvider,
34-
localStorageService.Object,
35-
new Mock<ILogger>().Object);
33+
localStorageService.Object);
3634
}
3735

3836
[Fact]
3937
public async Task ShouldStoreInformation()
4038
{
41-
const string Url = "http://localhost/subpart";
42-
fakeNavigationManager.NavigateTo(Url);
39+
const string url = "http://localhost/subpart";
40+
fakeNavigationManager.NavigateTo(url);
4341
UserRecord recordToDb = null;
4442
repositoryMock.Setup(r => r.StoreAsync(It.IsAny<UserRecord>()))
4543
.Callback<UserRecord>(u => recordToDb = u);
@@ -90,5 +88,21 @@ public async Task ShouldNotThrowExceptionToOutsideWorld()
9088

9189
await act.Should().NotThrowAsync<Exception>();
9290
}
91+
92+
[InlineData("http://localhost/blogPost/12?q=3", "blogPost/12")]
93+
[InlineData("http://localhost/?q=3", "")]
94+
[Theory]
95+
public async Task ShouldRemoveQueryStringIfPresent(string url, string expectedRecord)
96+
{
97+
fakeNavigationManager.NavigateTo(url);
98+
UserRecord recordToDb = null;
99+
repositoryMock.Setup(r => r.StoreAsync(It.IsAny<UserRecord>()))
100+
.Callback<UserRecord>(u => recordToDb = u);
101+
102+
await sut.StoreUserRecordAsync();
103+
104+
recordToDb.Should().NotBeNull();
105+
recordToDb.UrlClicked.Should().Be(expectedRecord);
106+
}
93107
}
94108
}

LinkDotNet.Blog.Web/Shared/UserRecordService.cs

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
using System;
2+
using System.Diagnostics;
23
using System.Threading.Tasks;
34
using Blazored.LocalStorage;
45
using LinkDotNet.Domain;
56
using LinkDotNet.Infrastructure.Persistence;
67
using Microsoft.AspNetCore.Components;
78
using Microsoft.AspNetCore.Components.Authorization;
8-
using Microsoft.Extensions.Logging;
99

1010
namespace LinkDotNet.Blog.Web.Shared
1111
{
@@ -20,35 +20,32 @@ public class UserRecordService : IUserRecordService
2020
private readonly NavigationManager navigationManager;
2121
private readonly AuthenticationStateProvider authenticationStateProvider;
2222
private readonly ILocalStorageService localStorageService;
23-
private readonly ILogger logger;
2423

2524
public UserRecordService(
2625
IRepository<UserRecord> userRecordRepository,
2726
NavigationManager navigationManager,
2827
AuthenticationStateProvider authenticationStateProvider,
29-
ILocalStorageService localStorageService,
30-
ILogger logger)
28+
ILocalStorageService localStorageService)
3129
{
3230
this.userRecordRepository = userRecordRepository;
3331
this.navigationManager = navigationManager;
3432
this.authenticationStateProvider = authenticationStateProvider;
3533
this.localStorageService = localStorageService;
36-
this.logger = logger;
3734
}
3835

3936
public async Task StoreUserRecordAsync()
4037
{
4138
try
4239
{
43-
await GetAndStoreUSerRecordAsync();
40+
await GetAndStoreUserRecordAsync();
4441
}
4542
catch (Exception e)
4643
{
47-
logger.Log(LogLevel.Error, e, "Couldn't write user record");
44+
Trace.Write($"Exception: {e}");
4845
}
4946
}
5047

51-
private async Task GetAndStoreUSerRecordAsync()
48+
private async Task GetAndStoreUserRecordAsync()
5249
{
5350
var userIdentity = (await authenticationStateProvider.GetAuthenticationStateAsync()).User.Identity;
5451
if (userIdentity == null || userIdentity.IsAuthenticated)
@@ -58,11 +55,13 @@ private async Task GetAndStoreUSerRecordAsync()
5855

5956
var identifierHash = await GetIdentifierHashAsync();
6057

58+
var url = GetClickedUrl();
59+
6160
var record = new UserRecord
6261
{
6362
UserIdentifierHash = identifierHash,
6463
DateTimeUtcClicked = DateTime.UtcNow,
65-
UrlClicked = navigationManager.ToBaseRelativePath(navigationManager.Uri),
64+
UrlClicked = url,
6665
};
6766

6867
await userRecordRepository.StoreAsync(record);
@@ -81,5 +80,18 @@ private async Task<int> GetIdentifierHashAsync()
8180
await localStorageService.SetItemAsync("user", id);
8281
return id.GetHashCode();
8382
}
83+
84+
private string GetClickedUrl()
85+
{
86+
var basePath = navigationManager.ToBaseRelativePath(navigationManager.Uri);
87+
88+
if (string.IsNullOrEmpty(basePath))
89+
{
90+
return string.Empty;
91+
}
92+
93+
var queryIndex = basePath.IndexOf('?');
94+
return queryIndex <= 0 ? basePath[..(queryIndex - 1)] : basePath;
95+
}
8496
}
8597
}

0 commit comments

Comments
 (0)