Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

87 show personalized favorites #131

Merged
merged 62 commits into from
May 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
351f12a
Add UserContract table to database
JacobBredin May 7, 2022
71ad5b3
Add services and infrastructure
JacobBredin May 7, 2022
341f881
Add server side endpoints
JacobBredin May 7, 2022
2df1222
Add client endpoints
JacobBredin May 7, 2022
eb4c535
Fix broken tests
JacobBredin May 7, 2022
f0fd186
Remove IsFavorite from contract
JacobBredin May 7, 2022
44485ba
Fix documentation
pkasinski May 7, 2022
ec3928d
Remove unnecessary method from FavoritesController
pkasinski May 7, 2022
c2c24b0
Rename methods in FavoritesController
pkasinski May 7, 2022
c807fc1
Move unit tests to separate files
JacobBredin May 7, 2022
2c5f846
Merge branch '87-show-personalized-favorites-2' of https://github.com…
JacobBredin May 7, 2022
4e745bf
Incomplete tests for FavoritesController
JacobBredin May 7, 2022
dac7dfa
Add tests to FavoriteContractServiceTests
pkasinski May 7, 2022
fbc8178
Add FavoriteButton unit tests
JacobBredin May 8, 2022
87c7d3b
Merge branch '87-show-personalized-favorites-2' of https://github.com…
JacobBredin May 8, 2022
9e69123
Fix incorrect logger in EFFavoriteRepository
JacobBredin May 8, 2022
8b60139
Change a test in FavoriteContractServiceTests
pkasinski May 8, 2022
10bf183
Rename method in FavoritesController
pkasinski May 8, 2022
5dc8b76
Add tests to FavoritesControllerTests
pkasinski May 8, 2022
b185b5b
Add exception handling
JacobBredin May 8, 2022
c7aa5a7
Add unit tests
JacobBredin May 8, 2022
ca74421
Merge branch '87-show-personalized-favorites-2' of https://github.com…
JacobBredin May 8, 2022
bb75820
Add and fix unit tests
JacobBredin May 9, 2022
d4573e4
Add exception handling to FavoritesController
JacobBredin May 9, 2022
672125d
Add migration for removing IsFavorite in contract
JacobBredin May 9, 2022
7607af5
Merge master into branch
JacobBredin May 9, 2022
cb5b599
Refactor FavoritesService to be smarter
JacobBredin May 9, 2022
6c52986
Rename method
JacobBredin May 9, 2022
2e0ca4a
Fix tests
JacobBredin May 9, 2022
a4127d0
Fix tests
JacobBredin May 9, 2022
fa58bcc
merge master into branch
JacobBredin May 9, 2022
df710e1
Apply suggestions from code review
JacobBredin May 9, 2022
7c83eeb
Fix broken test
JacobBredin May 9, 2022
7db80af
Rename FetchAllFavorites to FetchAll
pkasinski May 9, 2022
07d84d8
Hide user collection in contract
JacobBredin May 9, 2022
79c42ec
Merge branch '87-show-personalized-favorites-2' of https://github.com…
JacobBredin May 9, 2022
74d2501
Add documentation to EF navigation property
JacobBredin May 10, 2022
0e9c90e
Rename database table
JacobBredin May 11, 2022
560944d
Move favorite methods to user service and repo
JacobBredin May 11, 2022
9ba4b44
Change the structure of endpoint URLs in FavoritesController
pkasinski May 11, 2022
3a534d4
Remove Favorite service and repo
JacobBredin May 11, 2022
ebf003f
Fix broken test for contract repo
JacobBredin May 11, 2022
6628e1b
Make adjustments in FavoritesController
pkasinski May 11, 2022
14b9cfd
Add tests to FavoritesControllerTests
pkasinski May 11, 2022
57af96f
Fix broken favorites tests
JacobBredin May 11, 2022
e1c7208
Move SetFavoriteContract.cs to Application/Users
pkasinski May 11, 2022
c9d7608
Fix broken tests
JacobBredin May 11, 2022
674e641
Fix merge conflicts
JacobBredin May 11, 2022
74e281f
Fix build error
JacobBredin May 11, 2022
e890421
Rename method in ContractCard
pkasinski May 11, 2022
c50a836
Remove null-forgiving operator
pkasinski May 11, 2022
f295b5c
Rename SetFavoriteContract to FavoriteContractDto
JacobBredin May 12, 2022
87fad1c
Fix favorite controller endpoint bug
JacobBredin May 12, 2022
ac9d14a
Add check to LoggedInUser
JacobBredin May 12, 2022
9ec7c15
Make adjustments in FavoriteButtonTests
pkasinski May 12, 2022
5aba061
Apply suggestions from code review
JacobBredin May 12, 2022
038569b
Merge branch 'master' into 87-show-personalized-favorites-2
JacobBredin May 12, 2022
2f706ff
Rewrite documentation in unit test
JacobBredin May 12, 2022
b62ca10
Inject ISessionService
pkasinski May 13, 2022
58d2c1e
Fix tests in FavoriteButtonTests and FavoriteCardsTests
pkasinski May 13, 2022
3c1ec80
Use the IsAuthenticated property
pkasinski May 13, 2022
137d604
Merge master into branch
JacobBredin May 14, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions src/Application/Contracts/ContractService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,6 @@ public IEnumerable<ContractPreviewDto> SearchUnauthorized(string query)
return ConvertToPreviews(results);
}

