<repo_name>W0nsu/Fitverse.CalendarService<file_sep>/Fitverse.CalendarService/Commands/SignOutOfClassCommand.cs
using Fitverse.CalendarService.Dtos;
using MediatR;

namespace Fitverse.CalendarService.Commands
{
	public class SignOutOfClassCommand : IRequest<ReservationDtoSetter>
	{
		public SignOutOfClassCommand(int reservationId)
		{
			ReservationId = reservationId;
		}

		public int ReservationId { get; }
	}
}<file_sep>/Fitverse.CalendarService/Queries/GetClassTypeByIdQuery.cs
using Fitverse.CalendarService.Dtos;
using MediatR;

namespace Fitverse.CalendarService.Queries
{
	public class GetClassTypeByIdQuery : IRequest<ClassTypeDto>
	{
		public GetClassTypeByIdQuery(int classTypeId)
		{
			ClassTypeId = classTypeId;
		}

		public int ClassTypeId { get; }
	}
}<file_sep>/Fitverse.CalendarService/MessageBus/Senders/SignOutOfClassSender.cs
using System;
using Fitverse.CalendarService.Interfaces;
using Fitverse.Shared.MessageBus;
using Microsoft.Extensions.Options;

namespace Fitverse.CalendarService.MessageBus.Senders
{
	public class SignOutOfClassSender : ISignOutOfClassSender
	{
		private readonly IOptions<RabbitMqConfiguration> _rabbitMqOptions;

		public SignOutOfClassSender(IOptions<RabbitMqConfiguration> rabbitMqOptions)
		{
			_rabbitMqOptions = rabbitMqOptions;
		}
		
		public void DeleteReservation(int reservationId)
		{
			var exchangeConfig = new Tuple<string, string>("classes", "signOutOfClass");
			SendEventHandler.SendEvent(reservationId, _rabbitMqOptions, exchangeConfig);
		}
	}
}<file_sep>/Fitverse.CalendarService/Handlers/GetClassByDateHandler.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Fitverse.CalendarService.Data;
using Fitverse.CalendarService.Dtos;
using Fitverse.CalendarService.Queries;
using Mapster;
using MediatR;
using Microsoft.EntityFrameworkCore;

namespace Fitverse.CalendarService.Handlers
{
	public class GetClassByDateHandler : IRequestHandler<GetClassByDateQuery, List<CalendarClassDto>>
	{
		private readonly CalendarContext _dbContext;

		public GetClassByDateHandler(CalendarContext dbContext)
		{
			_dbContext = dbContext;
		}

		public async Task<List<CalendarClassDto>> Handle(GetClassByDateQuery request,
			CancellationToken cancellationToken)
		{
			var classList = await _dbContext
				.Classes
				.Where(c => c.Date.Date >= request.DateRange.DateFrom && c.Date.Date <= request.DateRange.DateTo)
				.ToListAsync(cancellationToken);

			if (classList is null)
			{
				throw new NullReferenceException(
					$"There is no classes for given period. [Date from: {request.DateRange.DateFrom}, Date to: {request.DateRange.DateTo}]");
			}

			return classList
				.Select(calendarClass => calendarClass.Adapt<CalendarClassDto>())
				.OrderBy(x => x.Date)
				.ThenBy(x => x.StartingTime.TimeOfDay)
				.ToList();
		}
	}
}<file_sep>/Fitverse.CalendarService/Controllers/SettingsController.cs
using System.Threading.Tasks;
using Fitverse.CalendarService.Commands;
using Fitverse.CalendarService.Dtos;
using Fitverse.CalendarService.Models;
using Fitverse.CalendarService.Queries;
using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.JsonPatch;
using Microsoft.AspNetCore.Mvc;

namespace Fitverse.CalendarService.Controllers
{
	[Authorize]
	[Route("/api/cs/calendar/settings")]
	[ApiController]
	public class SettingsController : Controller
	{
		private readonly IMediator _mediator;

		public SettingsController(IMediator mediator)
		{
			_mediator = mediator;
		}
		
		[HttpGet]
		public async Task<IActionResult> GetAllClassTypes()
		{
			var query = new GetAllClassTypesQuery();
			var result = await _mediator.Send(query);
			return Ok(result);
		}
		
		[HttpPost]
		public async Task<IActionResult> AddClassType([FromBody] ClassTypeDto membershipDto)
		{
			var command = new AddClassTypeCommand(membershipDto);
			var result = await _mediator.Send(command);
			return Ok(result);
		}
		
		[HttpGet("{classTypeId}")]
		public async Task<IActionResult> GetClassTypeById([FromRoute] int classTypeId)
		{
			var query = new GetClassTypeByIdQuery(classTypeId);
			var result = await _mediator.Send(query);
			return Ok(result);
		}
		
		[HttpPatch("{classTypeId}")]
		public async Task<IActionResult> EditClassType([FromRoute] int classTypeId,
			[FromBody] JsonPatchDocument<ClassType> classTypeEntity)
		{
			var command = new EditClassTypeCommand(classTypeId, classTypeEntity);
			var result = await _mediator.Send(command);
			return Ok(result);
		}
		
		[HttpDelete("{classTypeId}")]
		public async Task<IActionResult> DeleteClassType([FromRoute] int classTypeId)
		{
			var command = new DeleteClassTypeCommand(classTypeId);
			var result = await _mediator.Send(command);
			return Ok($"Class type [ClassTypeId: {result.ClassTypeId}] has been deleted");
		}
	}
}<file_sep>/Fitverse.CalendarService/Handlers/EditClassTypeHandler.cs
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Fitverse.CalendarService.Commands;
using Fitverse.CalendarService.Data;
using Fitverse.CalendarService.Dtos;
using Fitverse.CalendarService.Validators;
using FluentValidation;
using Mapster;
using MediatR;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

namespace Fitverse.CalendarService.Handlers
{
	public class EditClassTypeHandler : ControllerBase, IRequestHandler<EditClassTypeCommand, ClassTypeDto>
	{
		private readonly CalendarContext _dbContext;

		public EditClassTypeHandler(CalendarContext dbContext)
		{
			_dbContext = dbContext;
		}

		public async Task<ClassTypeDto> Handle(EditClassTypeCommand request, CancellationToken cancellationToken)
		{
			var classTypeEntity = await _dbContext
				.ClassTypes
				.SingleOrDefaultAsync(m => m.ClassTypeId == request.ClassTypeId, cancellationToken);

			if (classTypeEntity is null)
			{
				throw new NullReferenceException($"ClassType [classTypeId: {request.ClassTypeId}] not found");
			}

			var editedClassType = request.NewClassTypeEntity;
			var nameBeforeChange = classTypeEntity.Name;

			editedClassType.ApplyTo(classTypeEntity, ModelState);

			var nameAfterChange = classTypeEntity.Name;
			// var validator =
			// 	new ClassTypeValidator(_dbContext, new Tuple<string, string>(nameBeforeChange, nameAfterChange));
			// var validationResult = await validator.ValidateAsync(classTypeEntity, cancellationToken);
			//
			// if (!validationResult.IsValid)
			// 	throw new ValidationException(validationResult.Errors.ToList());

			_ = await _dbContext.SaveChangesAsync(cancellationToken);

			var patchedClassTypeEntity = await _dbContext
				.ClassTypes
				.SingleOrDefaultAsync(m => m.ClassTypeId == request.ClassTypeId, cancellationToken);

			if (patchedClassTypeEntity is null)
			{
				throw new NullReferenceException(
					$"Failed to fetch patched class type [ClassTypeId: {request.ClassTypeId}]");
			}

			var patchedClassTypeDto = patchedClassTypeEntity.Adapt<ClassTypeDto>();

			return patchedClassTypeDto;
		}
	}
}<file_sep>/Fitverse.CalendarService/Handlers/DeleteClassByIdHandler.cs
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Fitverse.CalendarService.Commands;
using Fitverse.CalendarService.Data;
using Fitverse.CalendarService.Dtos;
using Mapster;
using MediatR;
using Microsoft.EntityFrameworkCore;

