Skip to content

Commit

Permalink
Merge pull request #44 from TomasSirotek/feature/course-like
Browse files Browse the repository at this point in the history
🛠 Feature: As a user I need to be able to like/save courses and displ…
  • Loading branch information
TomasSirotek authored Dec 4, 2023
2 parents 146b0dd + b86b977 commit b66e907
Show file tree
Hide file tree
Showing 37 changed files with 1,784 additions and 67 deletions.
1 change: 1 addition & 0 deletions src/Application/Common/Interfaces/IApplicationDbContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public interface IApplicationDbContext
DbSet<Chapter> Chapters { get; }

DbSet<UserCourse> UsersCourses { get; }
DbSet<WishListItem> WishlistItems { get; }

Task<int> SaveChangesAsync(CancellationToken cancellationToken);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using SkillSphere.Application.Common.Interfaces;
using SkillSphere.Domain.Entities;

namespace SkillSphere.Application.Features.Courses.Commands.AddCourseUserWL;

public record AddCourseUserWLCommand(Guid UserId, Guid CourseId) : IRequest;

public class AddCourseUserWLCommandHandler : IRequestHandler<AddCourseUserWLCommand>
{
private readonly IApplicationDbContext _context;

public AddCourseUserWLCommandHandler(IApplicationDbContext context)
{
_context = context;
}

public async Task Handle(AddCourseUserWLCommand request, CancellationToken cancellationToken)
{
var newWishlistItem = new WishListItem { UserId = request.UserId, CourseId = request.CourseId, };

_context.WishlistItems.Add(newWishlistItem);
await _context.SaveChangesAsync(cancellationToken);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace SkillSphere.Application.Features.Courses.Commands.AddCourseUserWL;

public class CourseWishListCommand : IRequest
{
public Guid CourseId { get; init; }
public Guid UserId { get; init; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using SkillSphere.Application.Common.Interfaces;

namespace SkillSphere.Application.Features.Courses.Commands.DecreaseLike;

public record DecreaseLikeCountCommand(Guid CourseId) : IRequest;

public class DecreaseLikeCountCommandHandler : IRequestHandler<DecreaseLikeCountCommand>
{
private readonly IApplicationDbContext _context;

public DecreaseLikeCountCommandHandler(IApplicationDbContext context)
{
_context = context;
}

public async Task Handle(DecreaseLikeCountCommand request, CancellationToken cancellationToken)
{
var course = await _context.Courses.FindAsync(request.CourseId);

Guard.Against.NotFound(request.CourseId, course);

if (course.Likes > 0)
{
course.Likes--;
await _context.SaveChangesAsync(cancellationToken);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using SkillSphere.Application.Common.Interfaces;

namespace SkillSphere.Application.Features.Courses.Commands.IncreaseLikes;

public record IncreaseLikesCommand(Guid CourseId) : IRequest;

public class IncreaseLikesCommandHandler : IRequestHandler<IncreaseLikesCommand>
{
private readonly IApplicationDbContext _context;

public IncreaseLikesCommandHandler(IApplicationDbContext context)
{
_context = context;
}

public async Task Handle(IncreaseLikesCommand request, CancellationToken cancellationToken)
{
var course = await _context.Courses.FindAsync(request.CourseId);

Guard.Against.NotFound(request.CourseId, course);

course.Likes++;
await _context.SaveChangesAsync(cancellationToken);
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using SkillSphere.Application.Common.Interfaces;
using SkillSphere.Application.TodoLists.Commands.DeleteTodoList;

namespace SkillSphere.Application.Features.Courses.Commands.RemoveCourseWL;

public record RemoveCourseWLCommand(Guid CourseId,Guid UserId) : IRequest;

public class RemoveCourseWLCommandHandler : IRequestHandler<RemoveCourseWLCommand>
{
private readonly IApplicationDbContext _context;

public RemoveCourseWLCommandHandler(IApplicationDbContext context)
{
_context = context;
}

public async Task Handle(RemoveCourseWLCommand request, CancellationToken cancellationToken)
{
var wishlistItem = await _context.WishlistItems
.Where(l => l.UserId == request.UserId && l.CourseId == request.CourseId)
.SingleOrDefaultAsync(cancellationToken);

Guard.Against.NotFound(request.CourseId, wishlistItem);

_context.WishlistItems.Remove(wishlistItem);

await _context.SaveChangesAsync(cancellationToken);
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ public class QueryDto
public string? CoverImageRelativePath { get; set; }
public bool IsPublished { get; set; }
public float Price { get; set; }
public int Likes { get; set; }
public string? AuthorName { get; set; }
public IReadOnlyCollection<CategoryDto> Categories { get; set; }
public IReadOnlyCollection<ChapterDto> Chapters { get; init; }

public string? AuthorName { get; set; }

public QueryDto()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public async Task<GetCourseVm> Handle(GetCourseByUsedIdQuery request, Cancellati
.AsNoTracking()
.Include(c => c.Categories)
.Include(c => c.Chapters)
.Where(c => c.UserCourses.Any(uc => uc.UserId == userId)) // Filter courses for the specified user
.Where(c => c.UserCourses.Any(uc => uc.UserId == userId))
.ProjectTo<QueryDto>(_mapper.ConfigurationProvider)
.OrderBy(t => t.Title)
.ToListAsync(cancellationToken)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using SkillSphere.Application.Common.Interfaces;
using SkillSphere.Application.Features.Courses.Queries.GetAllCourses;

namespace SkillSphere.Application.Features.Courses.Queries;

public class GetUserWishListQuery : IRequest<GetCourseVm>
{
public Guid UserId { get; init; }
}


public class GetUserWishListQueryHandler : IRequestHandler<GetUserWishListQuery, GetCourseVm>
{
private readonly IApplicationDbContext _context;
private readonly IMapper _mapper;

public GetUserWishListQueryHandler(IApplicationDbContext context, IMapper mapper)
{
_context = context;
_mapper = mapper;
}

public async Task<GetCourseVm> Handle(GetUserWishListQuery request, CancellationToken cancellationToken)
{

Guid userId = request.UserId;



return new GetCourseVm
{
Courses = await _context.Courses
.AsNoTracking()
.Include(c => c.Categories)
.Include(c => c.Chapters)
.Where(c => c.WishList.Any(wi => wi.UserId == userId))
.ProjectTo<QueryDto>(_mapper.ConfigurationProvider)
.OrderBy(t => t.Title)
.ToListAsync(cancellationToken)
};

}
}


1 change: 1 addition & 0 deletions src/Domain/Entities/ApplicationUser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ namespace SkillSphere.Domain.Entities;
public class ApplicationUser : IdentityUser<Guid>
{
public IList<UserCourse>? UserCourses { get; set; }
public IList<WishListItem>? WishList { get; init; } = new List<WishListItem>();
}
4 changes: 4 additions & 0 deletions src/Domain/Entities/Course.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,14 @@ public class Course : BaseAuditableEntity

public bool? IsPublished { get; set; } = false;
public float Price { get; set; }

public int Likes { get; set; } = 0;

public IList<Chapter> Chapters { get; init; } = new List<Chapter>();

public IList<CourseCategory> Categories { get; init; } = new List<CourseCategory>();

public IList<UserCourse> UserCourses { get; init; } = new List<UserCourse>();

public IList<WishListItem> WishList { get; init; } = new List<WishListItem>();
}
13 changes: 0 additions & 13 deletions src/Domain/Entities/UserCourse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,3 @@ public class UserCourse
public Course? Course { get; set; }
}

// STUFF I HAVE TO DO AND WHAT IS WRONG

// STRUCTURE
// - appUser is in infrastructure
// - userCourse is in Domain
// - appUser cannot be referenced from Domain/Entity
// - cannot use interface and implement it into appUser class in order to
// use it inside the Domain
// - clean architecture
// - vertical slices
// - dbContext
// - Application layer for having the interface of email sender and send it from infrastructure

10 changes: 10 additions & 0 deletions src/Domain/Entities/WishListItem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace SkillSphere.Domain.Entities;

public class WishListItem
{
public Guid UserId { get; set; }
public ApplicationUser? User { get; set; }

public Guid CourseId { get; set; }
public Course? Course { get; set; }
}
1 change: 1 addition & 0 deletions src/Infrastructure/Data/ApplicationDbContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : ba
public DbSet<Chapter> Chapters => Set<Chapter>();

public DbSet<UserCourse> UsersCourses => Set<UserCourse>();
public DbSet<WishListItem> WishlistItems => Set<WishListItem>();


protected override void OnModelCreating(ModelBuilder builder)
Expand Down
1 change: 1 addition & 0 deletions src/Infrastructure/Data/ApplicationDbContextInitialiser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ public async Task TrySeedAsync()
Description = "Course 1 Description",
CoverImageRelativePath = "https://images.pexels.com/photos/693859/pexels-photo-693859.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
Price = 9.99f,
Likes = 0,
Categories = new List<CourseCategory>
{
new CourseCategory
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using SkillSphere.Domain.Entities;

namespace SkillSphere.Infrastructure.Data.Configurations;

public class WishListItemConfiguration : IEntityTypeConfiguration<WishListItem>
{
public void Configure(EntityTypeBuilder<WishListItem> builder)
{
builder
.HasKey(wi => new { wi.UserId, wi.CourseId });

builder
.HasOne(wi => wi.User)
.WithMany(u => u.WishList)
.HasForeignKey(wi => wi.UserId);

builder
.HasOne(wi => wi.Course)
.WithMany(c => c.WishList)
.HasForeignKey(wi => wi.CourseId);
}

}




Loading

0 comments on commit b66e907

Please sign in to comment.