/// <inheritdoc />
public IEnumerable<Contract> FetchFavorites()
{
return _repo.Favorites;
}

/// <inheritdoc />
public void UpdateContract(Contract contract)
{
Expand Down
6 changes: 0 additions & 6 deletions src/Application/Contracts/IContractRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,6 @@ public interface IContractRepository
/// </summary>
IEnumerable<Contract> Recent { get; }

/// <summary>
/// Gets all contracts marked as favorites.
/// </summary>
/// <returns>The contract with a favorite mark.</returns>
IEnumerable<Contract> Favorites { get; }

/// <summary>
/// Adds a new contract to store.
/// </summary>
Expand Down
6 changes: 0 additions & 6 deletions src/Application/Contracts/IContractService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,6 @@ public interface IContractService
/// <returns>Whether the removal was successful.</returns>
bool Remove(Guid id);

/// <summary>
/// Gets contracts marked as favorites.
/// </summary>
/// <returns>All favorite marked contracts.</returns>
IEnumerable<Contract> FetchFavorites();

/// <summary>
/// Updates the contract in the repository.
/// </summary>
Expand Down
22 changes: 22 additions & 0 deletions src/Application/Users/FavoriteContractDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
namespace Application.Users;

/// <summary>
/// Used to send information about making a Contract a users favorite.
/// </summary>
public class FavoriteContractDto
{
/// <summary>
/// Gets the id of the user.
/// </summary>
public string UserName { get; init; } = string.Empty;

/// <summary>
/// Gets the id of the contract.
/// </summary>
public Guid ContractId { get; init; }

/// <summary>
/// Gets a value indicating whether gets if the Contract should be a favorite or not.
/// </summary>
public bool IsFavorite { get; init; }
}
15 changes: 15 additions & 0 deletions src/Application/Users/IUserRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,19 @@ public interface IUserRepository
/// Ensures that an admin user is created.
/// </summary>
void EnsureAdminCreated();

/// <summary>
/// Adds a favorite contract to store, for a certain user.
/// </summary>
/// <param name="userName">The name of the user.</param>
/// <param name="contractId">The id of the contract.</param>
void AddFavorite(string userName, Guid contractId);

/// <summary>
/// Removes a stored favorite contract, for a certain user.
/// </summary>
/// <param name="userName">The name of the user.</param>
/// <param name="contractId">The id of the contract.</param>
/// <returns>If the removal was successful.</returns>
bool RemoveFavorite(string userName, Guid contractId);
}
31 changes: 31 additions & 0 deletions src/Application/Users/IUserService.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Domain.Contracts;
using Domain.Users;