namespace Fitverse.CalendarService.Handlers
{
	public class DeleteClassByIdHandler : IRequestHandler<DeleteClassByIdCommand, CalendarClassDto>
	{
		private readonly CalendarContext _dbContext;

		public DeleteClassByIdHandler(CalendarContext dbContext)
		{
			_dbContext = dbContext;
		}

		public async Task<CalendarClassDto> Handle(DeleteClassByIdCommand request, CancellationToken cancellationToken)
		{
			var classEntity = await _dbContext
				.Classes
				.SingleOrDefaultAsync(m => m.ClassId == request.ClassId, cancellationToken);

			_dbContext.Remove(classEntity);
			_ = await _dbContext.SaveChangesAsync(cancellationToken);
			
			var deletedReservationsForClass = await _dbContext.Reservations
				.Where(x => x.ClassId == request.ClassId)
				.ToListAsync(cancellationToken);

			foreach (var reservation in deletedReservationsForClass)
				_ = _dbContext.Remove(reservation);

			_ = _dbContext.SaveChangesAsync(cancellationToken);

			var classDto = classEntity.Adapt<CalendarClassDto>();

			return classDto;
		}
	}
}<file_sep>/Fitverse.CalendarService/Dtos/CalendarClassDto.cs
using System;
using System.Collections.Generic;
using System.Globalization;

namespace Fitverse.CalendarService.Dtos
{
	public class CalendarClassDto
	{
		public int ClassId { get; private set; }

		public int ClassTypeId { get; set; }

		public DateTime Date { get; set; }

		public DateTime StartingTime { get; set; }

		public DateTime EndingTime { get; set; }

		public string ClassName { get; set; }

		public int Limit { get; set; }

		public string Description { get; set; }

		public int TimetableId { get; set; }

		public List<int> Reservations { get; set; }

		public string ShortDate => Date.ToShortDateString();
	}
}<file_sep>/Fitverse.CalendarService/Queries/GetClassByDateQuery.cs
using System;
using System.Collections.Generic;
using Fitverse.CalendarService.Dtos;
using Fitverse.CalendarService.Helpers;
using MediatR;

namespace Fitverse.CalendarService.Queries
{
	public class GetClassByDateQuery : IRequest<List<CalendarClassDto>>
	{
		public GetClassByDateQuery(DateRange dateRange)
		{
			DateRange = dateRange;
		}

		public DateRange DateRange { get; }
	}
}<file_sep>/Fitverse.CalendarService/Data/CalendarContext.cs
using Fitverse.CalendarService.Models;
using Microsoft.EntityFrameworkCore;

namespace Fitverse.CalendarService.Data
{
	public class CalendarContext : DbContext
	{
		public CalendarContext(DbContextOptions<CalendarContext> options) : base(options)
		{
		}

		public DbSet<ClassType> ClassTypes { get; set; }

		public DbSet<CalendarClass> Classes { get; set; }

		public DbSet<Timetable> Timetables { get; set; }
		
		public DbSet<Reservation> Reservations { get; set; }
		
		public DbSet<Member> Members { get; set; }
		
		protected override void OnModelCreating(ModelBuilder builder)
		{
			builder.Entity<Member>()
				.HasIndex(u => u.MemberId)
				.IsUnique();

			builder.Entity<ClassType>()
				.Property(x => x.IsDeleted)
				.HasDefaultValue(false);
		}
	}
}<file_sep>/Fitverse.CalendarService/Commands/DeleteTimetableByIdCommand.cs
using Fitverse.CalendarService.Dtos;
using MediatR;

namespace Fitverse.CalendarService.Commands
{
	public class DeleteTimetableByIdCommand : IRequest<TimetableDto>
	{
		public DeleteTimetableByIdCommand(int timetableId)
		{
			TimetableId = timetableId;
		}

		public int TimetableId { get; }
	}
}<file_sep>/Fitverse.CalendarService/Helpers/DateRange.cs
using System;

namespace Fitverse.CalendarService.Helpers
{
	public class DateRange
	{
		public DateTime DateFrom { get; set; }
		public DateTime DateTo { get; set; }
	}
}<file_sep>/Fitverse.CalendarService/Interfaces/ISignUpForClassSender.cs
using Fitverse.CalendarService.Models;

namespace Fitverse.CalendarService.Interfaces
{
	public interface ISignUpForClassSender
	{
		public void AddReservation(Reservation reservation);
	}
}<file_sep>/Fitverse.CalendarService/Dtos/TimetableDto.cs
using System;

namespace Fitverse.CalendarService.Dtos
{
	public class TimetableDto
	{
		public int TimetableId { get; private set; }
		public int ClassTypeId { get; set; }
		public string ClassTypeName { get; set; }
		public DateTime StartingDate { get; set; }
		public DateTime EndingDate { get; set; }
		public DateTime ClassesStartingTime { get; set; }
		public int PeriodType { get; set; }
	}
}<file_sep>/Fitverse.CalendarService/Controllers/MembersController.cs
using System.Threading.Tasks;
using Fitverse.CalendarService.Queries;
using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace Fitverse.CalendarService.Controllers
{
	[Authorize]
	[ApiController]
	[Route("/api/cs/members")]
	public class MembersController : Controller
	{
		private readonly IMediator _mediator;

		public MembersController(IMediator mediator)
		{
			_mediator = mediator;
		}
		
		[HttpGet]
		public async Task<IActionResult> GetAllMembers()
		{
			var query = new GetAllMembersQuery();
			var result = await _mediator.Send(query);
			return Ok(result);
		}
	}
}<file_sep>/Fitverse.CalendarService/Handlers/SignUpForClassHandler.cs
using System;
using System.Threading;
using System.Threading.Tasks;
using Fitverse.CalendarService.Commands;
using Fitverse.CalendarService.Data;
using Fitverse.CalendarService.Dtos;
using Fitverse.CalendarService.Interfaces;
using Fitverse.CalendarService.Models;
using Mapster;
using MediatR;
using Microsoft.EntityFrameworkCore;

namespace Fitverse.CalendarService.Handlers
{
	public class SignUpForClassHandler : IRequestHandler<SignUpForClassCommand, ReservationDtoSetter>
	{
		private readonly CalendarContext _dbContext;
		private readonly ISignUpForClassSender _signUpForClassSender;
		private int _classTypeParticipantsLimit;

		public SignUpForClassHandler(CalendarContext dbContext, ISignUpForClassSender signUpForClassSender)
		{
			_dbContext = dbContext;
			_signUpForClassSender = signUpForClassSender;
		}

		public async Task<ReservationDtoSetter> Handle(SignUpForClassCommand request, CancellationToken cancellationToken)
		{
			var reservationEntity = request.Reservation.Adapt<Reservation>();

			if (await IsLimitExceeded(reservationEntity, cancellationToken))
			{
				throw new ArgumentException(
					$"Limit of participants for this classes has been exceeded [Limit: {_classTypeParticipantsLimit}]");
			}

			_ = await _dbContext.AddAsync(reservationEntity, cancellationToken);
			_ = await _dbContext.SaveChangesAsync(cancellationToken);

			var newReservation = await _dbContext
				.Reservations
				.SingleOrDefaultAsync(
					m => m.ClassId == reservationEntity.ClassId && m.MemberId == reservationEntity.MemberId,
					cancellationToken);

			if (newReservation is null)
				throw new NullReferenceException("Failed to sign in for classes. Try again");
			
			_signUpForClassSender.AddReservation(newReservation);

			var newReservationDto = newReservation.Adapt<ReservationDtoSetter>();

			return newReservationDto;
		}

		private async Task<bool> IsLimitExceeded(Reservation newReservation,
			CancellationToken cancellationToken = default)
		{
			var classEntity = await _dbContext
				.Classes
				.SingleOrDefaultAsync(x => x.ClassId == newReservation.ClassId, cancellationToken);

			var classTypeEntity = await _dbContext
				.ClassTypes
				.SingleOrDefaultAsync(x => x.ClassTypeId == classEntity.ClassTypeId, cancellationToken);

			_classTypeParticipantsLimit = classTypeEntity.Limit;

			var numberOfReservations = await _dbContext
				.Reservations
				.CountAsync(x => x.ClassId == newReservation.ClassId, cancellationToken);

			return numberOfReservations >= _classTypeParticipantsLimit;
		}
	}
}<file_sep>/Fitverse.CalendarService/Validators/AddTimetableCommandValidator.cs
using System;
using System.Linq;
using Fitverse.CalendarService.Commands;
using Fitverse.CalendarService.Data;
using Fitverse.Shared.Helpers;
using FluentValidation;

