Skip to content

Commit

Permalink
feat: Add child scheduling
Browse files Browse the repository at this point in the history
Add basic child scheduling feature.
  • Loading branch information
LuukvH committed Aug 29, 2024
1 parent 9818b99 commit e3c5f25
Show file tree
Hide file tree
Showing 22 changed files with 584 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public GroupsController(IMediator mediator, ILogger<GroupsController> logger)
public async Task<ActionResult<PagedList<GroupListVM>>> ListGroups([FromQuery] ListGroupsQuery listGroupsQuery)
{
var dtos = await _mediator.Send(listGroupsQuery);
Response.Headers.Add("x-Total", dtos.TotalCount.ToString());
Response.Headers.Append("x-Total", dtos.TotalCount.ToString());
return Ok(dtos);
}

Expand Down
39 changes: 39 additions & 0 deletions src/Services/Scheduling/Api/Controllers/SchedulesController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using KDVManager.Services.Scheduling.Application.Features.Groups.Queries.ListGroups;
using MediatR;
using Microsoft.AspNetCore.Mvc;
using KDVManager.Services.Scheduling.Application.Contracts.Pagination;
using KDVManager.Services.Scheduling.Application.Features.Schedules.Queries.GetChildSchedules;
using System.Net;
using KDVManager.Services.Scheduling.Application.Features.Schedules.Commands.AddSchedule;

namespace KDVManager.Services.Scheduling.Api.Controllers;