namespace Application.Users;
Expand Down Expand Up @@ -40,4 +41,34 @@ public interface IUserService
/// <param name="password">The password of the <see cref="User"/> to validate.</param>
/// <returns>A response containing the generated token.</returns>
AuthenticateResponse Authenticate(string username, string password);

/// <summary>
/// Gets all contracts marked as favorite by a certain user.
/// </summary>
/// <param name="userName">The name of the user.</param>
/// <returns>All contracts marked as favorite by the user.</returns>
IEnumerable<Contract> FetchAllFavorites(string userName);

/// <summary>
/// Checks if the contract is marked as favorite by the user.
/// </summary>
/// <param name="userName">The name of the user.</param>
/// <param name="contractId">The id of the contract.</param>
/// <returns>Whether the contract was marked as favorite.</returns>
bool IsFavorite(string userName, Guid contractId);

/// <summary>
/// Adds a favorite contract for a certain user.
/// </summary>
/// <param name="userName">The name of the user.</param>
/// <param name="contractId">The id of the contract.</param>
void AddFavorite(string userName, Guid contractId);

/// <summary>
/// Removes a favorite contract for a certain user.
/// </summary>
/// <param name="userName">The name of the user.</param>
/// <param name="contractId">The id of the contract.</param>
/// <returns>Whether the removal was successful.</returns>
bool RemoveFavorite(string userName, Guid contractId);
JacobBredin marked this conversation as resolved.
Show resolved Hide resolved
}
36 changes: 35 additions & 1 deletion src/Application/Users/UserService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

using Application.Configuration;
using Application.Exceptions;

using Domain.Contracts;
using Domain.Users;

using Microsoft.Extensions.Configuration;
Expand Down Expand Up @@ -71,6 +71,30 @@ public IEnumerable<User> FetchAllUsers()
return _repo.All;
}

/// <inheritdoc />
public IEnumerable<Contract> FetchAllFavorites(string userName)
{
return FetchUser(userName).Favorites;
}

/// <inheritdoc />
public bool IsFavorite(string userName, Guid contractId)
{
return FetchUser(userName).Favorites.Any(c => c.Id == contractId);
}

/// <inheritdoc />
public void AddFavorite(string userName, Guid contractId)
{
_repo.AddFavorite(userName, contractId);
}

/// <inheritdoc />
public bool RemoveFavorite(string userName, Guid contractId)
{
return _repo.RemoveFavorite(userName, contractId);
}

private static IEnumerable<Claim> CreateClaims(User user)
{
var claims = new List<Claim> { new("id", user.Id.ToString()), };
Expand Down Expand Up @@ -102,4 +126,14 @@ private string GenerateJwtToken(User user)
SecurityToken? token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
}

private User FetchUser(string username)
{
User? user = _repo.Fetch(username);

if (user == null)
throw new UserDoesNotExistException(username);

return user;
}
}
9 changes: 4 additions & 5 deletions src/Client/Pages/Contracts/ContractCard.razor
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
<h4 class="card-title text-truncate m-0" id="contract-name">@Contract.Name</h4>
</div>
<div class="d-flex justify-content-end width-10">
<FavoriteButton @ref="_favoriteButton" Contract="@Contract" OnFavoriteChange="@FavoriteChange"></FavoriteButton>
<FavoriteButton @ref="_favoriteButton" Contract="@Contract" OnFavoriteChange="@((args)=> ChangeFavorite(((Guid, bool))args))"></FavoriteButton>
</div>
<div class="pe-3 pointer" data-bs-toggle="modal" data-bs-target="#@("id_" + Contract.Id)" @onclick="AddToRecentlyViewed"></div>
</div>
Expand Down Expand Up @@ -50,7 +50,7 @@
/// Called when a contract has its favorite status changed.
/// </summary>
[Parameter]
public EventCallback<Contract> OnFavoriteChange { get; set; } = EventCallback<Contract>.Empty;
public EventCallback<(Guid, bool)> OnFavoriteChange { get; set; } = EventCallback<(Guid, bool)>.Empty;