namespace Fitverse.CalendarService.Validators
{
	public class AddTimetableCommandValidator : AbstractValidator<AddTimetableCommand>
	{
		public AddTimetableCommandValidator(CalendarContext dbContext)
		{
			RuleFor(x => x.NewTimetableDto.ClassTypeId)
				.GreaterThan(0);
				
			RuleFor(x => x.NewTimetableDto.ClassTypeId)
				.Must(id => dbContext.ClassTypes.Any(m => m.ClassTypeId == id))
				.WithMessage(x => $"ClassType [ClassTypeId: {x.NewTimetableDto.ClassTypeId}] doesn't exists.");

			RuleFor(x => x.NewTimetableDto.StartingDate)
				.NotEmpty();
				
			RuleFor(x => x.NewTimetableDto.StartingDate)
				.Must(startingDate => startingDate >= DateTime.Now)
				.WithMessage($"Select a date later than {DateTime.Now.ToShortDateString()}");

			RuleFor(x => x.NewTimetableDto.EndingDate)
				.NotEmpty();
				
			RuleFor(x => x.NewTimetableDto.EndingDate)
				.Must(endingDate => endingDate > DateTime.Now)
				.WithMessage("Select a date later than Timetable starting date");

			RuleFor(x => x.NewTimetableDto.ClassesStartingTime)
				.NotEmpty();

			RuleFor(x => x.NewTimetableDto.PeriodType)
				.NotEmpty();
				
			RuleFor(x => x.NewTimetableDto.PeriodType)
				.Must(periodType => Enum.IsDefined(typeof(PeriodType), periodType))
				.WithMessage("Available PeriodType: " + Enum.GetNames(typeof(PeriodType))
					.Aggregate("", (current, value) => current + value + ", "));
		}
	}
}<file_sep>/Fitverse.CalendarService/Queries/GetClassByIdQuery.cs
using Fitverse.CalendarService.Data;
using Fitverse.CalendarService.Dtos;
using MediatR;

namespace Fitverse.CalendarService.Queries
{
	public class GetClassByIdQuery : IRequest<CalendarClassDto>
	{
		public GetClassByIdQuery(int classId)
		{
			ClassId = classId;
		}

		public int ClassId { get; }
	}
}<file_sep>/Fitverse.CalendarService/Queries/GetAllTimetablesQuery.cs
using System.Collections.Generic;
using Fitverse.CalendarService.Dtos;
using MediatR;

namespace Fitverse.CalendarService.Queries
{
	public class GetAllTimetablesQuery : IRequest<List<TimetableDto>>
	{
	}
}<file_sep>/Fitverse.CalendarService/Dtos/ReservationDtoSetter.cs
namespace Fitverse.CalendarService.Dtos
{
	public class ReservationDtoSetter
	{
		public int ReservationId { get; private set; }
		public int ClassId { get; set; }
		public int MemberId { get; set; }
	}
}<file_sep>/Fitverse.CalendarService/Queries/GetReservationsByClassIdCommand.cs
using System.Collections.Generic;
using Fitverse.CalendarService.Dtos;
using MediatR;
using Microsoft.EntityFrameworkCore.Query.Internal;

namespace Fitverse.CalendarService.Queries
{
	public class GetReservationsByClassIdCommand : IRequest<List<ReservationDtoGetter>>
	{
		public GetReservationsByClassIdCommand(int classId)
		{
			ClassId = classId;
		}
		
		public int ClassId { get; }
	}
}<file_sep>/Fitverse.CalendarService/Queries/GetAllMembersQuery.cs
using System.Collections.Generic;
using Fitverse.CalendarService.Dtos;
using MediatR;

namespace Fitverse.CalendarService.Queries
{
	public class GetAllMembersQuery : IRequest<List<MemberDto>>
	{
		
	}
}<file_sep>/Fitverse.CalendarService/Queries/GetTimetableByIdQuery.cs
using Fitverse.CalendarService.Dtos;
using MediatR;

namespace Fitverse.CalendarService.Queries
{
	public class GetTimetableByIdQuery : IRequest<TimetableDto>
	{
		public GetTimetableByIdQuery(int timetableId)
		{
			TimetableId = timetableId;
		}

		public int TimetableId { get; }
	}
}<file_sep>/Fitverse.CalendarService/Dtos/MemberDto.cs
namespace Fitverse.CalendarService.Dtos
{
	public class MemberDto
	{
		public int MemberId { get; set; }

		public string Name { get; set; }

		public string SurName { get; set; }

		public string Email { get; set; }
	}
}<file_sep>/Fitverse.CalendarService/Migrations/20210424164208_ClassNameInClassModel.cs
﻿using Microsoft.EntityFrameworkCore.Migrations;

namespace Fitverse.CalendarService.Migrations
{
    public partial class ClassNameInClassModel : Migration
    {
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.AlterColumn<bool>(
                name: "IsDeleted",
                table: "ClassTypes",
                type: "bit",
                nullable: false,
                defaultValue: false,
                oldClrType: typeof(bool),
                oldType: "bit");

            migrationBuilder.AddColumn<string>(
                name: "ClassName",
                table: "Classes",
                type: "nvarchar(max)",
                nullable: false,
                defaultValue: "");
        }

        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.DropColumn(
                name: "ClassName",
                table: "Classes");

            migrationBuilder.AlterColumn<bool>(
                name: "IsDeleted",
                table: "ClassTypes",
                type: "bit",
                nullable: false,
                oldClrType: typeof(bool),
                oldType: "bit",
                oldDefaultValue: false);
        }
    }
}
<file_sep>/Fitverse.CalendarService/Validators/ClassTypeValidator.cs
using System;
using System.Linq;
using Fitverse.CalendarService.Data;
using Fitverse.CalendarService.Models;
using FluentValidation;

namespace Fitverse.CalendarService.Validators
{
	public class ClassTypeValidator : AbstractValidator<ClassType>
	{
		// public ClassTypeValidator(CalendarContext dbContext, Tuple<string, string> nameBeforeAndAfterChange)
		// {
		// 	string nameBeforeChange, nameAfterChange;
		// 	(nameBeforeChange, nameAfterChange) = nameBeforeAndAfterChange;
		//
		// 	RuleFor(x => x.Name)
		// 		.NotEmpty()
		// 		.MinimumLength(3)
		// 		.MaximumLength(30);
		//
		// 	if (nameBeforeChange != nameAfterChange)
		// 	{
		// 		RuleFor(x => x.Name)
		// 			.Must(name => !dbContext.ClassTypes.Any(m => m.Name == name))
		// 			.WithMessage(x => $"Name [Name: {x.Name}] already in use");
		// 	}
		//
		// 	RuleFor(x => x.Description)
		// 		.MaximumLength(255);
		//
		// 	RuleFor(x => x.Limit)
		// 		.NotEmpty()
		// 		.GreaterThan(0);
		//
		// 	RuleFor(x => x.Room)
		// 		.MinimumLength(3)
		// 		.MaximumLength(30);
		//
		// 	RuleFor(x => x.Duration)
		// 		.NotEmpty()
		// 		.GreaterThan(0);
		// }
	}
}<file_sep>/Fitverse.CalendarService/Dtos/ReservationDtoGetter.cs
namespace Fitverse.CalendarService.Dtos
{
	public class ReservationDtoGetter
	{
		public int ReservationId { get; private set; }
		public int ClassId { get; set; }
		public int MemberId { get; set; }

		public MemberDto Member { get; set; }
	}
}<file_sep>/Fitverse.CalendarService/Handlers/GetTimetableByIdHandler.cs
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Fitverse.CalendarService.Data;
using Fitverse.CalendarService.Dtos;
using Fitverse.CalendarService.Queries;
using Mapster;
using MediatR;
using Microsoft.EntityFrameworkCore;