[ApiController]
[Route("v1/[controller]")]
public class SchedulesController : ControllerBase
{
private readonly IMediator _mediator;
private readonly ILogger<SchedulesController> _logger;

public SchedulesController(IMediator mediator, ILogger<SchedulesController> logger)
{
_logger = logger;
_mediator = mediator;
}

[HttpGet("", Name = "GetChildSchedules")]
public async Task<ActionResult<PagedList<ChildScheduleListVM>>> ListScheduleItems([FromQuery] GetChildSchedulesQuery getChildSchedulesQuery)
{
var dtos = await _mediator.Send(getChildSchedulesQuery);
return Ok(dtos);
}

[HttpPost(Name = "AddSchedule")]
[ProducesResponseType(typeof(Guid), (int)HttpStatusCode.OK)]
[ProducesResponseType(typeof(UnprocessableEntityResponse), (int)HttpStatusCode.UnprocessableEntity)]
public async Task<ActionResult<Guid>> AddScheduleItem([FromBody] AddScheduleCommand addScheduleCommand)
{
var id = await _mediator.Send(addScheduleCommand);
return Ok(id);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public TimeSlotsController(IMediator mediator, ILogger<TimeSlotsController> logg
public async Task<ActionResult<PagedList<TimeSlotListVM>>> ListTimeSlots([FromQuery] ListTimeSlotsQuery listTimeSlotsQuery)
{
var dtos = await _mediator.Send(listTimeSlotsQuery);
Response.Headers.Add("x-Total", dtos.TotalCount.ToString());
Response.Headers.Append("x-Total", dtos.TotalCount.ToString());
return Ok(dtos);
}

Expand Down
2 changes: 1 addition & 1 deletion src/Services/Scheduling/Api/appsettings.Development.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"ConnectionStrings": {
"KDVManagerSchedulingConnectionString": "Server=127.0.0.1; port=5432; database=KDVManagerSchedulingDB; pooling=true; Integrated Security=true;"
"KDVManagerSchedulingConnectionString": "Server=127.0.0.1; port=5432; database=KDVManagerSchedulingDB; pooling=true;"
},
"Auth0": {
"Domain": "kdvmanager.eu.auth0.com",
Expand Down
43 changes: 21 additions & 22 deletions src/Services/Scheduling/Application/Application.csproj
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<Folder Include="Contracts\" />
<Folder Include="Exceptions\" />
<Folder Include="Profiles\" />
<Folder Include="Features\" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Domain\Domain.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />
<PackageReference Include="AutoMapper" Version="12.0.1" />
<PackageReference Include="FluentValidation" Version="11.9.2" />
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="11.1.0" />
</ItemGroup>
</Project>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Folder Include="Contracts\" />
<Folder Include="Exceptions\" />
<Folder Include="Profiles\" />
<Folder Include="Features\" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Domain\Domain.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />
<PackageReference Include="AutoMapper" Version="12.0.1" />
<PackageReference Include="FluentValidation" Version="11.9.2" />
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="11.1.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using KDVManager.Services.Scheduling.Application.Contracts.Persistence;
using KDVManager.Services.Scheduling.Domain.Entities;
using KDVManager.Services.Scheduling.Domain.Interfaces;

namespace KDVManager.Services.Scheduling.Application.Contracts.Persistence;

public interface IScheduleRepository : IAsyncRepository<Schedule>
{
Task<IReadOnlyList<Schedule>> GetSchedulesByChildIdAsync(Guid childId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System;
using MediatR;
using System.Collections.Generic;

namespace KDVManager.Services.Scheduling.Application.Features.Schedules.Commands.AddSchedule;

public class AddScheduleCommand : IRequest<Guid>
{
public Guid ChildId { get; set; }

public Guid GroupId { get; set; }

public DateTime StartDate { get; set; }

public DateTime? EndDate { get; set; }

// Collection of nested schedules
public ICollection<ScheduleRule> ScheduleRules { get; set; } = new List<ScheduleRule>();

public class ScheduleRule
{
public DayOfWeek Day { get; set; }

public Guid TimeSlotId { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using System;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using AutoMapper;
using KDVManager.Services.Scheduling.Application.Contracts.Persistence;
using KDVManager.Services.Scheduling.Domain.Entities;
using MediatR;
using Microsoft.Extensions.Logging;

namespace KDVManager.Services.Scheduling.Application.Features.Schedules.Commands.AddSchedule;

public class AddScheduleCommandHandler : IRequestHandler<AddScheduleCommand, Guid>
{
private readonly IScheduleRepository _scheduleRepository;
private readonly IMapper _mapper;

public AddScheduleCommandHandler(IScheduleRepository scheduleRepository, IMapper mapper)
{
_scheduleRepository = scheduleRepository;
_mapper = mapper;
}

public async Task<Guid> Handle(AddScheduleCommand request, CancellationToken cancellationToken)
{
var validator = new AddScheduleCommandValidator();
var validationResult = await validator.ValidateAsync(request);

if (!validationResult.IsValid)
throw new Exceptions.ValidationException(validationResult);

var schedule = _mapper.Map<Schedule>(request);

schedule = await _scheduleRepository.AddAsync(schedule);

return schedule.Id;
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using FluentValidation;
using KDVManager.Services.Scheduling.Application.Contracts.Persistence;

namespace KDVManager.Services.Scheduling.Application.Features.Schedules.Commands.AddSchedule;

public class AddScheduleCommandValidator : AbstractValidator<AddScheduleCommand>
{
public AddScheduleCommandValidator()
{

RuleFor(AddScheduleCommand => AddScheduleCommand.ChildId)
.NotEmpty()
.NotNull();

RuleFor(AddScheduleCommand => AddScheduleCommand.StartDate)
.NotEmpty()
.NotNull();

RuleFor(AddScheduleCommand => AddScheduleCommand.EndDate)
.GreaterThan(AddScheduleCommand => AddScheduleCommand.StartDate)
.When(AddScheduleCommand => AddScheduleCommand.EndDate.HasValue);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;

namespace KDVManager.Services.Scheduling.Application.Features.Schedules.Queries.GetChildSchedules;

public class ChildScheduleListVM
{
public Guid Id { get; set; }
public Guid ChildId { get; set; }
public DateTime StartDate { get; set; }
public DateTime? EndDate { get; set; }

// Collection of nested schedules
public ICollection<ScheduleRule> ScheduleRules { get; set; } = new List<ScheduleRule>();

public class ScheduleRule
{
public DayOfWeek Day { get; set; }

public Guid TimeSlotId { get; set; }
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using KDVManager.Services.Scheduling.Domain;
using KDVManager.Services.Scheduling.Application.Contracts.Pagination;
using MediatR;

namespace KDVManager.Services.Scheduling.Application.Features.Schedules.Queries.GetChildSchedules;

public class GetChildSchedulesQuery : IRequest<List<ChildScheduleListVM>>
{
public Guid ChildId { get; set; }
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using AutoMapper;
using KDVManager.Services.Scheduling.Application.Contracts.Persistence;
using KDVManager.Services.Scheduling.Application.Contracts.Pagination;
using KDVManager.Services.Scheduling.Domain.Entities;
using MediatR;

namespace KDVManager.Services.Scheduling.Application.Features.Schedules.Queries.GetChildSchedules;

public class GetChildSchedulesQueryHandler : IRequestHandler<GetChildSchedulesQuery, List<ChildScheduleListVM>>
{
private readonly IScheduleRepository _scheduleRepository;
private readonly IMapper _mapper;

public GetChildSchedulesQueryHandler(IMapper mapper, IScheduleRepository scheduleRepository)
{
_scheduleRepository = scheduleRepository;
_mapper = mapper;
}

public async Task<List<ChildScheduleListVM>> Handle(GetChildSchedulesQuery request, CancellationToken cancellationToken)
{
var scheduleItems = await _scheduleRepository.GetSchedulesByChildIdAsync(request.ChildId);

List<ChildScheduleListVM> childScheduleListVMs = _mapper.Map<List<ChildScheduleListVM>>(scheduleItems);

return new List<ChildScheduleListVM>(childScheduleListVMs);
}
}

13 changes: 13 additions & 0 deletions src/Services/Scheduling/Application/Profiles/MappingProfile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
using KDVManager.Services.Scheduling.Application.Features.Groups.Queries.ListGroups;
using KDVManager.Services.Scheduling.Application.Features.TimeSlots.Commands.AddTimeSlot;
using KDVManager.Services.Scheduling.Application.Features.TimeSlots.Queries.ListTimeSlots;
using KDVManager.Services.Scheduling.Application.Features.Schedules.Commands.AddSchedule;
using KDVManager.Services.Scheduling.Application.Features.Schedules.Queries.GetChildSchedules;
using KDVManager.Services.Scheduling.Domain.Entities;

namespace KDVManager.Services.Scheduling.Application.Profiles;
Expand All @@ -12,13 +14,24 @@ public class MappingProfile : Profile
{
public MappingProfile()
{
// Group Mappings
CreateMap<Group, GroupListVM>();
CreateMap<Group, AddGroupCommand>().ReverseMap();
CreateMap<ListGroupsQuery, PageParameters>();

// TimeSlot Mappings
CreateMap<TimeSlot, TimeSlotListVM>();
CreateMap<TimeSlot, AddTimeSlotCommand>().ReverseMap();
CreateMap<ListTimeSlotsQuery, PageParameters>();

// Schedule Mappings
CreateMap<AddScheduleCommand, Schedule>()
.ForMember(dest => dest.ScheduleRules, opt => opt.MapFrom(src => src.ScheduleRules));
CreateMap<AddScheduleCommand.ScheduleRule, ScheduleRule>();

CreateMap<Schedule, ChildScheduleListVM>()
.ForMember(dest => dest.ScheduleRules, opt => opt.MapFrom(src => src.ScheduleRules));
CreateMap<ScheduleRule, ChildScheduleListVM.ScheduleRule>();
}
}

16 changes: 16 additions & 0 deletions src/Services/Scheduling/Domain/Entities/Schedule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using KDVManager.Services.Scheduling.Domain.Interfaces;

namespace KDVManager.Services.Scheduling.Domain.Entities;

public class Schedule : IMustHaveTenant
{
public Guid Id { get; set; }
public Guid TenantId { get; set; }
public Guid ChildId { get; set; }
public Guid GroupId { get; set; }
public DateTime StartDate { get; set; }
public DateTime? EndDate { get; set; }
public ICollection<ScheduleRule> ScheduleRules { get; set; }
}
15 changes: 15 additions & 0 deletions src/Services/Scheduling/Domain/Entities/ScheduleRule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System;
using System.ComponentModel.DataAnnotations;
using KDVManager.Services.Scheduling.Domain.Interfaces;

namespace KDVManager.Services.Scheduling.Domain.Entities;

public class ScheduleRule : IMustHaveTenant
{
public Guid Id { get; set; }
public Guid TenantId { get; set; }
public DayOfWeek Day { get; set; }
public Guid ScheduleId { get; set; }
[Required]
public Guid TimeSlotId { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public static IServiceCollection AddInfrastructureServices(this IServiceCollecti

services.AddScoped<IGroupRepository, GroupRepository>();
services.AddScoped<ITimeSlotRepository, TimeSlotRepository>();
services.AddScoped<IScheduleRepository, ScheduleRepository>();

return services;
}
Expand Down
3 changes: 2 additions & 1 deletion src/Services/Scheduling/Infrastructure/MigrationDbContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ public MigrationDbContext(DbContextOptions<MigrationDbContext> options) : base(o
}

public DbSet<Group> Groups { get; set; }

public DbSet<TimeSlot> TimeSlots { get; set; }
public DbSet<Schedule> Schedules { get; set; }
public DbSet<ScheduleRule> ScheduleRules { get; set; }

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
Expand Down
Loading

0 comments on commit e3c5f25

Please sign in to comment.