private async Task AddToRecentlyViewed()
{
Expand All @@ -61,10 +61,9 @@
}
}

private async Task FavoriteChange(Contract contract)
private async Task ChangeFavorite((Guid Id, bool IsFavorite)args)
{
Contract = contract;
await OnFavoriteChange.InvokeAsync(contract);
await OnFavoriteChange.InvokeAsync(args);
}

}
48 changes: 34 additions & 14 deletions src/Client/Pages/Contracts/FavoriteButton.razor
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
@using Newtonsoft.Json
@using Domain.Contracts
@using System.Text
@using Application.Users
@inject HttpClient _http
@inject ISessionService _session

<button class="btn p-0 white-text" @onclick="ChangeFavorite">
@if (Contract.IsFavorite)
@if (isFavorite)
{
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" class="bi bi-heart-fill" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M8 1.314C12.438-3.248 23.534 4.735 8 15-7.534 4.736 3.562-3.248 8 1.314z"/>
Expand All @@ -31,23 +33,41 @@
/// Called when a contract has its favorite status changed.
/// </summary>
[Parameter]
public EventCallback<Contract> OnFavoriteChange { get; set; } = EventCallback<Contract>.Empty;
public EventCallback<(Guid, bool)> OnFavoriteChange { get; set; } = EventCallback<(Guid, bool)>.Empty;

private async Task ChangeFavorite()
{
var patchDocument = new JsonPatchDocument<Contract>();
patchDocument.Replace(contract => contract.IsFavorite, !Contract.IsFavorite);
var serializedDocument = JsonConvert.SerializeObject(patchDocument);
var content = new StringContent(serializedDocument, Encoding.UTF8, "application/json-patch+json");
/// <summary>
/// Whether the contract is marked as favorite.
/// </summary>
private bool isFavorite { get; set; }

var response = await _http.PatchAsync($"/api/v1/Contracts/{Contract.Id}", content);
if (response.IsSuccessStatusCode)
protected override async void OnInitialized()
{
if (_session.IsAuthenticated)
{
HttpResponseMessage response = await _http.GetAsync($"/api/v1/users/{_session.Username}/favorites/{Contract.Id}");
isFavorite = response.IsSuccessStatusCode;
}
else
{
var contract = await response.Content.ReadFromJsonAsync<Contract>();
if (contract != null) Contract = contract;
await InvokeAsync(StateHasChanged);
await OnFavoriteChange.InvokeAsync(Contract);
isFavorite = false;
}
await InvokeAsync(StateHasChanged);
base.OnInitialized();
}

private async Task ChangeFavorite()
{
if (!string.IsNullOrEmpty(_session.Username))
pkasinski marked this conversation as resolved.
Show resolved Hide resolved
{
FavoriteContractDto favoriteContract = new() { UserName = _session.Username, ContractId = Contract.Id, IsFavorite = !isFavorite };

HttpResponseMessage response = await _http.PostAsJsonAsync($"/api/v1/users/{_session.Username}/favorites", favoriteContract);
if (response.IsSuccessStatusCode)
{
isFavorite = !isFavorite;
await InvokeAsync(StateHasChanged);
await OnFavoriteChange.InvokeAsync((Contract.Id, isFavorite));
}
}
}
}
11 changes: 6 additions & 5 deletions src/Client/Pages/Dashboard/FavoriteCards.razor
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
@using Domain.Contracts
@using Newtonsoft.Json
@inject HttpClient _http
@inject ISessionService _session

<div id="favorite-contracts" style="padding-top: 30px;">
<h3>Favoriter</h3>
<FetchData @ref="_dataFetcher"
TData="List<Contract>"
Url="/api/v1/Contracts/favorites"
Url=@($"/api/v1/users/{_session.Username}/favorites")
Context="contracts">
@{
var contractsList = contracts.ToList();
Expand All @@ -16,7 +17,7 @@
<div id="favorite-cards-container" class="d-flex flex-row justify-content-around flex-wrap">
@foreach (var contract in contractsList)
{
<ContractCard Contract=@contract OnFavoriteChange="OnFavoriteChange"/>
<ContractCard Contract=@contract OnFavoriteChange="@((args)=> OnFavoriteChange(((Guid, bool))args))"/>
}
</div>
}
Expand All @@ -34,10 +35,10 @@
@code {
private FetchData<List<Contract>> _dataFetcher = null!;

private async Task OnFavoriteChange(Contract contract)
private async Task OnFavoriteChange((Guid Id, bool IsFavorite)args)
{
if (contract.IsFavorite) return;
_dataFetcher.Data?.RemoveAll(other => other.Id == contract.Id);
if (args.IsFavorite) return;
_dataFetcher.Data?.RemoveAll(other => other.Id == args.Id);
await InvokeAsync(StateHasChanged);
}

Expand Down
15 changes: 9 additions & 6 deletions src/Domain/Contracts/Contract.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace Domain.Contracts;
using Domain.Users;

namespace Domain.Contracts;

/// <summary>
/// A document containing negotiated discounts and benefits.
Expand All @@ -10,11 +12,6 @@ public class Contract
/// </summary>
public Guid Id { get; init; } = Guid.NewGuid();

/// <summary>
/// Gets or sets a value indicating whether the contract is a favorite or not.
/// </summary>
public bool IsFavorite { get; set; }

/// <summary>
/// Gets or sets the name of the contract.
/// </summary>
Expand Down Expand Up @@ -69,4 +66,10 @@ public class Contract
/// Gets or sets the contact information for the supplier.
/// </summary>
public string SupplierContactInfo { get; set; } = "Kontaktinformation till leverantör saknas.";

// Entity Framework requires that a navigation property exists in both
// classes (User and Contract) when a many-to-many relation is to be genereated,
// and exists only for this reason.
// See the Entity Framework documentation for more information.
private ICollection<User> FavoritedBy { get; } = new List<User>();
}
7 changes: 7 additions & 0 deletions src/Domain/Users/User.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using Domain.Contracts;

namespace Domain.Users;

/// <summary>
Expand Down Expand Up @@ -34,4 +36,9 @@ public class User
/// Gets or sets the date of the latest payment by the user. It is set to 1/1/0001 12:00:00 AM by default.
/// </summary>
public DateTime LatestPaymentDate { get; set; } = DateTime.MinValue;

/// <summary>
/// Gets favorite contracts.
/// </summary>
public ICollection<Contract> Favorites { get; init; } = new List<Contract>();
}
3 changes: 0 additions & 3 deletions src/Infrastructure/Databases/EFContractRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,6 @@ public EFContractRepository(EFDatabaseContext context, ILogger<EFContractReposit
/// <inheritdoc />
public IEnumerable<Contract> Recent => _recent.FetchRecentContracts();

/// <inheritdoc />
public IEnumerable<Contract> Favorites => Contracts.Where(contract => contract.IsFavorite);

private DbSet<Contract> Contracts => _context.Contracts;

/// <inheritdoc />
Expand Down
6 changes: 4 additions & 2 deletions src/Infrastructure/Databases/EFDatabaseContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ public EFDatabaseContext(
/// <inheritdoc />
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
_ = modelBuilder.Entity<User>()
.HasKey(user => user.Id);
_ = modelBuilder
.Entity<User>()
.HasMany(p => p.Favorites)
.WithMany("FavoritedBy");
}
}
Loading