namespace Fitverse.CalendarService.Handlers
{
	public class GetTimetableByIdHandler : IRequestHandler<GetTimetableByIdQuery, TimetableDto>
	{
		private readonly CalendarContext _dbContext;

		public GetTimetableByIdHandler(CalendarContext dbContext)
		{
			_dbContext = dbContext;
		}

		public async Task<TimetableDto> Handle(GetTimetableByIdQuery request, CancellationToken cancellationToken)
		{
			var timetableEntity = await _dbContext
				.Timetables
				.SingleOrDefaultAsync(m => m.TimetableId == request.TimetableId, cancellationToken);

			if (timetableEntity is null)
				throw new NullReferenceException($"Timetable[TimetableId: {request.TimetableId} not found]");

			var timetableDto = timetableEntity.Adapt<TimetableDto>();
			timetableDto.ClassTypeName =  _dbContext
				.ClassTypes
				.FirstOrDefault(x => x.ClassTypeId == timetableEntity.ClassTypeId)
				?.Name;

			return timetableDto;
		}
	}
}<file_sep>/Fitverse.CalendarService/Validators/EditClassTypeCommandValidator.cs
using System.Linq;
using Fitverse.CalendarService.Commands;
using Fitverse.CalendarService.Data;
using FluentValidation;

namespace Fitverse.CalendarService.Validators
{
	public class EditClassTypeCommandValidator : AbstractValidator<EditClassTypeCommand>
	{
		public EditClassTypeCommandValidator(CalendarContext dbContext)
		{
			RuleFor(x => x.ClassTypeId)
				.GreaterThan(0);
				
			RuleFor(x => x.ClassTypeId)
				.Must(id => dbContext.ClassTypes.Any(m => m.ClassTypeId == id))
				.WithMessage(x => $"Class type [ClassTypeId: {x.ClassTypeId}] not found");
		}
	}
}<file_sep>/Fitverse.CalendarService/Handlers/AddTimetableHandler.cs
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Fitverse.CalendarService.Commands;
using Fitverse.CalendarService.Data;
using Fitverse.CalendarService.Dtos;
using Fitverse.CalendarService.Helpers;
using Fitverse.CalendarService.Models;
using Mapster;
using MediatR;

namespace Fitverse.CalendarService.Handlers
{
	public class AddTimetableHandler : IRequestHandler<AddTimetableCommand, TimetableDto>
	{
		private readonly CalendarContext _dbContext;

		public AddTimetableHandler(CalendarContext dbContext)
		{
			_dbContext = dbContext;
		}

		public async Task<TimetableDto> Handle(AddTimetableCommand request, CancellationToken cancellationToken)
		{
			var timetableEntity = request.NewTimetableDto.Adapt<Timetable>();

			_ = await _dbContext.AddAsync(timetableEntity, cancellationToken);
			_ = await _dbContext.SaveChangesAsync(cancellationToken);

			var newTimetable = _dbContext
				.Timetables
				.Where(m => m.ClassTypeId == request.NewTimetableDto.ClassTypeId)
				.AsEnumerable()
				.LastOrDefault();

			if (newTimetable is null)
				throw new NullReferenceException("Failed to add timetable. Try again");

			var classGenerator = new ClassGenerator(_dbContext);
			await classGenerator.AddClassesForTimetableAsync(timetableEntity, cancellationToken);

			var newTimetableDto = newTimetable.Adapt<TimetableDto>();

			return newTimetableDto;
		}
	}
}<file_sep>/Fitverse.CalendarService/Validators/SignUpForClassCommandValidator.cs
using System.Linq;
using Fitverse.CalendarService.Commands;
using Fitverse.CalendarService.Data;
using FluentValidation;

namespace Fitverse.CalendarService.Validators
{
	public class SignUpForClassCommandValidator : AbstractValidator<SignUpForClassCommand>
	{
		public SignUpForClassCommandValidator(CalendarContext dbContext)
		{
			RuleFor(x => x.Reservation.ClassId)
				.GreaterThan(0);

			RuleFor(x => x.Reservation.ClassId)
				.Must(id => dbContext.Classes.Any(m => m.ClassId == id))
				.WithMessage(x => $"Classes [ClassId: {x.Reservation.ClassId}] doesn't exists.");

			RuleFor(x => x.Reservation.MemberId)
				.GreaterThan(0);

			RuleFor(x => x.Reservation.MemberId)
				.Must(id => dbContext.Members.Any(m => m.MemberId == id))
				.WithMessage(x => $"Member [MemberId: {x.Reservation.MemberId}] doesn't exists.");

			RuleFor(x => x.Reservation)
				.Must(reservation =>
					!dbContext.Reservations.Any(r =>
						r.ClassId == reservation.ClassId && r.MemberId == reservation.MemberId))
				.WithMessage(x =>
					$"Member [MemberId: {x.Reservation.MemberId}] is already registered to Classes [ClassId: {x.Reservation.ClassId}]");
		}
	}
}<file_sep>/Fitverse.CalendarService/Handlers/DeleteTimetableByIdHandler.cs
using System;
using System.Threading;
using System.Threading.Tasks;
using Fitverse.CalendarService.Commands;
using Fitverse.CalendarService.Data;
using Fitverse.CalendarService.Dtos;
using Fitverse.CalendarService.Helpers;
using Fitverse.CalendarService.Models;
using Mapster;
using MediatR;
using Microsoft.EntityFrameworkCore;

namespace Fitverse.CalendarService.Handlers
{
	public class DeleteTimetableByIdHandler : IRequestHandler<DeleteTimetableByIdCommand, TimetableDto>
	{
		private readonly CalendarContext _dbContext;

		public DeleteTimetableByIdHandler(CalendarContext dbContext)
		{
			_dbContext = dbContext;
		}
		
		public async Task<TimetableDto> Handle(DeleteTimetableByIdCommand request, CancellationToken cancellationToken)
		{
			var timetableEntity = await _dbContext
				.Timetables
				.SingleOrDefaultAsync(m => m.TimetableId == request.TimetableId, cancellationToken);

			var classGenerator = new ClassGenerator(_dbContext);
			await classGenerator.DeleteAllClassesByTimetableIdAsync(timetableEntity, cancellationToken);

			_dbContext.Remove(timetableEntity);
			_ = await _dbContext.SaveChangesAsync(cancellationToken);

			var timetableDto = timetableEntity.Adapt<TimetableDto>();

			return timetableDto;
		}
	}
}<file_sep>/Fitverse.CalendarService/Models/Reservation.cs
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace Fitverse.CalendarService.Models
{
	public class Reservation
	{
		[Key]
		[Required]
		[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
		public int ReservationId { get; set; }

		[Required] public int ClassId { get; set; }

		[Required] public int MemberId { get; set; }
	}
}<file_sep>/Fitverse.CalendarService/Validators/DeleteTimetableByIdCommandValidator.cs
using System.Linq;
using Fitverse.CalendarService.Commands;
using Fitverse.CalendarService.Data;
using FluentValidation;

namespace Fitverse.CalendarService.Validators
{
	public class DeleteTimetableByIdCommandValidator : AbstractValidator<DeleteTimetableByIdCommand>
	{
		public DeleteTimetableByIdCommandValidator(CalendarContext dbContext)
		{
			RuleFor(x => x.TimetableId)
				.GreaterThan(0);
				
			RuleFor(x => x.TimetableId)
				.Must(id => dbContext.Classes.Any(m => m.TimetableId == id))
				.WithMessage(x => $"Timetable [TimetableId: {x.TimetableId}] not found");
		}
	}
}<file_sep>/Fitverse.CalendarService/Commands/AddTimetableCommand.cs
using Fitverse.CalendarService.Dtos;
using MediatR;

namespace Fitverse.CalendarService.Commands
{
	public class AddTimetableCommand : IRequest<TimetableDto>
	{
		public AddTimetableCommand(TimetableDto timetableDto)
		{
			NewTimetableDto = timetableDto;
		}

		public TimetableDto NewTimetableDto { get; }
	}
}<file_sep>/Fitverse.CalendarService/Models/Timetable.cs
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace Fitverse.CalendarService.Models
{
	public class Timetable
	{
		[Key]
		[Required]
		[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
		public int TimetableId { get; set; }

		[Required] public int ClassTypeId { get; set; }

		[Required] [Column(TypeName = "Date")] public DateTime StartingDate { get; set; }

		[Required] [Column(TypeName = "Date")] public DateTime EndingDate { get; set; }

		[Required] public DateTime ClassesStartingTime { get; set; }

		[Required] public int PeriodType { get; set; }
	}
}<file_sep>/Fitverse.CalendarService/Interfaces/ISignOutOfClassSender.cs
namespace Fitverse.CalendarService.Interfaces
{
	public interface ISignOutOfClassSender
	{
		public void DeleteReservation(int reservationId);
	}
}<file_sep>/Fitverse.CalendarService/Handlers/GetAllClassTypesHandler.cs
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Fitverse.CalendarService.Data;
using Fitverse.CalendarService.Dtos;
using Fitverse.CalendarService.Queries;
using Mapster;
using MediatR;
using Microsoft.EntityFrameworkCore;

namespace Fitverse.CalendarService.Handlers
{
	public class GetAllClassTypesHandler : IRequestHandler<GetAllClassTypesQuery, List<ClassTypeDto>>
	{
		private readonly CalendarContext _dbContext;

		public GetAllClassTypesHandler(CalendarContext dbContext)
		{
			_dbContext = dbContext;
		}

		public async Task<List<ClassTypeDto>> Handle(GetAllClassTypesQuery request, CancellationToken cancellationToken)
		{
			var classTypesList = await _dbContext
				.ClassTypes
				.Where(x => !x.IsDeleted)
				.ToListAsync(cancellationToken);

			return classTypesList.Select(classType => classType.Adapt<ClassTypeDto>()).ToList();
		}
	}
}<file_sep>/Fitverse.CalendarService/Commands/DeleteClassByIdCommand.cs
using Fitverse.CalendarService.Dtos;
using MediatR;

namespace Fitverse.CalendarService.Commands
{
	public class DeleteClassByIdCommand : IRequest<CalendarClassDto>
	{
		public DeleteClassByIdCommand(int classId)
		{
			ClassId = classId;
		}

		public int ClassId { get; }
	}
}<file_sep>/Fitverse.CalendarService/Handlers/GetAllTimetablesHandler.cs
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Fitverse.CalendarService.Data;
using Fitverse.CalendarService.Dtos;
using Fitverse.CalendarService.Queries;
using Mapster;
using MediatR;
using Microsoft.EntityFrameworkCore;

namespace Fitverse.CalendarService.Handlers
{
	public class GetAllTimetablesHandler : IRequestHandler<GetAllTimetablesQuery, List<TimetableDto>>
	{
		private readonly CalendarContext _dbContext;

		public GetAllTimetablesHandler(CalendarContext dbContext)
		{
			_dbContext = dbContext;
		}

		public async Task<List<TimetableDto>> Handle(GetAllTimetablesQuery request, CancellationToken cancellationToken)
		{
			var timetablesList = await _dbContext
				.Timetables
				.ToListAsync(cancellationToken);

			var classTypesList = await _dbContext.ClassTypes
				.ToListAsync(cancellationToken);

			var timetablesDtoList = new List<TimetableDto>();

			foreach (var timetable in timetablesList)
			{
				var timetableDto = timetable.Adapt<TimetableDto>();
				timetableDto.ClassTypeName =
					classTypesList.FirstOrDefault(x => x.ClassTypeId == timetable.ClassTypeId)?.Name;
				
				timetablesDtoList.Add(timetableDto);
			}

			return timetablesDtoList;
		}
	}
}<file_sep>/Fitverse.CalendarService/Controllers/TimetablesController.cs
using System.Threading.Tasks;
using Fitverse.CalendarService.Commands;
using Fitverse.CalendarService.Dtos;
using Fitverse.CalendarService.Queries;
using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace Fitverse.CalendarService.Controllers
{
	[Authorize]
	[ApiController]
	[Route("/api/cs/timetable")]
	public class TimetablesController : Controller
	{
		private readonly IMediator _mediator;

		public TimetablesController(IMediator mediator)
		{
			_mediator = mediator;
		}
		
		[HttpGet]
		public async Task<IActionResult> GetAllTimetables()
		{
			var query = new GetAllTimetablesQuery();
			var result = await _mediator.Send(query);
			return Ok(result);
		}

		[HttpGet]
		[Route("{timetableId}")]
		public async Task<IActionResult> GetTimetableById(int timetableId)
		{
			var query = new GetTimetableByIdQuery(timetableId);
			var result = await _mediator.Send(query);
			return Ok(result);
		}

		[HttpPost]
		public async Task<IActionResult> AddTimetable([FromBody] TimetableDto timetableDto)
		{
			var command = new AddTimetableCommand(timetableDto);
			var result = await _mediator.Send(command);
			return Ok(result);
		}

		[HttpDelete]
		[Route("{timetableId}")]
		public async Task<IActionResult> DeleteTimetable([FromRoute]int timetableId)
		{
			var command = new DeleteTimetableByIdCommand(timetableId);
			var result = await _mediator.Send(command);
			return Ok($"Timetable [timetableId: {result.TimetableId}] has been deleted");
		}
	}
}<file_sep>/Fitverse.CalendarService/Controllers/ClassesController.cs
using System;
using System.Threading.Tasks;
using Fitverse.CalendarService.Commands;
using Fitverse.CalendarService.Dtos;
using Fitverse.CalendarService.Helpers;
using Fitverse.CalendarService.Queries;
using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore.Query.Internal;

namespace Fitverse.CalendarService.Controllers
{
	[Authorize]
	[ApiController]
	[Route("/api/cs/classes")]
	public class ClassesController : Controller
	{
		private readonly IMediator _mediator;

		public ClassesController(IMediator mediator)
		{
			_mediator = mediator;
		}

		[HttpGet]
		public async Task<IActionResult> GetClassByDate([FromQuery] DateRange dateRange)
		{
			var query = new GetClassByDateQuery(dateRange);
			var result = await _mediator.Send(query);
			return Ok(result);
		}

		[HttpGet]
		[Route("{classId}")]
		public async Task<IActionResult> GetClassById([FromRoute] int classId)
		{
			var query = new GetClassByIdQuery(classId);
			var result = await _mediator.Send(query);
			return Ok(result);
		}
		
		[HttpDelete]
		[Route("{classId}")]
		public async Task<IActionResult> DeleteClassById([FromRoute] int classId)
		{
			var command = new DeleteClassByIdCommand(classId);
			var result = await _mediator.Send(command);
			return Ok($"Class [ClassId: {result.ClassId}] has been deleted");
		}
		
		[HttpGet]
		[Route("{classId}/reservation")]
		public async Task<IActionResult> GetReservationsByClassId([FromRoute] int classId)
		{
			var command = new GetReservationsByClassIdCommand(classId);
			var result = await _mediator.Send(command);
			return Ok(result);
		}

		[HttpPost]
		[Route("reservation")]
		public async Task<IActionResult> SignUpForClass([FromBody] ReservationDtoSetter reservation)
		{
			var command = new SignUpForClassCommand(reservation);
			var result = await _mediator.Send(command);
			return Ok(result);
		}

		[HttpDelete]
		[Route("reservation/{reservationId}")]
		public async Task<IActionResult> SignOutOfClass([FromRoute] int reservationId)
		{
			var command = new SignOutOfClassCommand(reservationId);
			var result = await _mediator.Send(command);
			return Ok(result);
		}
	}
}<file_sep>/Fitverse.CalendarService/Helpers/DayOfTheWeek.cs
namespace Fitverse.CalendarService.Helpers
{
	public enum DayOfTheWeek
	{
		Monday = 0,
		Tuesday = 1,
		Wednesday = 2,
		Thursday = 3,
		Friday = 4,
		Saturday = 5,
		Sunday = 6
	}
}<file_sep>/Fitverse.CalendarService/Queries/GetAllClassTypesQuery.cs
using System.Collections.Generic;
using Fitverse.CalendarService.Dtos;
using MediatR;

namespace Fitverse.CalendarService.Queries
{
	public class GetAllClassTypesQuery : IRequest<List<ClassTypeDto>>
	{
	}
}<file_sep>/Fitverse.CalendarService/Handlers/DeleteClassTypeHandler.cs
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Fitverse.CalendarService.Commands;
using Fitverse.CalendarService.Data;
using Fitverse.CalendarService.Dtos;
using Fitverse.CalendarService.Helpers;
using Mapster;
using MediatR;
using Microsoft.EntityFrameworkCore;

namespace Fitverse.CalendarService.Handlers
{
	public class DeleteClassTypeHandler : IRequestHandler<DeleteClassTypeCommand, ClassTypeDto>
	{
		private readonly CalendarContext _dbContext;

		public DeleteClassTypeHandler(CalendarContext dbContext)
		{
			_dbContext = dbContext;
		}

		public async Task<ClassTypeDto> Handle(DeleteClassTypeCommand request, CancellationToken cancellationToken)
		{
			var classTypeEntity = await _dbContext
				.ClassTypes
				.SingleOrDefaultAsync(m => m.ClassTypeId == request.ClassTypeId, cancellationToken);

			classTypeEntity.IsDeleted = true;
			_ = await _dbContext.SaveChangesAsync(cancellationToken);

			var timetablesList = await _dbContext
				.Timetables
				.Where(x => x.ClassTypeId == request.ClassTypeId && x.EndingDate > DateTime.Today)
				.ToListAsync(cancellationToken);

			var classGenerator = new ClassGenerator(_dbContext);
			foreach (var timetable in timetablesList)
			{
				await classGenerator.DeleteAllFutureClassesByTimetableIdAsync(timetable, cancellationToken);
				timetable.EndingDate = DateTime.Today;
			}

			await _dbContext.SaveChangesAsync(cancellationToken);
			var classTypeDto = classTypeEntity.Adapt<ClassTypeDto>();

			return classTypeDto;
		}
	}
}<file_sep>/Fitverse.CalendarService/Validators/SignOutOfClassCommandValidator.cs
using System.Linq;
using Fitverse.CalendarService.Commands;
using Fitverse.CalendarService.Data;
using FluentValidation;

namespace Fitverse.CalendarService.Validators
{
	public class SignOutOfClassCommandValidator : AbstractValidator<SignOutOfClassCommand>
	{
		public SignOutOfClassCommandValidator(CalendarContext dbContext)
		{
			RuleFor(x => x.ReservationId)
				.GreaterThan(0);
				
			RuleFor(x => x.ReservationId)
				.Must(id => dbContext.Reservations.Any(m => m.ReservationId == id))
				.WithMessage(x => $"Reservation [ReservationId: {x.ReservationId}] not found.");
		}
	}
}<file_sep>/Fitverse.CalendarService/Handlers/GetAllMembersHandler.cs
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Fitverse.CalendarService.Data;
using Fitverse.CalendarService.Dtos;
using Fitverse.CalendarService.Queries;
using Mapster;
using MediatR;
using Microsoft.EntityFrameworkCore;

namespace Fitverse.CalendarService.Handlers
{
	public class GetAllMembersHandler : IRequestHandler<GetAllMembersQuery, List<MemberDto>>
	{
		private readonly CalendarContext _dbContext;

		public GetAllMembersHandler(CalendarContext dbContext)
		{
			_dbContext = dbContext;
		}
		
		public async Task<List<MemberDto>> Handle(GetAllMembersQuery request, CancellationToken cancellationToken)
		{
			var membersList = await _dbContext.Members.ToListAsync(cancellationToken);

			return membersList.Select(member => member.Adapt<MemberDto>())
				.OrderBy(x => x.SurName).ToList();
		}
	}
}<file_sep>/Fitverse.CalendarService/Commands/AddClassTypeCommand.cs
using Fitverse.CalendarService.Dtos;
using MediatR;

namespace Fitverse.CalendarService.Commands
{
	public class AddClassTypeCommand : IRequest<ClassTypeDto>
	{
		public AddClassTypeCommand(ClassTypeDto classType)
		{
			NewClassType = classType;
		}

		public ClassTypeDto NewClassType { get; }
	}
}<file_sep>/Fitverse.CalendarService/Commands/DeleteClassTypeCommand.cs
using Fitverse.CalendarService.Dtos;
using MediatR;

namespace Fitverse.CalendarService.Commands
{
	public class DeleteClassTypeCommand : IRequest<ClassTypeDto>
	{
		public DeleteClassTypeCommand(int classTypeId)
		{
			ClassTypeId = classTypeId;
		}

		public int ClassTypeId { get; }
	}
}<file_sep>/Fitverse.CalendarService/Validators/AddClassTypeCommandValidator.cs
using System.Data;
using System.Linq;
using Fitverse.CalendarService.Commands;
using Fitverse.CalendarService.Data;
using FluentValidation;

namespace Fitverse.CalendarService.Validators
{
	public class AddClassTypeCommandValidator : AbstractValidator<AddClassTypeCommand>
	{
		public AddClassTypeCommandValidator(CalendarContext dbContext)
		{
			RuleFor(x => x.NewClassType.Name)
				.NotEmpty()
				.MinimumLength(3)
				.MaximumLength(30);
				
			RuleFor(x => x.NewClassType.Name)
				.Must(n => !dbContext.ClassTypes.Any(c => c.Name == n))
				.WithMessage(x => $"Class type name [ClassTypeName: {x.NewClassType.Name}] already in use.");

			RuleFor(x => x.NewClassType.Description)
				.NotEmpty()
				.MinimumLength(3)
				.MaximumLength(255);

			RuleFor(x => x.NewClassType.Limit)
				.NotEmpty()
				.GreaterThan(0);
			
			RuleFor(x => x.NewClassType.Room)
				.MinimumLength(3)
				.MaximumLength(30);

			RuleFor(x => x.NewClassType.Duration)
				.NotEmpty()
				.GreaterThan(0);
		}
	}
}<file_sep>/Fitverse.CalendarService/Helpers/ClassGenerator.cs
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Fitverse.CalendarService.Data;
using Fitverse.CalendarService.Dtos;
using Fitverse.CalendarService.Models;
using Fitverse.Shared.Helpers;
using Mapster;
using Microsoft.EntityFrameworkCore;

namespace Fitverse.CalendarService.Helpers
{
	public class ClassGenerator
	{
		private readonly CalendarContext _dbContext;

		public ClassGenerator(CalendarContext dbContext)
		{
			_dbContext = dbContext;
		}

		public async Task AddClassesForTimetableAsync(Timetable timetable, CancellationToken cancellationToken = default)
		{
			var timetableStartingDate = timetable.StartingDate;

			var classTypeEntity = await _dbContext
				.ClassTypes
				.SingleOrDefaultAsync(m => m.ClassTypeId == timetable.ClassTypeId, cancellationToken);

			var classesStartingTime = timetable.ClassesStartingTime;
			var classDate = timetableStartingDate;

			var classesDto = new CalendarClassDto {Date = classDate};
			while (classDate >= timetableStartingDate && classDate <= timetable.EndingDate)
			{
				if (classDate == timetableStartingDate)
				{
					classesDto.ClassName = classTypeEntity.Name;
					classesDto.ClassTypeId = timetable.ClassTypeId;
					classesDto.StartingTime = classesStartingTime;
					classesDto.EndingTime = classesStartingTime.AddMinutes(classTypeEntity.Duration);
					classesDto.TimetableId = timetable.TimetableId;
				}
				else
					classesDto.Date = classDate;

				var classEntity = classesDto.Adapt<CalendarClass>();
				_ = await _dbContext.AddAsync(classEntity, cancellationToken);

				classDate = CalculateNextClassDate(classDate, timetable);
			}

			_ = await _dbContext.SaveChangesAsync(cancellationToken);
		}

		public async Task DeleteAllClassesByTimetableIdAsync(Timetable timetable, CancellationToken cancellationToken = default)
		{
			var classesList = await _dbContext
				.Classes
				.Where(m => m.TimetableId == timetable.TimetableId)
				.ToListAsync(cancellationToken);

			foreach (var calendarClass in classesList)
			{
				await DeleteReservationsAsync(calendarClass, cancellationToken);
				_dbContext.Remove(calendarClass);
			}

			_ = await _dbContext.SaveChangesAsync(cancellationToken);
		}
		
		public async Task DeleteAllFutureClassesByTimetableIdAsync(Timetable timetable, CancellationToken cancellationToken = default)
		{
			var classesList = await _dbContext
				.Classes
				.Where(m => m.TimetableId == timetable.TimetableId && m.Date > DateTime.Now)
				.ToListAsync(cancellationToken);

			foreach (var calendarClass in classesList)
			{
				await DeleteReservationsAsync(calendarClass, cancellationToken);
				_dbContext.Remove(calendarClass);
			}

			_ = await _dbContext.SaveChangesAsync(cancellationToken);
		}

		private async Task DeleteReservationsAsync(CalendarClass calendarClass,
			CancellationToken cancellationToken = default)
		{
			var reservationsForClass = await _dbContext
				.Reservations
				.Where(m => m.ClassId == calendarClass.ClassId)
				.ToListAsync(cancellationToken);

			foreach (var reservation in reservationsForClass)
				_dbContext.Remove(reservation);

			_ = await _dbContext.SaveChangesAsync(cancellationToken);
		}

		private DateTime CalculateNextClassDate(DateTime classDate, Timetable timetable)
		{
			switch ((PeriodType) timetable.PeriodType)
			{
				case PeriodType.Day:
					return classDate.AddDays(1);
				case PeriodType.Month:
					return classDate.AddMonths(1);
				case PeriodType.Year:
					return classDate.AddYears(1);
				case PeriodType.Week:
					return classDate.AddDays(7);
				default:
					throw new ArgumentException(
						$"Timetable period [period: {timetable.PeriodType}] do not exists.");
			}
		}
	}
}<file_sep>/Fitverse.CalendarService/Migrations/20210315153423_ClassesAdded.cs
﻿using System;
using Microsoft.EntityFrameworkCore.Migrations;

namespace Fitverse.CalendarService.Migrations
{
    public partial class ClassesAdded : Migration
    {
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.CreateTable(
                name: "Classes",
                columns: table => new
                {
                    ClassId = table.Column<int>(type: "int", nullable: false)
                        .Annotation("SqlServer:Identity", "1, 1"),
                    ClassTypeId = table.Column<int>(type: "int", nullable: true),
                    Date = table.Column<DateTime>(type: "datetime2", nullable: false),
                    StartingTime = table.Column<DateTime>(type: "datetime2", nullable: false),
                    EndingTime = table.Column<DateTime>(type: "datetime2", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Classes", x => x.ClassId);
                    table.ForeignKey(
                        name: "FK_Classes_ClassTypes_ClassTypeId",
                        column: x => x.ClassTypeId,
                        principalTable: "ClassTypes",
                        principalColumn: "ClassTypeId",
                        onDelete: ReferentialAction.Restrict);
                });

            migrationBuilder.CreateIndex(
                name: "IX_Classes_ClassTypeId",
                table: "Classes",
                column: "ClassTypeId");
        }

        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.DropTable(
                name: "Classes");
        }
    }
}
<file_sep>/Fitverse.CalendarService/Handlers/SignOutOfClassHandler.cs
using System;
using System.Threading;
using System.Threading.Tasks;
using Fitverse.CalendarService.Commands;
using Fitverse.CalendarService.Data;
using Fitverse.CalendarService.Dtos;
using Fitverse.CalendarService.Interfaces;
using Mapster;
using MediatR;
using Microsoft.EntityFrameworkCore;

namespace Fitverse.CalendarService.Handlers
{
	public class SignOutOfClassHandler : IRequestHandler<SignOutOfClassCommand, ReservationDtoSetter>
	{
		private readonly CalendarContext _dbContext;
		private readonly ISignOutOfClassSender _signOutOfClassSender;

		public SignOutOfClassHandler(CalendarContext dbContext, ISignOutOfClassSender signOutOfClassSender)
		{
			_dbContext = dbContext;
			_signOutOfClassSender = signOutOfClassSender;
		}

		public async Task<ReservationDtoSetter> Handle(SignOutOfClassCommand request, CancellationToken cancellationToken)
		{
			var reservationEntity = await _dbContext
				.Reservations
				.SingleOrDefaultAsync(m => m.ReservationId == request.ReservationId, cancellationToken);

			_ = _dbContext.Remove(reservationEntity);
			_ = await _dbContext.SaveChangesAsync(cancellationToken);
			
			_signOutOfClassSender.DeleteReservation(reservationEntity.ReservationId);

			var reservationDto = reservationEntity.Adapt<ReservationDtoSetter>();

			return reservationDto;
		}
	}
}<file_sep>/Fitverse.CalendarService/Models/CalendarClass.cs
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace Fitverse.CalendarService.Models
{
	public class CalendarClass
	{
		[Key]
		[Required]
		[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
		public int ClassId { get; set; }

		[Required] public int ClassTypeId { get; set; }
		
		[Required] public string ClassName { get; set; }

		[Required] public DateTime Date { get; set; }

		[Required] public DateTime StartingTime { get; set; }

		[Required] public DateTime EndingTime { get; set; }

		[Required] public int TimetableId { get; set; }
	}
}<file_sep>/Fitverse.CalendarService/Commands/EditClassTypeCommand.cs
using Fitverse.CalendarService.Dtos;
using Fitverse.CalendarService.Models;
using MediatR;
using Microsoft.AspNetCore.JsonPatch;

namespace Fitverse.CalendarService.Commands
{
	public class EditClassTypeCommand : IRequest<ClassTypeDto>
	{
		public EditClassTypeCommand(int classTypeId, JsonPatchDocument<ClassType> classTypeEntity)
		{
			ClassTypeId = classTypeId;
			NewClassTypeEntity = classTypeEntity;
		}

		public int ClassTypeId { get; }

		public JsonPatchDocument<ClassType> NewClassTypeEntity { get; }
	}
}<file_sep>/Fitverse.CalendarService/Handlers/GetClassTypeByIdHandler.cs
using System;
using System.Threading;
using System.Threading.Tasks;
using Fitverse.CalendarService.Data;
using Fitverse.CalendarService.Dtos;
using Fitverse.CalendarService.Queries;
using Mapster;
using MediatR;
using Microsoft.EntityFrameworkCore;

namespace Fitverse.CalendarService.Handlers
{
	public class GetClassTypeByIdHandler : IRequestHandler<GetClassTypeByIdQuery, ClassTypeDto>
	{
		private readonly CalendarContext _dbContext;

		public GetClassTypeByIdHandler(CalendarContext dbContext)
		{
			_dbContext = dbContext;
		}

		public async Task<ClassTypeDto> Handle(GetClassTypeByIdQuery request, CancellationToken cancellationToken)
		{
			var classTypeEntity = await _dbContext
				.ClassTypes
				.SingleOrDefaultAsync(m => m.ClassTypeId == request.ClassTypeId && !m.IsDeleted, cancellationToken);

			if (classTypeEntity is null)
				throw new NullReferenceException($"ClassType [ClassTypeId: {request.ClassTypeId} not found]");

			var classTypeDto = classTypeEntity.Adapt<ClassTypeDto>();

			return classTypeDto;
		}
	}
}<file_sep>/Fitverse.CalendarService/Models/ClassType.cs
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace Fitverse.CalendarService.Models
{
	public class ClassType
	{
		[Key]
		[Required]
		[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
		public int ClassTypeId { get; set; }
		
		[Required]
		[MinLength(3)]
		[MaxLength(30)]
		public string Name { get; set; }
		
		[Required]
		[MaxLength(255)]
		public string Description { get; set; }

		[Required]
		public int Limit { get; set; }
		
		[MinLength(3)]
		[MaxLength(30)]
		public string Room { get; set; }

		[Required]
		public int Duration { get; set; }

		[Required]
		public bool IsDeleted { get; set; }
	}
}<file_sep>/Fitverse.CalendarService/Validators/DeleteClassByIdCommandValidator.cs
using System.Linq;
using Fitverse.CalendarService.Commands;
using Fitverse.CalendarService.Data;
using FluentValidation;

namespace Fitverse.CalendarService.Validators
{
	public class DeleteClassByIdCommandValidator : AbstractValidator<DeleteClassByIdCommand>
	{
		public DeleteClassByIdCommandValidator(CalendarContext dbContext)
		{
			RuleFor(x => x.ClassId)
				.GreaterThan(0);
				
			RuleFor(x => x.ClassId)
				.Must(id => dbContext.Classes.Any(m => m.ClassId == id))
				.WithMessage(x => $"Class [ClassId: {x.ClassId}] not found.");
		}
	}
}<file_sep>/Fitverse.CalendarService/Handlers/GetClassByIdHandler.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Fitverse.CalendarService.Data;
using Fitverse.CalendarService.Dtos;
using Fitverse.CalendarService.Queries;
using Mapster;
using MediatR;
using Microsoft.EntityFrameworkCore;

namespace Fitverse.CalendarService.Handlers
{
	public class GetClassByIdHandler : IRequestHandler<GetClassByIdQuery, CalendarClassDto>
	{
		private readonly CalendarContext _dbContext;

		public GetClassByIdHandler(CalendarContext dbContext)
		{
			_dbContext = dbContext;
		}

		public async Task<CalendarClassDto> Handle(GetClassByIdQuery request, CancellationToken cancellationToken)
		{
			var classEntity = await _dbContext
				.Classes
				.SingleOrDefaultAsync(m => m.ClassId == request.ClassId, cancellationToken);

			if (classEntity is null)
				throw new NullReferenceException($"Class [ClassId: {request.ClassId} not found]");

			var classDto = classEntity.Adapt<CalendarClassDto>();

			var classTypeEntity = await _dbContext
				.ClassTypes
				.SingleOrDefaultAsync(m => m.ClassTypeId == classEntity.ClassTypeId, cancellationToken);

			if (classTypeEntity is null)
				throw new NullReferenceException($"ClassType [ClassTypeId: {classEntity.ClassTypeId} not found]");

			classDto.ClassName = classTypeEntity.Name;
			classDto.Description = classTypeEntity.Description;
			classDto.Limit = classTypeEntity.Limit;

			var reservations = await _dbContext
				.Reservations
				.Where(m => m.ClassId == classDto.ClassId)
				.ToListAsync(cancellationToken);

			classDto.Reservations = new List<int>();
			
			foreach (var reservation in reservations)
				classDto.Reservations.Add(reservation.ReservationId);

			return classDto;
		}
	}
}<file_sep>/Fitverse.CalendarService/Commands/SignUpForClassCommand.cs
using Fitverse.CalendarService.Dtos;
using MediatR;

namespace Fitverse.CalendarService.Commands
{
	public class SignUpForClassCommand : IRequest<ReservationDtoSetter>
	{
		public SignUpForClassCommand(ReservationDtoSetter reservation)
		{
			Reservation = reservation;
		}

		public ReservationDtoSetter Reservation { get; }
	}
}<file_sep>/Fitverse.CalendarService/Handlers/GetReservationsByClassIdHandler.cs
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Fitverse.CalendarService.Data;
using Fitverse.CalendarService.Dtos;
using Fitverse.CalendarService.Queries;
using Mapster;
using MediatR;
using Microsoft.EntityFrameworkCore;

namespace Fitverse.CalendarService.Handlers
{
	public class GetReservationsByClassIdHandler : IRequestHandler<GetReservationsByClassIdCommand, List<ReservationDtoGetter>>
	{
		private readonly CalendarContext _dbContext;

		public GetReservationsByClassIdHandler(CalendarContext dbContext)
		{
			_dbContext = dbContext;
		}
		
		public async Task<List<ReservationDtoGetter>> Handle(GetReservationsByClassIdCommand request, CancellationToken cancellationToken)
		{
			var reservationsList = await _dbContext
				.Reservations
				.Where(x => x.ClassId == request.ClassId)
				.ToListAsync(cancellationToken);
			var reservationsDtoList = new List<ReservationDtoGetter>();

			foreach (var reservation in reservationsList)
			{
				var reservationDto = reservation.Adapt<ReservationDtoGetter>();
				var member = await _dbContext
					.Members
					.FirstAsync(x => x.MemberId == reservation.MemberId, cancellationToken);
				reservationDto.Member = member.Adapt<MemberDto>();
				reservationsDtoList.Add(reservationDto);
			}

			reservationsDtoList = reservationsDtoList.OrderBy(x => x.Member.SurName).ToList();
			return reservationsDtoList;
		}
	}
}<file_sep>/Fitverse.CalendarService/Validators/DeleteClassTypeCommandValidator.cs
using System.Linq;
using Fitverse.CalendarService.Commands;
using Fitverse.CalendarService.Data;
using FluentValidation;

namespace Fitverse.CalendarService.Validators
{
	public class DeleteClassTypeCommandValidator : AbstractValidator<DeleteClassTypeCommand>
	{
		public DeleteClassTypeCommandValidator(CalendarContext dbContext)
		{
			RuleFor(x => x.ClassTypeId)
				.GreaterThan(0);
				
			RuleFor(x => x.ClassTypeId)
				.Must(id => dbContext.ClassTypes.Any(m => m.ClassTypeId == id))
				.WithMessage(x => $"Class type [ClassTypeId: {x.ClassTypeId}] not found");
		}
	}
}<file_sep>/Fitverse.CalendarService/MessageBus/Senders/SignUpForClassSender.cs
using System;
using Fitverse.CalendarService.Interfaces;
using Fitverse.CalendarService.Models;
using Fitverse.Shared.MessageBus;
using Microsoft.Extensions.Options;

namespace Fitverse.CalendarService.MessageBus.Senders
{
	public class SignUpForClassSender : ISignUpForClassSender
	{
		private readonly IOptions<RabbitMqConfiguration> _rabbitMqOptions;

		public SignUpForClassSender(IOptions<RabbitMqConfiguration> rabbitMqOptions)
		{
			_rabbitMqOptions = rabbitMqOptions;
		}
		
		public void AddReservation(Reservation reservation)
		{
			var exchangeConfig = new Tuple<string, string>("classes", "signUpForClass");
			SendEventHandler.SendEvent(reservation, _rabbitMqOptions, exchangeConfig);
		}
	}
}<file_sep>/Fitverse.CalendarService/Dtos/ClassTypeDto.cs
namespace Fitverse.CalendarService.Dtos
{
	public class ClassTypeDto
	{
		public int ClassTypeId { get; private set; }
		public string Name { get; set; }
		public string Description { get; set; }
		public int Limit { get; set; }
		public string Room { get; set; }
		public int Duration { get; set; }
	}
}<file_sep>/Fitverse.CalendarService/Handlers/AddClassTypeHandler.cs
using System;
using System.Threading;
using System.Threading.Tasks;
using Fitverse.CalendarService.Commands;
using Fitverse.CalendarService.Data;
using Fitverse.CalendarService.Dtos;
using Fitverse.CalendarService.Models;
using Mapster;
using MediatR;
using Microsoft.EntityFrameworkCore;

namespace Fitverse.CalendarService.Handlers
{
	public class AddClassTypeHandler : IRequestHandler<AddClassTypeCommand, ClassTypeDto>
	{
		private readonly CalendarContext _dbContext;

		public AddClassTypeHandler(CalendarContext dbContext)
		{
			_dbContext = dbContext;
		}

		public async Task<ClassTypeDto> Handle(AddClassTypeCommand request, CancellationToken cancellationToken)
		{
			var name = request.NewClassType.Name;
			var classTypeEntity = request.NewClassType.Adapt<ClassType>();

			_ = await _dbContext.AddAsync(classTypeEntity, cancellationToken);
			_ = await _dbContext.SaveChangesAsync(cancellationToken);

			var newClassTypeEntity = await _dbContext
				.ClassTypes
				.SingleOrDefaultAsync(m => m.Name == name, cancellationToken);
			if (newClassTypeEntity is null)
				throw new NullReferenceException("Failed to add class type. Try again");

			var newClassTypeDto = newClassTypeEntity.Adapt<ClassTypeDto>();

			return newClassTypeDto;
		}
	}
}